summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java4
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java7
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java22
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java21
-rw-r--r--core/api/test-current.txt6
-rw-r--r--core/java/android/app/Activity.java14
-rw-r--r--core/java/android/app/ActivityClient.java22
-rw-r--r--core/java/android/app/ActivityManager.java1
-rw-r--r--core/java/android/app/ActivityOptions.java1
-rw-r--r--core/java/android/app/ActivityThread.java56
-rw-r--r--core/java/android/app/ActivityThreadInternal.java2
-rw-r--r--core/java/android/app/ActivityTransitionState.java10
-rw-r--r--core/java/android/app/BroadcastOptions.java33
-rw-r--r--core/java/android/app/ConfigurationController.java6
-rw-r--r--core/java/android/app/IActivityClientController.aidl7
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl5
-rw-r--r--core/java/android/app/Instrumentation.java39
-rw-r--r--core/java/android/app/NotificationChannelGroup.java21
-rw-r--r--core/java/android/app/ResourcesManager.java4
-rw-r--r--core/java/android/app/TaskInfo.java10
-rw-r--r--core/java/android/app/WindowConfiguration.java22
-rw-r--r--core/java/android/app/search/SearchAction.java4
-rw-r--r--core/java/android/app/search/SearchTarget.java2
-rw-r--r--core/java/android/app/usage/UsageStats.java11
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java8
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java4
-rw-r--r--core/java/android/companion/AssociationInfo.java46
-rw-r--r--core/java/android/content/ClipboardManager.java55
-rw-r--r--core/java/android/content/IClipboard.aidl20
-rw-r--r--core/java/android/content/pm/ActivityInfo.java20
-rw-r--r--core/java/android/content/pm/PackageParser.java56
-rw-r--r--core/java/android/content/pm/UserInfo.java19
-rw-r--r--core/java/android/content/res/CompatibilityInfo.java5
-rw-r--r--core/java/android/hardware/display/AmbientDisplayConfiguration.java3
-rw-r--r--core/java/android/hardware/display/DisplayManager.java23
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java13
-rw-r--r--core/java/android/hardware/display/IDisplayManager.aidl2
-rw-r--r--core/java/android/hardware/radio/ProgramList.java55
-rw-r--r--core/java/android/nfc/INfcTag.aidl1
-rw-r--r--core/java/android/nfc/Tag.java23
-rw-r--r--core/java/android/os/IUserManager.aidl1
-rw-r--r--core/java/android/os/UserManager.java52
-rw-r--r--core/java/android/provider/Settings.java68
-rw-r--r--core/java/android/service/dreams/DreamActivity.java15
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java42
-rw-r--r--core/java/android/util/FeatureFlagUtils.java12
-rw-r--r--core/java/android/view/IDisplayChangeWindowCallback.aidl (renamed from core/java/android/view/IDisplayWindowRotationCallback.aidl)10
-rw-r--r--core/java/android/view/IDisplayChangeWindowController.aidl (renamed from core/java/android/view/IDisplayWindowRotationController.aidl)24
-rw-r--r--core/java/android/view/IDisplayWindowInsetsController.aidl6
-rw-r--r--core/java/android/view/IRecentsAnimationController.aidl5
-rw-r--r--core/java/android/view/IWindowManager.aidl36
-rw-r--r--core/java/android/view/IWindowSession.aidl48
-rw-r--r--core/java/android/view/InsetsFrameProvider.java294
-rw-r--r--core/java/android/view/InsetsState.java69
-rw-r--r--core/java/android/view/RemoteAccessibilityController.java23
-rw-r--r--core/java/android/view/RemoteAnimationTarget.java13
-rw-r--r--core/java/android/view/ScrollCaptureConnection.java2
-rw-r--r--core/java/android/view/SurfaceControl.java10
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java6
-rw-r--r--core/java/android/view/ThreadedRenderer.java33
-rw-r--r--core/java/android/view/ViewRootImpl.java196
-rw-r--r--core/java/android/view/ViewTreeObserver.java84
-rw-r--r--core/java/android/view/WindowInsets.java18
-rw-r--r--core/java/android/view/WindowLayout.java49
-rw-r--r--core/java/android/view/WindowManager.java168
-rw-r--r--core/java/android/view/WindowManagerGlobal.java12
-rw-r--r--core/java/android/view/WindowManagerPolicyConstants.java4
-rw-r--r--core/java/android/view/WindowlessWindowLayout.java39
-rw-r--r--core/java/android/view/WindowlessWindowManager.java29
-rw-r--r--core/java/android/widget/TextView.java15
-rw-r--r--core/java/android/window/ClientWindowFrames.java13
-rw-r--r--core/java/android/window/ImeOnBackInvokedDispatcher.java4
-rw-r--r--core/java/android/window/PictureInPictureSurfaceTransaction.java23
-rw-r--r--core/java/android/window/SizeConfigurationBuckets.java17
-rw-r--r--core/java/android/window/TransitionInfo.java16
-rw-r--r--core/java/android/window/TransitionRequestInfo.java4
-rw-r--r--core/java/android/window/WindowContainerTransaction.java50
-rw-r--r--core/java/android/window/WindowOnBackInvokedDispatcher.java26
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java131
-rw-r--r--core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java4
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java17
-rw-r--r--core/java/com/android/internal/app/ResolverListAdapter.java13
-rw-r--r--core/java/com/android/internal/app/ResolverListController.java11
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java303
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java258
-rw-r--r--core/java/com/android/internal/policy/DecorView.java14
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java6
-rw-r--r--core/java/com/android/internal/policy/TransitionAnimation.java33
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl5
-rw-r--r--core/java/com/android/internal/statusbar/LetterboxDetails.aidl19
-rw-r--r--core/java/com/android/internal/statusbar/LetterboxDetails.java243
-rw-r--r--core/java/com/android/internal/statusbar/RegisterStatusBarResult.java11
-rw-r--r--core/java/com/android/internal/widget/ResolverDrawerLayout.java38
-rw-r--r--core/jni/android_graphics_BLASTBufferQueue.cpp21
-rw-r--r--core/jni/android_view_SurfaceControl.cpp7
-rw-r--r--core/proto/android/server/windowmanagerservice.proto4
-rw-r--r--core/proto/android/server/windowmanagertransitiontrace.proto70
-rw-r--r--core/res/AndroidManifest.xml7
-rw-r--r--core/res/res/anim-ldrtl/task_fragment_clear_top_close_enter.xml39
-rw-r--r--core/res/res/anim-ldrtl/task_fragment_clear_top_close_exit.xml38
-rw-r--r--core/res/res/anim-ldrtl/task_fragment_clear_top_open_enter.xml37
-rw-r--r--core/res/res/anim-ldrtl/task_fragment_clear_top_open_exit.xml36
-rw-r--r--core/res/res/anim-ldrtl/task_fragment_open_enter.xml2
-rw-r--r--core/res/res/anim/task_fragment_clear_top_close_enter.xml40
-rw-r--r--core/res/res/anim/task_fragment_clear_top_close_exit.xml41
-rw-r--r--core/res/res/anim/task_fragment_clear_top_open_enter.xml41
-rw-r--r--core/res/res/anim/task_fragment_clear_top_open_exit.xml40
-rw-r--r--core/res/res/anim/task_fragment_open_enter.xml2
-rw-r--r--core/res/res/anim/task_fragment_open_exit.xml4
-rw-r--r--core/res/res/drawable-nodpi/default_wallpaper.pngbin738385 -> 861738 bytes
-rw-r--r--core/res/res/drawable-sw600dp-nodpi/default_wallpaper.pngbin2774036 -> 3063458 bytes
-rw-r--r--core/res/res/drawable-sw720dp-nodpi/default_wallpaper.pngbin4958722 -> 4449640 bytes
-rw-r--r--core/res/res/layout-car/car_alert_dialog.xml2
-rw-r--r--core/res/res/layout-car/car_alert_dialog_button_bar.xml20
-rw-r--r--core/res/res/values-eu/strings.xml4
-rw-r--r--core/res/res/values-gl/strings.xml2
-rw-r--r--core/res/res/values-hi/strings.xml4
-rw-r--r--core/res/res/values-it/strings.xml4
-rw-r--r--core/res/res/values-kn/strings.xml2
-rw-r--r--core/res/res/values-ky/strings.xml2
-rw-r--r--core/res/res/values-mcc334-mnc020-pa/strings.xml2
-rw-r--r--core/res/res/values-or/strings.xml8
-rw-r--r--core/res/res/values-ru/strings.xml8
-rw-r--r--core/res/res/values-te/strings.xml14
-rw-r--r--core/res/res/values-uk/strings.xml4
-rw-r--r--core/res/res/values-vi/strings.xml2
-rw-r--r--core/res/res/values/attrs.xml9
-rw-r--r--core/res/res/values/config.xml51
-rw-r--r--core/res/res/values/dimens.xml2
-rw-r--r--core/res/res/values/styles.xml3
-rw-r--r--core/res/res/values/styles_car.xml93
-rw-r--r--core/res/res/values/symbols.xml16
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java48
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java26
-rw-r--r--core/tests/coretests/src/android/widget/TextViewTest.java17
-rw-r--r--core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java28
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java130
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java12
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java124
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java39
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java15
-rw-r--r--core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java11
-rw-r--r--core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java20
-rw-r--r--core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java15
-rw-r--r--data/etc/services.core.protolog.json110
-rw-r--r--data/fonts/fonts.xml2
-rw-r--r--graphics/java/android/graphics/ImageDecoder.java263
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java19
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java4
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java119
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java22
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java45
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java28
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java27
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java133
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java57
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java42
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java32
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java23
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java23
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java17
-rw-r--r--libs/WindowManager/Shell/res/color/decor_button_dark_color.xml21
-rw-r--r--libs/WindowManager/Shell/res/color/decor_button_light_color.xml21
-rw-r--r--libs/WindowManager/Shell/res/color/decor_caption_title_color.xml23
-rw-r--r--libs/WindowManager/Shell/res/color/taskbar_background.xml3
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_caption_title.xml22
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml32
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml36
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_overflow_container.xml3
-rw-r--r--libs/WindowManager/Shell/res/layout/caption_window_decoration.xml45
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml5
-rw-r--r--libs/WindowManager/Shell/res/values/attrs.xml4
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java80
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java357
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java213
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsPool.java93
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/OWNERS2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java126
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java303
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java54
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java358
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java104
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarInputEventReceiver.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java175
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationController.java75
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java431
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerStub.java57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/OverScroll.java57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java73
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java233
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java208
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java233
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md73
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/dagger.md50
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md69
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/extending.md13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/overview.md58
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md65
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/testing.md49
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md83
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java78
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java39
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java418
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java1314
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerWindowManager.java117
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivity.java110
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivityController.java150
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java326
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreen.java85
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java762
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java376
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java348
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/MinimizedDockShadow.java99
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java386
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java68
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java44
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java230
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java39
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java127
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java97
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java154
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java83
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java478
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java128
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreen.aidl103
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/MainStage.java104
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineManager.java181
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineView.java82
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SideStage.java144
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreen.java99
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java595
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java292
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitscreenEventLogger.java324
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java1333
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java298
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskUnfoldController.java224
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ConfigurationChangeListener.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/KeyguardChangeListener.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java)84
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java240
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java)111
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java62
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.java82
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java289
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java47
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java99
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java225
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java95
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java)191
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java)279
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/UnfoldTaskAnimator.java117
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldShellTransition.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldTransition.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java)20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java174
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java200
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java288
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskFocusStateConsumer.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerState.java)12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java99
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorLinearLayout.java72
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java49
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java341
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt128
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt115
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt104
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt128
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt121
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt178
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt109
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt117
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt76
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt44
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt178
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt116
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt104
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt126
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt110
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt115
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt124
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt129
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt196
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt153
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt169
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt155
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt47
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt160
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt149
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt122
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt228
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt114
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt113
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt136
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt133
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt116
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt111
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt12
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt7
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt148
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt18
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt123
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt139
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt129
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt59
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_notification.xml30
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml33
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java27
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SendNotificationActivity.java61
-rw-r--r--libs/WindowManager/Shell/tests/unittest/Android.bp8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java56
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitTest.java134
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java22
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java33
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java123
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java104
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java77
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java42
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsPool.java36
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java142
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTestActivity.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java181
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java21
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java27
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java65
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java166
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java23
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java42
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java17
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipDummySurfaceControlTx.java66
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java34
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java96
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java24
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java (renamed from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/StagedSplitBoundsTest.java)17
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java167
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java63
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java331
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperControllerTest.java58
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java354
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java178
-rw-r--r--libs/hwui/JankTracker.cpp5
-rw-r--r--libs/hwui/Readback.cpp35
-rw-r--r--libs/hwui/tests/unit/JankTrackerTests.cpp65
-rw-r--r--media/TEST_MAPPING12
-rw-r--r--media/java/android/media/projection/IMediaProjection.aidl14
-rw-r--r--media/java/android/media/projection/MediaProjection.java10
-rw-r--r--packages/CompanionDeviceManager/res/values-af/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-am/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-ar/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-as/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-az/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-be/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-bg/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-bn/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-bs/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-ca/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-cs/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-da/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-de/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-el/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rAU/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rCA/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rGB/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rIN/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rXC/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-es-rUS/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-es/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-et/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-eu/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-fa/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-fi/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-fr/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-gl/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-gu/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-hi/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-hr/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-hu/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-hy/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-in/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-is/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-it/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-iw/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-ja/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-ka/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-kk/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-km/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-kn/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-ko/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-ky/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-lo/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-lt/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-lv/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-mk/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-ml/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-mn/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-mr/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-ms/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-my/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-nb/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-ne/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-nl/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-or/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-pa/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-pl/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-pt/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-ro/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-ru/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-si/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-sk/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-sl/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-sq/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-sr/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-sv/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-sw/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-ta/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-te/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-th/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-tl/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-tr/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-uk/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-ur/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-uz/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-vi/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values-zu/strings.xml6
-rw-r--r--packages/CompanionDeviceManager/res/values/strings.xml4
-rw-r--r--packages/PackageInstaller/res/values-hi/strings.xml2
-rw-r--r--packages/SettingsLib/Android.bp1
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/Android.bp16
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/AndroidManifest.xml21
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java (renamed from packages/SettingsLib/src/com/android/settingslib/devicestate/AndroidSecureSettings.java)0
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java (renamed from packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java)0
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java (renamed from packages/SettingsLib/src/com/android/settingslib/devicestate/SecureSettings.java)0
-rw-r--r--packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml2
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-az/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-en-rXC/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-mn/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-ms/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-si/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml16
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml16
-rw-r--r--packages/SettingsLib/res/values/strings.xml44
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/Utils.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java14
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java29
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java46
-rw-r--r--packages/SettingsLib/tests/integ/Android.bp3
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java11
-rw-r--r--packages/SettingsProvider/Android.bp2
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/DeviceSpecificSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java10
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java2
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java59
-rw-r--r--packages/SystemUI/Android.bp3
-rw-r--r--packages/SystemUI/AndroidManifest.xml13
-rw-r--r--packages/SystemUI/TEST_MAPPING11
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt318
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt2
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt535
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt31
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt148
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt2
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt187
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt4
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt489
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt41
-rw-r--r--packages/SystemUI/docs/camera.md45
-rw-r--r--packages/SystemUI/docs/device-entry/doze.md3
-rw-r--r--packages/SystemUI/docs/user-file-manager.md33
-rw-r--r--packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java5
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java2
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java2
-rw-r--r--packages/SystemUI/proguard.flags6
-rw-r--r--packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_off.xml725
-rw-r--r--packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_on.xml781
-rw-r--r--packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_off.xml144
-rw-r--r--packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_on.xml138
-rw-r--r--packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_search.xml268
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml1
-rw-r--r--packages/SystemUI/res-keyguard/values-af/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-am/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-ar/strings.xml36
-rw-r--r--packages/SystemUI/res-keyguard/values-as/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-az/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml24
-rw-r--r--packages/SystemUI/res-keyguard/values-be/strings.xml28
-rw-r--r--packages/SystemUI/res-keyguard/values-bg/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-bn/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-bs/strings.xml24
-rw-r--r--packages/SystemUI/res-keyguard/values-ca/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-cs/strings.xml28
-rw-r--r--packages/SystemUI/res-keyguard/values-da/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-de/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-el/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rAU/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rCA/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rGB/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rIN/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-en-rXC/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-es-rUS/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-es/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-et/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-eu/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-fa/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-fi/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-fr/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-gl/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-gu/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-hi/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-hr/strings.xml24
-rw-r--r--packages/SystemUI/res-keyguard/values-hu/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-hy/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-in/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-is/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-it/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-iw/strings.xml28
-rw-r--r--packages/SystemUI/res-keyguard/values-ja/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-ka/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-kk/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-km/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-kn/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-ko/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-ky/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-lo/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-lt/strings.xml28
-rw-r--r--packages/SystemUI/res-keyguard/values-lv/strings.xml24
-rw-r--r--packages/SystemUI/res-keyguard/values-mk/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-ml/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-mn/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-mr/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-ms/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-my/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-nb/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-ne/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-nl/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-or/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-pa/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-pl/strings.xml28
-rw-r--r--packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-pt/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-ro/strings.xml24
-rw-r--r--packages/SystemUI/res-keyguard/values-ru/strings.xml28
-rw-r--r--packages/SystemUI/res-keyguard/values-si/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-sk/strings.xml28
-rw-r--r--packages/SystemUI/res-keyguard/values-sl/strings.xml28
-rw-r--r--packages/SystemUI/res-keyguard/values-sq/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-sr/strings.xml24
-rw-r--r--packages/SystemUI/res-keyguard/values-sv/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-sw/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-ta/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-te/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-th/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-tl/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-tr/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-uk/strings.xml28
-rw-r--r--packages/SystemUI/res-keyguard/values-ur/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-uz/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-vi/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values-zu/strings.xml20
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml13
-rw-r--r--packages/SystemUI/res-keyguard/values/strings.xml46
-rw-r--r--packages/SystemUI/res/drawable/broadcast_dialog_btn_bg.xml23
-rw-r--r--packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml20
-rw-r--r--packages/SystemUI/res/drawable/dream_overlay_camera_off.xml29
-rw-r--r--packages/SystemUI/res/drawable/dream_overlay_mic_and_camera_off.xml33
-rw-r--r--packages/SystemUI/res/drawable/dream_overlay_mic_off.xml29
-rw-r--r--packages/SystemUI/res/drawable/ic_media_pause_container.xml11
-rw-r--r--packages/SystemUI/res/drawable/ic_media_play_container.xml6
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_screen_saver.xml24
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_screen_saver_undocked.xml30
-rw-r--r--packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml22
-rw-r--r--packages/SystemUI/res/drawable/qs_airplane_icon_off.xml53
-rw-r--r--packages/SystemUI/res/drawable/qs_airplane_icon_on.xml98
-rw-r--r--packages/SystemUI/res/drawable/qs_battery_saver_icon_off.xml67
-rw-r--r--packages/SystemUI/res/drawable/qs_battery_saver_icon_on.xml636
-rw-r--r--packages/SystemUI/res/drawable/qs_camera_access_icon_off.xml110
-rw-r--r--packages/SystemUI/res/drawable/qs_camera_access_icon_on.xml93
-rw-r--r--packages/SystemUI/res/drawable/qs_dnd_icon_off.xml56
-rw-r--r--packages/SystemUI/res/drawable/qs_dnd_icon_on.xml73
-rw-r--r--packages/SystemUI/res/drawable/qs_location_icon_off.xml166
-rw-r--r--packages/SystemUI/res/drawable/qs_location_icon_on.xml155
-rw-r--r--packages/SystemUI/res/drawable/qs_mic_access_off.xml212
-rw-r--r--packages/SystemUI/res/drawable/qs_mic_access_on.xml212
-rw-r--r--packages/SystemUI/res/drawable/screenshot_edit_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/settings_input_antenna.xml23
-rw-r--r--packages/SystemUI/res/layout/auth_container_view.xml1
-rw-r--r--packages/SystemUI/res/layout/brightness_mirror_container.xml1
-rw-r--r--packages/SystemUI/res/layout/broadcast_dialog.xml79
-rw-r--r--packages/SystemUI/res/layout/clipboard_overlay.xml1
-rw-r--r--packages/SystemUI/res/layout/combined_qs_header.xml60
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_complication_aqi.xml26
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml4
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml4
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml29
-rw-r--r--packages/SystemUI/res/layout/hybrid_conversation_notification.xml6
-rw-r--r--packages/SystemUI/res/layout/hybrid_notification.xml9
-rw-r--r--packages/SystemUI/res/layout/keyguard_bottom_area.xml20
-rw-r--r--packages/SystemUI/res/layout/keyguard_status_bar.xml3
-rw-r--r--packages/SystemUI/res/layout/large_screen_shade_header.xml2
-rw-r--r--packages/SystemUI/res/layout/media_output_dialog.xml36
-rw-r--r--packages/SystemUI/res/layout/media_session_view.xml4
-rw-r--r--packages/SystemUI/res/layout/media_ttt_chip.xml5
-rw-r--r--packages/SystemUI/res/layout/media_ttt_chip_receiver.xml11
-rw-r--r--packages/SystemUI/res/layout/notification_icon_area.xml17
-rw-r--r--packages/SystemUI/res/layout/people_space_activity.xml104
-rw-r--r--packages/SystemUI/res/layout/people_space_activity_no_conversations.xml2
-rw-r--r--packages/SystemUI/res/layout/people_space_activity_with_conversations.xml115
-rw-r--r--packages/SystemUI/res/layout/people_space_tile_view.xml4
-rw-r--r--packages/SystemUI/res/layout/qs_tile_label.xml4
-rw-r--r--packages/SystemUI/res/layout/status_bar.xml160
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml8
-rw-r--r--packages/SystemUI/res/layout/super_notification_shade.xml4
-rw-r--r--packages/SystemUI/res/layout/user_switcher_fullscreen.xml4
-rw-r--r--packages/SystemUI/res/layout/wireless_charging_layout.xml2
-rw-r--r--packages/SystemUI/res/values-af/strings.xml56
-rw-r--r--packages/SystemUI/res/values-af/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-am/strings.xml56
-rw-r--r--packages/SystemUI/res/values-am/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml84
-rw-r--r--packages/SystemUI/res/values-ar/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-as/strings.xml56
-rw-r--r--packages/SystemUI/res/values-as/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-az/strings.xml56
-rw-r--r--packages/SystemUI/res/values-az/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml63
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-be/strings.xml70
-rw-r--r--packages/SystemUI/res/values-be/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml56
-rw-r--r--packages/SystemUI/res/values-bg/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml56
-rw-r--r--packages/SystemUI/res/values-bn/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml63
-rw-r--r--packages/SystemUI/res/values-bs/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml56
-rw-r--r--packages/SystemUI/res/values-ca/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml70
-rw-r--r--packages/SystemUI/res/values-cs/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-da/strings.xml56
-rw-r--r--packages/SystemUI/res/values-da/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-de/strings.xml56
-rw-r--r--packages/SystemUI/res/values-de/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-el/strings.xml56
-rw-r--r--packages/SystemUI/res/values-el/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml55
-rw-r--r--packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml55
-rw-r--r--packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml55
-rw-r--r--packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml55
-rw-r--r--packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml55
-rw-r--r--packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml56
-rw-r--r--packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-es/strings.xml56
-rw-r--r--packages/SystemUI/res/values-es/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-et/strings.xml56
-rw-r--r--packages/SystemUI/res/values-et/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml56
-rw-r--r--packages/SystemUI/res/values-eu/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml56
-rw-r--r--packages/SystemUI/res/values-fa/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml56
-rw-r--r--packages/SystemUI/res/values-fi/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml56
-rw-r--r--packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml56
-rw-r--r--packages/SystemUI/res/values-fr/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml56
-rw-r--r--packages/SystemUI/res/values-gl/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml56
-rw-r--r--packages/SystemUI/res/values-gu/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml58
-rw-r--r--packages/SystemUI/res/values-hi/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml63
-rw-r--r--packages/SystemUI/res/values-hr/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml56
-rw-r--r--packages/SystemUI/res/values-hu/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml56
-rw-r--r--packages/SystemUI/res/values-hy/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-in/strings.xml56
-rw-r--r--packages/SystemUI/res/values-in/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-is/strings.xml56
-rw-r--r--packages/SystemUI/res/values-is/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-it/strings.xml59
-rw-r--r--packages/SystemUI/res/values-it/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml70
-rw-r--r--packages/SystemUI/res/values-iw/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml56
-rw-r--r--packages/SystemUI/res/values-ja/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml56
-rw-r--r--packages/SystemUI/res/values-ka/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml56
-rw-r--r--packages/SystemUI/res/values-kk/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-km/strings.xml56
-rw-r--r--packages/SystemUI/res/values-km/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml56
-rw-r--r--packages/SystemUI/res/values-kn/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml56
-rw-r--r--packages/SystemUI/res/values-ko/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml56
-rw-r--r--packages/SystemUI/res/values-ky/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml56
-rw-r--r--packages/SystemUI/res/values-lo/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml70
-rw-r--r--packages/SystemUI/res/values-lt/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml63
-rw-r--r--packages/SystemUI/res/values-lv/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml56
-rw-r--r--packages/SystemUI/res/values-mk/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml56
-rw-r--r--packages/SystemUI/res/values-ml/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml56
-rw-r--r--packages/SystemUI/res/values-mn/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml56
-rw-r--r--packages/SystemUI/res/values-mr/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml56
-rw-r--r--packages/SystemUI/res/values-ms/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-my/strings.xml56
-rw-r--r--packages/SystemUI/res/values-my/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml56
-rw-r--r--packages/SystemUI/res/values-nb/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml56
-rw-r--r--packages/SystemUI/res/values-ne/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml56
-rw-r--r--packages/SystemUI/res/values-nl/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-or/strings.xml56
-rw-r--r--packages/SystemUI/res/values-or/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml56
-rw-r--r--packages/SystemUI/res/values-pa/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml70
-rw-r--r--packages/SystemUI/res/values-pl/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml56
-rw-r--r--packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml56
-rw-r--r--packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml56
-rw-r--r--packages/SystemUI/res/values-pt/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml63
-rw-r--r--packages/SystemUI/res/values-ro/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml70
-rw-r--r--packages/SystemUI/res/values-ru/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-si/strings.xml56
-rw-r--r--packages/SystemUI/res/values-si/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml70
-rw-r--r--packages/SystemUI/res/values-sk/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml70
-rw-r--r--packages/SystemUI/res/values-sl/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml56
-rw-r--r--packages/SystemUI/res/values-sq/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml63
-rw-r--r--packages/SystemUI/res/values-sr/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml56
-rw-r--r--packages/SystemUI/res/values-sv/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml56
-rw-r--r--packages/SystemUI/res/values-sw/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw600dp-port/dimens.xml5
-rw-r--r--packages/SystemUI/res/values-sw720dp-land/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw720dp-port/dimens.xml7
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml56
-rw-r--r--packages/SystemUI/res/values-ta/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-te/strings.xml56
-rw-r--r--packages/SystemUI/res/values-te/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-television/config.xml2
-rw-r--r--packages/SystemUI/res/values-th/strings.xml56
-rw-r--r--packages/SystemUI/res/values-th/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml56
-rw-r--r--packages/SystemUI/res/values-tl/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml56
-rw-r--r--packages/SystemUI/res/values-tr/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml70
-rw-r--r--packages/SystemUI/res/values-uk/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml56
-rw-r--r--packages/SystemUI/res/values-ur/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml56
-rw-r--r--packages/SystemUI/res/values-uz/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml56
-rw-r--r--packages/SystemUI/res/values-vi/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml56
-rw-r--r--packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml56
-rw-r--r--packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml56
-rw-r--r--packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml56
-rw-r--r--packages/SystemUI/res/values-zu/tiles_states_strings.xml5
-rw-r--r--packages/SystemUI/res/values/attrs.xml10
-rw-r--r--packages/SystemUI/res/values/colors.xml10
-rw-r--r--packages/SystemUI/res/values/config.xml43
-rw-r--r--packages/SystemUI/res/values/dimens.xml84
-rw-r--r--packages/SystemUI/res/values/strings.xml130
-rw-r--r--packages/SystemUI/res/values/styles.xml72
-rw-r--r--packages/SystemUI/res/values/tiles_states_strings.xml10
-rw-r--r--packages/SystemUI/res/xml/combined_qs_header_scene.xml93
-rw-r--r--packages/SystemUI/res/xml/large_screen_shade_header.xml40
-rw-r--r--packages/SystemUI/res/xml/media_session_collapsed.xml17
-rw-r--r--packages/SystemUI/res/xml/qqs_header.xml47
-rw-r--r--packages/SystemUI/res/xml/qs_header_new.xml124
-rw-r--r--packages/SystemUI/screenshot/Android.bp45
-rw-r--r--packages/SystemUI/screenshot/AndroidManifest.xml26
-rw-r--r--packages/SystemUI/screenshot/res/values/themes.xml31
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/Bitmap.kt66
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotActivity.kt (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java)18
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt42
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt180
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt203
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/WindowCapture.kt37
-rw-r--r--packages/SystemUI/shared/Android.bp2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt41
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt44
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockProviderPlugin.kt65
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt146
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java9
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl21
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java11
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java9
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerKt.kt (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java)25
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java46
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java22
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java37
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java80
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java90
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java44
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java68
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt49
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt35
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt51
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt61
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt13
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt19
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java470
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java85
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java135
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java22
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java21
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/Gefingerpoken.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java269
-rw-r--r--packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java129
-rw-r--r--packages/SystemUI/src/com/android/systemui/Somnambulator.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java158
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt183
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java (renamed from packages/SystemUI/src/com/android/systemui/SystemUIFactory.java)168
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt72
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/DwellRippleShader.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java134
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogController.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt158
-rw-r--r--packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt)19
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/qualifiers/InstrumentationTest.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDrop.java)26
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLog.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeUi.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayNotificationCountProvider.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationViewModel.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java185
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java110
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt (renamed from packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamsSmartspaceController.kt)42
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/HideComplicationTouchHandler.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/lifecycle/WindowAddedViewLifecycleOwner.kt114
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/NotificationInterruptLog.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarNetworkControllerLog.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java)25
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java119
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaData.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataUtils.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt147
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt119
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java172
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java97
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreenListener.aidl)30
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt397
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt760
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java156
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt140
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/GestureModule.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleModule.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java146
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/data/model/PeopleTileModel.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/data/repository/PeopleTileRepository.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/data/repository/PeopleWidgetRepository.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt243
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleTileViewModel.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt149
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt114
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java253
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt)10
-rw-r--r--packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt)25
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java131
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserFileManager.kt (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java)30
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt144
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java (renamed from packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java)17
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManager.kt79
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt109
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt510
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NPVCDownEventState.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVCDownEventState.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotifPanelEvents.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEvents.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotifPanelEventsModule.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEventsModule.java)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelView.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java)712
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java)3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java)5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/OWNERS13
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/transition/NoOpOverScroller.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/NoOpOverScroller.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt106
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/transition/ShadeOverScroller.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeOverScroller.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt)44
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt)22
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java129
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java154
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SharedCoordinatorLogger.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java77
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java100
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java128
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt182
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CollectionReadyForBuildListener.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt137
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionClassifier.kt)6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java106
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt163
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java182
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java96
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java104
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java107
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java97
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java184
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java587
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java806
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LargeScreenShadeHeaderController.kt304
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt92
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java136
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java99
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionChangeEvent.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java132
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java150
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java238
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSystemUIInitializer.java (renamed from packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java)16
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/UserSwitcherRootView.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/AsyncActivityLauncher.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt79
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/InitializationChecker.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/PluralMessageFormater.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java274
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java188
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java149
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java17
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt43
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt36
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java188
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java157
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt59
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt311
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt)29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java72
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java68
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java64
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java111
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java79
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/HideComplicationTouchHandlerTest.java45
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/lifecycle/WindowAddedViewLifecycleOwnerTest.kt150
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt90
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt107
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaTestUtils.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java50
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java222
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java120
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt112
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt73
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt112
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java270
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt112
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt110
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java59
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt120
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt328
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangeTest.kt85
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangesTest.kt75
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt659
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LargeScreenShadeHeaderControllerTest.kt)80
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java)112
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java)34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt284
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java)5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt151
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt)99
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/transition/SplitShadeOverScrollerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt)11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt209
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt87
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java153
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java74
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java72
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java99
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt68
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java91
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java118
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java89
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt80
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt197
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java130
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt95
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLoggerTest.kt98
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java345
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java299
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt175
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java72
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt281
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt171
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconListTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java)137
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java86
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt144
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt48
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt166
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/NotificationChannelsTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java)15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java145
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java416
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java1401
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java48
-rw-r--r--packages/SystemUI/unfold/Android.bp39
-rw-r--r--packages/SystemUI/unfold/AndroidManifest.xml (renamed from libs/WindowManager/Shell/res/color/unfold_transition_background.xml)13
-rw-r--r--packages/SystemUI/unfold/lint-baseline.xml3
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt)18
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt)4
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt)15
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt)4
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt56
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt54
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt41
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt)1
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt25
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt25
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt)0
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt)4
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt)91
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldProvider.kt26
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt)7
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt27
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt33
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt)14
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt)2
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt)14
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CallbackController.kt20
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt22
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt)14
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt (renamed from packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt)0
-rw-r--r--proto/src/system_messages.proto8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java26
-rw-r--r--services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java13
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java21
-rw-r--r--services/companion/java/com/android/server/companion/AssociationStoreImpl.java16
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java366
-rw-r--r--services/companion/java/com/android/server/companion/PackageUtils.java24
-rw-r--r--services/companion/java/com/android/server/companion/PersistentDataStore.java24
-rw-r--r--services/companion/java/com/android/server/companion/RolesUtils.java2
-rw-r--r--services/contentcapture/Android.bp5
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java19
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags8
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java9
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java50
-rw-r--r--services/core/java/com/android/server/am/BroadcastRecord.java3
-rw-r--r--services/core/java/com/android/server/am/ContentProviderHelper.java20
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java16
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java18
-rw-r--r--services/core/java/com/android/server/am/PendingStartActivityUids.java6
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java4
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java184
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java62
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java141
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricLogger.java30
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/UsageStats.java43
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java159
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java478
-rw-r--r--services/core/java/com/android/server/connectivity/VpnIkev2Utils.java68
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerService.java23
-rw-r--r--services/core/java/com/android/server/display/BrightnessThrottler.java205
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java62
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java32
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java4
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java6
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java120
-rw-r--r--services/core/java/com/android/server/display/color/ColorDisplayService.java2
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java20
-rw-r--r--services/core/java/com/android/server/dreams/DreamShellCommand.java92
-rw-r--r--services/core/java/com/android/server/input/GestureMonitorSpyWindow.java6
-rw-r--r--services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java5
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java10
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java4
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java12
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java19
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java37
-rw-r--r--services/core/java/com/android/server/notification/PermissionHelper.java8
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java50
-rw-r--r--services/core/java/com/android/server/notification/SnoozeHelper.java391
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java56
-rw-r--r--services/core/java/com/android/server/pm/ScanPackageUtils.java3
-rw-r--r--services/core/java/com/android/server/pm/SettingBase.java11
-rw-r--r--services/core/java/com/android/server/pm/Settings.java25
-rw-r--r--services/core/java/com/android/server/pm/ShortcutLauncher.java6
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java42
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java19
-rw-r--r--services/core/java/com/android/server/policy/WindowManagerPolicy.java24
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java2
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java27
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java4
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java16
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java3
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java50
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java55
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java88
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java375
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecordInputSink.java9
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java50
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartInterceptor.java52
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java109
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java166
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java79
-rw-r--r--services/core/java/com/android/server/wm/AnrController.java2
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java206
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java25
-rw-r--r--services/core/java/com/android/server/wm/AsyncRotationController.java79
-rw-r--r--services/core/java/com/android/server/wm/BLASTSyncEngine.java6
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java12
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java8
-rw-r--r--services/core/java/com/android/server/wm/ContentRecordingController.java1
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java138
-rw-r--r--services/core/java/com/android/server/wm/DisplayFrames.java17
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java265
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java114
-rw-r--r--services/core/java/com/android/server/wm/DragState.java16
-rw-r--r--services/core/java/com/android/server/wm/EventLogTags.logtags2
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java11
-rw-r--r--services/core/java/com/android/server/wm/InputConsumerImpl.java5
-rw-r--r--services/core/java/com/android/server/wm/InputManagerCallback.java2
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java32
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java75
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java103
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java51
-rw-r--r--services/core/java/com/android/server/wm/Letterbox.java17
-rw-r--r--services/core/java/com/android/server/wm/LetterboxConfiguration.java568
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java234
-rw-r--r--services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java55
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java30
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java55
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java61
-rw-r--r--services/core/java/com/android/server/wm/RemoteDisplayChangeController.java171
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java77
-rw-r--r--services/core/java/com/android/server/wm/SafeActivityOptions.java11
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java14
-rw-r--r--services/core/java/com/android/server/wm/Session.java46
-rw-r--r--services/core/java/com/android/server/wm/StartingSurfaceController.java5
-rw-r--r--services/core/java/com/android/server/wm/Task.java364
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java84
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java82
-rw-r--r--services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java371
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java3
-rw-r--r--services/core/java/com/android/server/wm/Transition.java356
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java126
-rw-r--r--services/core/java/com/android/server/wm/TransitionTracer.java243
-rw-r--r--services/core/java/com/android/server/wm/UnknownAppVisibilityController.java4
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java23
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java78
-rw-r--r--services/core/java/com/android/server/wm/WindowContainerThumbnail.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java247
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java740
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java187
-rw-r--r--services/core/java/com/android/server/wm/WindowOrientationListener.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java116
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java171
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java45
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java4
-rw-r--r--services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java13
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java28
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java160
-rw-r--r--services/tests/servicestests/AndroidTest.xml1
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java42
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java58
-rw-r--r--services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java181
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java182
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java89
-rw-r--r--services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java46
-rw-r--r--services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java86
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java2
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java150
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java37
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java85
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java17
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java108
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java35
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java109
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java22
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java19
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java26
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java33
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java120
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java21
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java18
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java111
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java19
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java280
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java96
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java86
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java136
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java98
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java102
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java146
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java76
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java75
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java40
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java51
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java73
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsDatabase.java37
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java3
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java5
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java52
-rw-r--r--telephony/common/com/android/internal/telephony/SmsApplication.java31
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthNr.java16
-rw-r--r--telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java8
-rw-r--r--telephony/java/android/telephony/UiccSlotInfo.java2
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java14
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt46
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt34
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml1
-rw-r--r--tests/WindowInsetsTests/res/layout/controller_activity.xml2
-rw-r--r--tests/WindowInsetsTests/res/values/strings.xml2
-rw-r--r--tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java46
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java10
1866 files changed, 56496 insertions, 38338 deletions
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index 452bb0ab5909..06207215b7be 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -19,6 +19,7 @@ package android.wm;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemClock;
import android.perftests.utils.ManualBenchmarkState;
@@ -86,6 +87,7 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase
final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
final InsetsState mOutInsetsState = new InsetsState();
final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0];
+ final Rect mOutAttachedFrame = new Rect();
TestWindow() {
mLayoutParams.setTitle(TestWindow.class.getName());
@@ -104,7 +106,7 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase
long startTime = SystemClock.elapsedRealtimeNanos();
session.addToDisplay(this, mLayoutParams, View.VISIBLE,
Display.DEFAULT_DISPLAY, mRequestedVisibilities, inputChannel,
- mOutInsetsState, mOutControls);
+ mOutInsetsState, mOutControls, mOutAttachedFrame);
final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
state.addExtraResult("add", elapsedTimeNsOfAdd);
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index c43c832992cf..9b64edf53d8c 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -164,6 +164,13 @@ public interface AppStandbyInternal {
@ElapsedRealtimeLong long elapsedRealtime);
/**
+ * Puts the list of apps in the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RARE}
+ * bucket.
+ * @param restoredApps the list of restored apps
+ */
+ void restoreAppsToRare(@NonNull Set<String> restoredApps, int userId);
+
+ /**
* Put the specified app in the
* {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
* bucket. If it has been used by the user recently, the restriction will delayed until an
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index d6b246a9e2e3..c94cc8ff2612 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -335,12 +335,18 @@ public class AlarmManagerService extends SystemService {
"REORDER_ALARMS_FOR_TARE",
});
- BroadcastOptions mOptsWithFgs = BroadcastOptions.makeBasic();
- BroadcastOptions mOptsWithFgsForAlarmClock = BroadcastOptions.makeBasic();
- BroadcastOptions mOptsWithoutFgs = BroadcastOptions.makeBasic();
- BroadcastOptions mOptsTimeBroadcast = BroadcastOptions.makeBasic();
+ BroadcastOptions mOptsWithFgs = makeBasicAlarmBroadcastOptions();
+ BroadcastOptions mOptsWithFgsForAlarmClock = makeBasicAlarmBroadcastOptions();
+ BroadcastOptions mOptsWithoutFgs = makeBasicAlarmBroadcastOptions();
+ BroadcastOptions mOptsTimeBroadcast = makeBasicAlarmBroadcastOptions();
ActivityOptions mActivityOptsRestrictBal = ActivityOptions.makeBasic();
- BroadcastOptions mBroadcastOptsRestrictBal = BroadcastOptions.makeBasic();
+ BroadcastOptions mBroadcastOptsRestrictBal = makeBasicAlarmBroadcastOptions();
+
+ private static BroadcastOptions makeBasicAlarmBroadcastOptions() {
+ final BroadcastOptions b = BroadcastOptions.makeBasic();
+ b.setAlarmBroadcast(true);
+ return b;
+ }
// TODO(b/172085676): Move inside alarm store.
private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
@@ -2889,7 +2895,11 @@ public class AlarmManagerService extends SystemService {
} else {
needsPermission = false;
lowerQuota = allowWhileIdle;
- idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null;
+ idleOptions = (allowWhileIdle || (alarmClock != null))
+ // This avoids exceptions on existing alarms when the app upgrades to
+ // target S. Note that FGS from pre-S apps isn't restricted anyway.
+ ? mOptsWithFgs.toBundle()
+ : null;
if (exact) {
exactAllowReason = EXACT_ALLOW_REASON_COMPAT;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 9e3e3553c125..5d9f3357125a 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -23,6 +23,7 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_DEFAULT_APP_RESTORED;
import static android.app.usage.UsageStatsManager.REASON_SUB_DEFAULT_APP_UPDATE;
import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_USER_FLAG_INTERACTION;
@@ -1605,6 +1606,26 @@ public class AppStandbyController
}
@Override
+ public void restoreAppsToRare(Set<String> restoredApps, int userId) {
+ final int reason = REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_APP_RESTORED;
+ final long nowElapsed = mInjector.elapsedRealtime();
+ for (String packageName : restoredApps) {
+ // If the package is not installed, don't allow the bucket to be set.
+ if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
+ Slog.e(TAG, "Tried to restore bucket for uninstalled app: " + packageName);
+ continue;
+ }
+
+ final int standbyBucket = getAppStandbyBucket(packageName, userId, nowElapsed, false);
+ // Only update the standby bucket to RARE if the app is still in the NEVER bucket.
+ if (standbyBucket == STANDBY_BUCKET_NEVER) {
+ setAppStandbyBucket(packageName, userId, STANDBY_BUCKET_RARE, reason,
+ nowElapsed, false);
+ }
+ }
+ }
+
+ @Override
public void setAppStandbyBucket(@NonNull String packageName, int bucket, int userId,
int callingUid, int callingPid) {
setAppStandbyBuckets(
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 1796c7b971a9..8bfb1aef1989 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -488,8 +488,6 @@ package android.app {
field public static final int WINDOWING_MODE_FULLSCREEN = 1; // 0x1
field public static final int WINDOWING_MODE_MULTI_WINDOW = 6; // 0x6
field public static final int WINDOWING_MODE_PINNED = 2; // 0x2
- field public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = 3; // 0x3
- field public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = 4; // 0x4
field public static final int WINDOWING_MODE_UNDEFINED = 0; // 0x0
}
@@ -1863,7 +1861,6 @@ package android.os {
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public String getUserType();
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
- method public static boolean isGuestUserEphemeral();
method public static boolean isSplitSystemUser();
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo preCreateUser(@NonNull String) throws android.os.UserManager.UserOperationException;
}
@@ -3402,7 +3399,8 @@ package android.window {
method @NonNull public android.window.WindowContainerTransaction reparentTasks(@Nullable android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, @Nullable int[], @Nullable int[], boolean);
method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int);
- method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken, boolean);
+ method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken);
+ method @Deprecated @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken, boolean);
method @NonNull public android.window.WindowContainerTransaction setAdjacentTaskFragments(@NonNull android.os.IBinder, @Nullable android.os.IBinder, @Nullable android.window.WindowContainerTransaction.TaskFragmentAdjacentParams);
method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 7141259d7dce..90c37d1999e1 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6433,6 +6433,20 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Ensures the activity's result is immediately returned to the caller when {@link #finish()}
+ * is invoked
+ *
+ * <p>Should be invoked alongside {@link #setResult(int, Intent)}, so the provided results are
+ * in place before finishing. Must only be invoked during MediaProjection setup.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
+ public final void setForceSendResultForMediaProjection() {
+ ActivityClient.getInstance().setForceSendResultForMediaProjection(mToken);
+ }
+
+ /**
* Call this to set the result that your activity will return to its
* caller.
*
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index 73678d9f2dda..482f456b5d83 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.content.ComponentName;
import android.content.Intent;
import android.content.res.Configuration;
@@ -184,6 +185,15 @@ public class ActivityClient {
}
}
+ @RequiresPermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
+ void setForceSendResultForMediaProjection(IBinder token) {
+ try {
+ getActivityClientController().setForceSendResultForMediaProjection(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
public boolean isTopOfTask(IBinder token) {
try {
return getActivityClientController().isTopOfTask(token);
@@ -217,6 +227,18 @@ public class ActivityClient {
}
/**
+ * Returns the windowing mode of the task that hosts the activity, or {@code -1} if task is not
+ * found.
+ */
+ public int getTaskWindowingMode(IBinder activityToken) {
+ try {
+ return getActivityClientController().getTaskWindowingMode(activityToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the non-finishing activity token below in the same task if it belongs to the same
* process.
*/
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 5d1d225f4d2d..e25e374122cf 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2210,7 +2210,6 @@ public class ActivityManager {
pw.print(((baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0));
pw.print(" activityType="); pw.print(activityTypeToString(getActivityType()));
pw.print(" windowingMode="); pw.print(windowingModeToString(getWindowingMode()));
- pw.print(" supportsSplitScreenMultiWindow="); pw.print(supportsSplitScreenMultiWindow);
pw.print(" supportsMultiWindow=");
pw.println(supportsMultiWindow);
if (taskDescription != null) {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index d6441a2b629b..4fc3254ed1a3 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1149,7 +1149,6 @@ public class ActivityOptions extends ComponentOptions {
opts.mLaunchIntoPipParams = new PictureInPictureParams.Builder(pictureInPictureParams)
.setIsLaunchIntoPip(true)
.build();
- opts.mLaunchBounds = new Rect(pictureInPictureParams.getSourceRectHint());
return opts;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ae8809d8ac29..f384fa9e6a0b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3423,26 +3423,12 @@ public final class ActivityThread extends ClientTransactionHandler
}
}
- /**
- * Returns {@code true} if the {@link android.app.ActivityManager.ProcessState} of the current
- * process is cached.
- */
- @Override
- @VisibleForTesting
- public boolean isCachedProcessState() {
- synchronized (mAppThread) {
- return mLastProcessState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
- }
- }
-
@Override
public void updateProcessState(int processState, boolean fromIpc) {
- final boolean wasCached;
synchronized (mAppThread) {
if (mLastProcessState == processState) {
return;
}
- wasCached = isCachedProcessState();
mLastProcessState = processState;
updateVmProcessState(processState);
if (localLOGV) {
@@ -3450,22 +3436,6 @@ public final class ActivityThread extends ClientTransactionHandler
+ (fromIpc ? " (from ipc" : ""));
}
}
-
- // Handle the pending configuration if the process state is changed from cached to
- // non-cached. Except the case where there is a launching activity because the
- // LaunchActivityItem will handle it.
- if (wasCached && !isCachedProcessState() && mNumLaunchingActivities.get() == 0) {
- final Configuration pendingConfig =
- mConfigurationController.getPendingConfiguration(false /* clearPending */);
- if (pendingConfig == null) {
- return;
- }
- if (Looper.myLooper() == mH.getLooper()) {
- handleConfigurationChanged(pendingConfig);
- } else {
- sendMessage(H.CONFIGURATION_CHANGED, pendingConfig);
- }
- }
}
/** Update VM state based on ActivityManager.PROCESS_STATE_* constants. */
@@ -5893,20 +5863,20 @@ public final class ActivityThread extends ClientTransactionHandler
final boolean movedToDifferentDisplay = isDifferentDisplay(activity.getDisplayId(),
displayId);
- final Configuration currentConfig = activity.mCurrentConfig;
- final int diff = currentConfig.diffPublicOnly(newConfig);
- final boolean hasPublicConfigChange = diff != 0;
+ final Configuration currentResConfig = activity.getResources().getConfiguration();
+ final int diff = currentResConfig.diffPublicOnly(newConfig);
+ final boolean hasPublicResConfigChange = diff != 0;
final ActivityClientRecord r = getActivityClient(activityToken);
// TODO(b/173090263): Use diff instead after the improvement of AssetManager and
// ResourcesImpl constructions.
- final boolean shouldUpdateResources = hasPublicConfigChange
- || shouldUpdateResources(activityToken, currentConfig, newConfig, amOverrideConfig,
- movedToDifferentDisplay, hasPublicConfigChange);
- final boolean shouldReportChange = shouldReportChange(diff, currentConfig, newConfig,
+ final boolean shouldUpdateResources = hasPublicResConfigChange
+ || shouldUpdateResources(activityToken, currentResConfig, newConfig,
+ amOverrideConfig, movedToDifferentDisplay, hasPublicResConfigChange);
+ final boolean shouldReportChange = shouldReportChange(activity.mCurrentConfig, newConfig,
r != null ? r.mSizeConfigurations : null,
activity.mActivityInfo.getRealConfigChanged());
// Nothing significant, don't proceed with updating and reporting.
- if (!shouldUpdateResources) {
+ if (!shouldUpdateResources && !shouldReportChange) {
return null;
}
@@ -5926,9 +5896,6 @@ public final class ActivityThread extends ClientTransactionHandler
amOverrideConfig, contextThemeWrapperOverrideConfig);
mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId);
- activity.mConfigChangeFlags = 0;
- activity.mCurrentConfig = new Configuration(newConfig);
-
// Apply the ContextThemeWrapper override if necessary.
// NOTE: Make sure the configurations are not modified, as they are treated as immutable
// in many places.
@@ -5939,8 +5906,10 @@ public final class ActivityThread extends ClientTransactionHandler
activity.dispatchMovedToDisplay(displayId, configToReport);
}
+ activity.mConfigChangeFlags = 0;
if (shouldReportChange) {
activity.mCalled = false;
+ activity.mCurrentConfig = new Configuration(newConfig);
activity.onConfigurationChanged(configToReport);
if (!activity.mCalled) {
throw new SuperNotCalledException("Activity " + activity.getLocalClassName() +
@@ -5955,8 +5924,6 @@ public final class ActivityThread extends ClientTransactionHandler
* Returns {@code true} if {@link Activity#onConfigurationChanged(Configuration)} should be
* dispatched.
*
- * @param publicDiff Usually computed by {@link Configuration#diffPublicOnly(Configuration)}.
- * This parameter is to prevent we compute it again.
* @param currentConfig The current configuration cached in {@link Activity#mCurrentConfig}.
* It is {@code null} before the first config update from the server side.
* @param newConfig The updated {@link Configuration}
@@ -5965,9 +5932,10 @@ public final class ActivityThread extends ClientTransactionHandler
* @return {@code true} if the config change should be reported to the Activity
*/
@VisibleForTesting
- public static boolean shouldReportChange(int publicDiff, @Nullable Configuration currentConfig,
+ public static boolean shouldReportChange(@Nullable Configuration currentConfig,
@NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets sizeBuckets,
int handledConfigChanges) {
+ final int publicDiff = currentConfig.diffPublicOnly(newConfig);
// Don't report the change if there's no public diff between current and new config.
if (publicDiff == 0) {
return false;
diff --git a/core/java/android/app/ActivityThreadInternal.java b/core/java/android/app/ActivityThreadInternal.java
index b9ad5c337813..72506b9fcdbb 100644
--- a/core/java/android/app/ActivityThreadInternal.java
+++ b/core/java/android/app/ActivityThreadInternal.java
@@ -32,8 +32,6 @@ interface ActivityThreadInternal {
boolean isInDensityCompatMode();
- boolean isCachedProcessState();
-
Application getApplication();
ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts);
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 877e7d3b3bf7..57dacd024ba1 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -263,6 +263,11 @@ class ActivityTransitionState {
// After orientation change, the onResume can come in before the top Activity has
// left, so if the Activity is not top, wait a second for the top Activity to exit.
if (mEnterTransitionCoordinator == null || activity.isTopOfTask()) {
+ if (mEnterTransitionCoordinator != null) {
+ mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
+ mEnterTransitionCoordinator = null;
+ });
+ }
restoreExitedViews();
restoreReenteringViews();
} else {
@@ -271,6 +276,11 @@ class ActivityTransitionState {
public void run() {
if (mEnterTransitionCoordinator == null ||
mEnterTransitionCoordinator.isWaitingForRemoteExit()) {
+ if (mEnterTransitionCoordinator != null) {
+ mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
+ mEnterTransitionCoordinator = null;
+ });
+ }
restoreExitedViews();
restoreReenteringViews();
} else if (mEnterTransitionCoordinator.isReturning()) {
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 56f8760f6059..f0e14483d98a 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -53,6 +53,7 @@ public class BroadcastOptions extends ComponentOptions {
private String[] mRequireNoneOfPermissions;
private long mRequireCompatChangeId = CHANGE_INVALID;
private boolean mRequireCompatChangeEnabled = true;
+ private boolean mIsAlarmBroadcast = false;
private long mIdForResponseEvent;
/**
@@ -149,6 +150,13 @@ public class BroadcastOptions extends ComponentOptions {
"android:broadcast.requireCompatChangeEnabled";
/**
+ * Corresponds to {@link #setAlarmBroadcast(boolean)}
+ * @hide
+ */
+ public static final String KEY_ALARM_BROADCAST =
+ "android:broadcast.is_alarm";
+
+ /**
* @hide
* @deprecated Use {@link android.os.PowerExemptionManager#
* TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED} instead.
@@ -207,6 +215,7 @@ public class BroadcastOptions extends ComponentOptions {
mRequireCompatChangeId = opts.getLong(KEY_REQUIRE_COMPAT_CHANGE_ID, CHANGE_INVALID);
mRequireCompatChangeEnabled = opts.getBoolean(KEY_REQUIRE_COMPAT_CHANGE_ENABLED, true);
mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT);
+ mIsAlarmBroadcast = opts.getBoolean(KEY_ALARM_BROADCAST, false);
}
/**
@@ -498,6 +507,27 @@ public class BroadcastOptions extends ComponentOptions {
mRequireCompatChangeEnabled = true;
}
+ /**
+ * When set, this broadcast will be understood as having originated from an
+ * alarm going off. Only the OS itself can use this option; uses by other
+ * senders will be ignored.
+ * @hide
+ *
+ * @param senderIsAlarm Whether the broadcast is alarm-triggered.
+ */
+ public void setAlarmBroadcast(boolean senderIsAlarm) {
+ mIsAlarmBroadcast = senderIsAlarm;
+ }
+
+ /**
+ * Did this broadcast originate from an alarm triggering?
+ * @return true if this broadcast is an alarm message, false otherwise
+ * @hide
+ */
+ public boolean isAlarmBroadcast() {
+ return mIsAlarmBroadcast;
+ }
+
/** {@hide} */
public long getRequireCompatChangeId() {
return mRequireCompatChangeId;
@@ -560,6 +590,9 @@ public class BroadcastOptions extends ComponentOptions {
b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE, mTemporaryAppAllowlistReasonCode);
b.putString(KEY_TEMPORARY_APP_ALLOWLIST_REASON, mTemporaryAppAllowlistReason);
}
+ if (mIsAlarmBroadcast) {
+ b.putBoolean(KEY_ALARM_BROADCAST, true);
+ }
if (mMinManifestReceiverApiLevel != 0) {
b.putInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, mMinManifestReceiverApiLevel);
}
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index 1a77b65c8ef6..18dc1ce18baf 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -124,12 +124,6 @@ class ConfigurationController {
* @param config The new configuration.
*/
void handleConfigurationChanged(@NonNull Configuration config) {
- if (mActivityThread.isCachedProcessState()) {
- updatePendingConfiguration(config);
- // If the process is in a cached state, delay the handling until the process is no
- // longer cached.
- return;
- }
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
handleConfigurationChanged(config, null /* compat */);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index 0138186974a6..f5e5cda9c639 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -67,11 +67,18 @@ interface IActivityClientController {
boolean finishActivityAffinity(in IBinder token);
/** Finish all activities that were started for result from the specified activity. */
void finishSubActivity(in IBinder token, in String resultWho, int requestCode);
+ /**
+ * Indicates that when the activity finsihes, the result should be immediately sent to the
+ * originating activity. Must only be invoked during MediaProjection setup.
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)")
+ void setForceSendResultForMediaProjection(in IBinder token);
boolean isTopOfTask(in IBinder token);
boolean willActivityBeVisible(in IBinder token);
int getDisplayId(in IBinder activityToken);
int getTaskForActivity(in IBinder token, in boolean onlyRoot);
+ int getTaskWindowingMode(in IBinder activityToken);
IBinder getActivityTokenBelow(IBinder token);
ComponentName getCallingActivity(in IBinder token);
String getCallingPackage(in IBinder token);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index fe75dd302beb..dc6825c85705 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -263,9 +263,12 @@ interface IActivityTaskManager {
* @param taskId the id of the task to retrieve the sAutoapshots for
* @param isLowResolution if set, if the snapshot needs to be loaded from disk, this will load
* a reduced resolution of it, which is much faster
+ * @param takeSnapshotIfNeeded if set, call {@link #takeTaskSnapshot} to trigger the snapshot
+ if no cache exists.
* @return a graphic buffer representing a screenshot of a task
*/
- android.window.TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution);
+ android.window.TaskSnapshot getTaskSnapshot(
+ int taskId, boolean isLowResolution, boolean takeSnapshotIfNeeded);
/**
* @param taskId the id of the task to take a snapshot of
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 8984c4292023..556058b567f9 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -783,6 +783,17 @@ public class Instrumentation {
return null;
}
+ /**
+ * This is called after starting an Activity and provides the result code that defined in
+ * {@link ActivityManager}, like {@link ActivityManager#START_SUCCESS}.
+ *
+ * @param result the result code that returns after starting an Activity.
+ * @param bOptions the bundle generated from {@link ActivityOptions} that originally
+ * being used to start the Activity.
+ * @hide
+ */
+ public void onStartActivityResult(int result, @NonNull Bundle bOptions) {}
+
final boolean match(Context who,
Activity activity,
Intent intent) {
@@ -1344,6 +1355,28 @@ public class Instrumentation {
return apk.getAppFactory();
}
+ /**
+ * This should be called before {@link #checkStartActivityResult(int, Object)}, because
+ * exceptions might be thrown while checking the results.
+ */
+ private void notifyStartActivityResult(int result, @Nullable Bundle options) {
+ if (mActivityMonitors == null) {
+ return;
+ }
+ synchronized (mSync) {
+ final int size = mActivityMonitors.size();
+ for (int i = 0; i < size; i++) {
+ final ActivityMonitor am = mActivityMonitors.get(i);
+ if (am.ignoreMatchingSpecificIntents()) {
+ if (options == null) {
+ options = ActivityOptions.makeBasic().toBundle();
+ }
+ am.onStartActivityResult(result, options);
+ }
+ }
+ }
+ }
+
private void prePerformCreate(Activity activity) {
if (mWaitingActivities != null) {
synchronized (mSync) {
@@ -1802,6 +1835,7 @@ public class Instrumentation {
who.getOpPackageName(), who.getAttributionTag(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()), token,
target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
+ notifyStartActivityResult(result, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
@@ -1876,6 +1910,7 @@ public class Instrumentation {
int result = ActivityTaskManager.getService().startActivities(whoThread,
who.getOpPackageName(), who.getAttributionTag(), intents, resolvedTypes,
token, options, userId);
+ notifyStartActivityResult(result, options);
checkStartActivityResult(result, intents[0]);
return result;
} catch (RemoteException e) {
@@ -1947,6 +1982,7 @@ public class Instrumentation {
who.getOpPackageName(), who.getAttributionTag(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()), token, target,
requestCode, 0, null, options);
+ notifyStartActivityResult(result, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
@@ -2017,6 +2053,7 @@ public class Instrumentation {
who.getOpPackageName(), who.getAttributionTag(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()), token, resultWho,
requestCode, 0, null, options, user.getIdentifier());
+ notifyStartActivityResult(result, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
@@ -2068,6 +2105,7 @@ public class Instrumentation {
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options,
ignoreTargetSecurity, userId);
+ notifyStartActivityResult(result, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
@@ -2115,6 +2153,7 @@ public class Instrumentation {
int result = appTask.startActivity(whoThread.asBinder(), who.getOpPackageName(),
who.getAttributionTag(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()), options);
+ notifyStartActivityResult(result, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index f97415ca20c8..1769993e0e07 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -20,6 +20,7 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
+import android.content.pm.ParceledListSlice;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -66,7 +67,7 @@ public final class NotificationChannelGroup implements Parcelable {
private CharSequence mName;
private String mDescription;
private boolean mBlocked;
- private List<NotificationChannel> mChannels = new ArrayList<>();
+ private ParceledListSlice<NotificationChannel> mChannels;
// Bitwise representation of fields that have been changed by the user
private int mUserLockedFields;
@@ -100,7 +101,8 @@ public final class NotificationChannelGroup implements Parcelable {
} else {
mDescription = null;
}
- in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader(), android.app.NotificationChannel.class);
+ mChannels = in.readParcelable(
+ NotificationChannelGroup.class.getClassLoader(), ParceledListSlice.class);
mBlocked = in.readBoolean();
mUserLockedFields = in.readInt();
}
@@ -127,7 +129,7 @@ public final class NotificationChannelGroup implements Parcelable {
} else {
dest.writeByte((byte) 0);
}
- dest.writeParcelableList(mChannels, flags);
+ dest.writeParcelable(mChannels, flags);
dest.writeBoolean(mBlocked);
dest.writeInt(mUserLockedFields);
}
@@ -157,7 +159,7 @@ public final class NotificationChannelGroup implements Parcelable {
* Returns the list of channels that belong to this group
*/
public List<NotificationChannel> getChannels() {
- return mChannels;
+ return mChannels == null ? new ArrayList<>() : mChannels.getList();
}
/**
@@ -191,15 +193,8 @@ public final class NotificationChannelGroup implements Parcelable {
/**
* @hide
*/
- public void addChannel(NotificationChannel channel) {
- mChannels.add(channel);
- }
-
- /**
- * @hide
- */
public void setChannels(List<NotificationChannel> channels) {
- mChannels = channels;
+ mChannels = new ParceledListSlice<>(channels);
}
/**
@@ -334,7 +329,7 @@ public final class NotificationChannelGroup implements Parcelable {
proto.write(NotificationChannelGroupProto.NAME, mName.toString());
proto.write(NotificationChannelGroupProto.DESCRIPTION, mDescription);
proto.write(NotificationChannelGroupProto.IS_BLOCKED, mBlocked);
- for (NotificationChannel channel : mChannels) {
+ for (NotificationChannel channel : mChannels.getList()) {
channel.dumpDebug(proto, NotificationChannelGroupProto.CHANNELS);
}
proto.end(token);
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 2a1883d5e0bb..d275c8336251 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -365,8 +365,8 @@ public class ResourcesManager {
@NonNull Configuration config) {
config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
config.densityDpi = dm.densityDpi;
- config.screenWidthDp = (int) (dm.widthPixels / dm.density);
- config.screenHeightDp = (int) (dm.heightPixels / dm.density);
+ config.screenWidthDp = (int) (dm.widthPixels / dm.density + 0.5f);
+ config.screenHeightDp = (int) (dm.heightPixels / dm.density + 0.5f);
int sl = Configuration.resetScreenLayout(config.screenLayout);
if (dm.widthPixels > dm.heightPixels) {
config.orientation = Configuration.ORIENTATION_LANDSCAPE;
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 7910f1a426c2..b09463e3074b 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -140,13 +140,6 @@ public class TaskInfo {
public LocusId mTopActivityLocusId;
/**
- * True if the task can go in the split-screen primary stack.
- * @hide
- */
- @UnsupportedAppUsage
- public boolean supportsSplitScreenMultiWindow;
-
- /**
* Whether this task supports multi windowing modes based on the device settings and the
* root activity resizability and configuration.
* @hide
@@ -499,7 +492,6 @@ public class TaskInfo {
lastActiveTime = source.readLong();
taskDescription = source.readTypedObject(ActivityManager.TaskDescription.CREATOR);
- supportsSplitScreenMultiWindow = source.readBoolean();
supportsMultiWindow = source.readBoolean();
resizeMode = source.readInt();
configuration.readFromParcel(source);
@@ -546,7 +538,6 @@ public class TaskInfo {
dest.writeLong(lastActiveTime);
dest.writeTypedObject(taskDescription, flags);
- dest.writeBoolean(supportsSplitScreenMultiWindow);
dest.writeBoolean(supportsMultiWindow);
dest.writeInt(resizeMode);
configuration.writeToParcel(dest, flags);
@@ -584,7 +575,6 @@ public class TaskInfo {
+ " realActivity=" + realActivity
+ " numActivities=" + numActivities
+ " lastActiveTime=" + lastActiveTime
- + " supportsSplitScreenMultiWindow=" + supportsSplitScreenMultiWindow
+ " supportsMultiWindow=" + supportsMultiWindow
+ " resizeMode=" + resizeMode
+ " isResizeable=" + isResizeable
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index e96a98697184..d0ea8d41d65c 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -104,19 +104,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
public static final int WINDOWING_MODE_FULLSCREEN = 1;
/** Always on-top (always visible). of other siblings in its parent container. */
public static final int WINDOWING_MODE_PINNED = 2;
- /** The primary container driving the screen to be in split-screen mode. */
- // TODO: Remove once split-screen is migrated to wm-shell.
- public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = 3;
- /**
- * The containers adjacent to the {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} container in
- * split-screen mode.
- * NOTE: Containers launched with the windowing mode with APIs like
- * {@link ActivityOptions#setLaunchWindowingMode(int)} will be launched in
- * {@link #WINDOWING_MODE_FULLSCREEN} if the display isn't currently in split-screen windowing
- * mode
- */
- // TODO: Remove once split-screen is migrated to wm-shell.
- public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = 4;
/** Can be freely resized within its parent container. */
// TODO: Remove once freeform is migrated to wm-shell.
public static final int WINDOWING_MODE_FREEFORM = 5;
@@ -129,8 +116,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
WINDOWING_MODE_FULLSCREEN,
WINDOWING_MODE_MULTI_WINDOW,
WINDOWING_MODE_PINNED,
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
WINDOWING_MODE_FREEFORM,
})
public @interface WindowingMode {}
@@ -890,9 +875,8 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
}
/**
- * Returns true if this container can be put in either
- * {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} or
- * {@link #WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} windowing modes based on its current state.
+ * Returns true if this container can be put in {@link #WINDOWING_MODE_MULTI_WINDOW}
+ * windowing mode based on its current state.
* @hide
*/
public boolean supportSplitScreenWindowingMode() {
@@ -911,8 +895,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
case WINDOWING_MODE_FULLSCREEN: return "fullscreen";
case WINDOWING_MODE_MULTI_WINDOW: return "multi-window";
case WINDOWING_MODE_PINNED: return "pinned";
- case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return "split-screen-primary";
- case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return "split-screen-secondary";
case WINDOWING_MODE_FREEFORM: return "freeform";
}
return String.valueOf(windowingMode);
diff --git a/core/java/android/app/search/SearchAction.java b/core/java/android/app/search/SearchAction.java
index 9e40e7ebaef0..0c4508a4fb8d 100644
--- a/core/java/android/app/search/SearchAction.java
+++ b/core/java/android/app/search/SearchAction.java
@@ -67,7 +67,7 @@ public final class SearchAction implements Parcelable {
private final UserHandle mUserHandle;
@Nullable
- private Bundle mExtras;
+ private final Bundle mExtras;
SearchAction(Parcel in) {
mId = in.readString();
@@ -99,7 +99,7 @@ public final class SearchAction implements Parcelable {
mPendingIntent = pendingIntent;
mIntent = intent;
mUserHandle = userHandle;
- mExtras = extras;
+ mExtras = extras != null ? extras : new Bundle();
if (mPendingIntent == null && mIntent == null) {
throw new IllegalStateException("At least one type of intent should be available.");
diff --git a/core/java/android/app/search/SearchTarget.java b/core/java/android/app/search/SearchTarget.java
index a590a5d7b767..a3874f7cb007 100644
--- a/core/java/android/app/search/SearchTarget.java
+++ b/core/java/android/app/search/SearchTarget.java
@@ -185,7 +185,7 @@ public final class SearchTarget implements Parcelable {
mShortcutInfo = shortcutInfo;
mAppWidgetProviderInfo = appWidgetProviderInfo;
mSliceUri = sliceUri;
- mExtras = extras;
+ mExtras = extras != null ? extras : new Bundle();
int published = 0;
if (mSearchAction != null) published++;
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index d61abc64fae5..e213c934e95a 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -293,6 +293,17 @@ public final class UsageStats implements Parcelable {
}
/**
+ * Returns the last time the package was used - defined by the latest of
+ * mLastTimeUsed, mLastTimeVisible, mLastTimeForegroundServiceUsed, or mLastTimeComponentUsed.
+ * @hide
+ */
+ public long getLastTimePackageUsed() {
+ return Math.max(mLastTimeUsed,
+ Math.max(mLastTimeVisible,
+ Math.max(mLastTimeForegroundServiceUsed, mLastTimeComponentUsed)));
+ }
+
+ /**
* Returns the number of times the app was launched as an activity from outside of the app.
* Excludes intra-app activity transitions.
* @hide
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index c013fcd5adb9..1dfc7d48640e 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -220,6 +220,11 @@ public final class UsageStatsManager {
*/
public static final int REASON_SUB_DEFAULT_APP_UPDATE = 0x0001;
/**
+ * The app was restored.
+ * @hide
+ */
+ public static final int REASON_SUB_DEFAULT_APP_RESTORED = 0x0002;
+ /**
* The app was interacted with in some way by the system.
* @hide
*/
@@ -1209,6 +1214,9 @@ public final class UsageStatsManager {
case REASON_SUB_DEFAULT_APP_UPDATE:
sb.append("-au");
break;
+ case REASON_SUB_DEFAULT_APP_RESTORED:
+ sb.append("-ar");
+ break;
}
break;
case REASON_MAIN_FORCED_BY_SYSTEM:
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 18c638112480..a432b8dec2cb 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -1130,7 +1130,9 @@ public class AppWidgetManager {
* @param intent The intent of the service which will be providing the data to the
* RemoteViewsAdapter.
* @param connection The callback interface to be notified when a connection is made or lost.
- * @param flags Flags used for binding to the service
+ * @param flags Flags used for binding to the service. Currently only
+ * {@link Context#BIND_AUTO_CREATE} and
+ * {@link Context#BIND_FOREGROUND_SERVICE_WHILE_AWAKE} are supported.
*
* @see Context#getServiceDispatcher(ServiceConnection, Handler, int)
* @hide
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index f7f0235cd508..93748f81ffa1 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -55,6 +55,14 @@ public final class AssociationInfo implements Parcelable {
private final boolean mSelfManaged;
private final boolean mNotifyOnDeviceNearby;
+
+ /**
+ * Indicates that the association has been revoked (removed), but we keep the association
+ * record for final clean up (e.g. removing the app from the list of the role holders).
+ *
+ * @see CompanionDeviceManager#disassociate(int)
+ */
+ private final boolean mRevoked;
private final long mTimeApprovedMs;
/**
* A long value indicates the last time connected reported by selfManaged devices
@@ -71,7 +79,7 @@ public final class AssociationInfo implements Parcelable {
public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName,
@Nullable MacAddress macAddress, @Nullable CharSequence displayName,
@Nullable String deviceProfile, boolean selfManaged, boolean notifyOnDeviceNearby,
- long timeApprovedMs, long lastTimeConnectedMs) {
+ boolean revoked, long timeApprovedMs, long lastTimeConnectedMs) {
if (id <= 0) {
throw new IllegalArgumentException("Association ID should be greater than 0");
}
@@ -91,6 +99,7 @@ public final class AssociationInfo implements Parcelable {
mSelfManaged = selfManaged;
mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+ mRevoked = revoked;
mTimeApprovedMs = timeApprovedMs;
mLastTimeConnectedMs = lastTimeConnectedMs;
}
@@ -176,6 +185,14 @@ public final class AssociationInfo implements Parcelable {
}
/**
+ * @return if the association has been revoked (removed).
+ * @hide
+ */
+ public boolean isRevoked() {
+ return mRevoked;
+ }
+
+ /**
* @return the last time self reported disconnected for selfManaged only.
* @hide
*/
@@ -244,6 +261,7 @@ public final class AssociationInfo implements Parcelable {
+ ", mDeviceProfile='" + mDeviceProfile + '\''
+ ", mSelfManaged=" + mSelfManaged
+ ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby
+ + ", mRevoked=" + mRevoked
+ ", mTimeApprovedMs=" + new Date(mTimeApprovedMs)
+ ", mLastTimeConnectedMs=" + (
mLastTimeConnectedMs == Long.MAX_VALUE
@@ -260,6 +278,7 @@ public final class AssociationInfo implements Parcelable {
&& mUserId == that.mUserId
&& mSelfManaged == that.mSelfManaged
&& mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
+ && mRevoked == that.mRevoked
&& mTimeApprovedMs == that.mTimeApprovedMs
&& mLastTimeConnectedMs == that.mLastTimeConnectedMs
&& Objects.equals(mPackageName, that.mPackageName)
@@ -271,7 +290,7 @@ public final class AssociationInfo implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName,
- mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mTimeApprovedMs,
+ mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mRevoked, mTimeApprovedMs,
mLastTimeConnectedMs);
}
@@ -293,6 +312,7 @@ public final class AssociationInfo implements Parcelable {
dest.writeBoolean(mSelfManaged);
dest.writeBoolean(mNotifyOnDeviceNearby);
+ dest.writeBoolean(mRevoked);
dest.writeLong(mTimeApprovedMs);
dest.writeLong(mLastTimeConnectedMs);
}
@@ -309,6 +329,7 @@ public final class AssociationInfo implements Parcelable {
mSelfManaged = in.readBoolean();
mNotifyOnDeviceNearby = in.readBoolean();
+ mRevoked = in.readBoolean();
mTimeApprovedMs = in.readLong();
mLastTimeConnectedMs = in.readLong();
}
@@ -352,11 +373,13 @@ public final class AssociationInfo implements Parcelable {
@NonNull
private final AssociationInfo mOriginalInfo;
private boolean mNotifyOnDeviceNearby;
+ private boolean mRevoked;
private long mLastTimeConnectedMs;
private Builder(@NonNull AssociationInfo info) {
mOriginalInfo = info;
mNotifyOnDeviceNearby = info.mNotifyOnDeviceNearby;
+ mRevoked = info.mRevoked;
mLastTimeConnectedMs = info.mLastTimeConnectedMs;
}
@@ -388,6 +411,17 @@ public final class AssociationInfo implements Parcelable {
}
/**
+ * Should only be used by the CompanionDeviceManagerService.
+ * @hide
+ */
+ @Override
+ @NonNull
+ public Builder setRevoked(boolean revoked) {
+ mRevoked = revoked;
+ return this;
+ }
+
+ /**
* @hide
*/
@NonNull
@@ -401,6 +435,7 @@ public final class AssociationInfo implements Parcelable {
mOriginalInfo.mDeviceProfile,
mOriginalInfo.mSelfManaged,
mNotifyOnDeviceNearby,
+ mRevoked,
mOriginalInfo.mTimeApprovedMs,
mLastTimeConnectedMs
);
@@ -433,5 +468,12 @@ public final class AssociationInfo implements Parcelable {
*/
@NonNull
Builder setLastTimeConnected(long lastTimeConnectedMs);
+
+ /**
+ * Should only be used by the CompanionDeviceManagerService.
+ * @hide
+ */
+ @NonNull
+ Builder setRevoked(boolean revoked);
}
}
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index d41cda102103..85af87722ed2 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -129,7 +129,11 @@ public class ClipboardManager extends android.text.ClipboardManager {
try {
Objects.requireNonNull(clip);
clip.prepareToLeaveProcess(true);
- mService.setPrimaryClip(clip, mContext.getOpPackageName(), mContext.getUserId());
+ mService.setPrimaryClip(
+ clip,
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag(),
+ mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -154,7 +158,11 @@ public class ClipboardManager extends android.text.ClipboardManager {
Objects.requireNonNull(sourcePackage);
clip.prepareToLeaveProcess(true);
mService.setPrimaryClipAsPackage(
- clip, mContext.getOpPackageName(), mContext.getUserId(), sourcePackage);
+ clip,
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag(),
+ mContext.getUserId(),
+ sourcePackage);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -167,7 +175,10 @@ public class ClipboardManager extends android.text.ClipboardManager {
*/
public void clearPrimaryClip() {
try {
- mService.clearPrimaryClip(mContext.getOpPackageName(), mContext.getUserId());
+ mService.clearPrimaryClip(
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag(),
+ mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -183,24 +194,29 @@ public class ClipboardManager extends android.text.ClipboardManager {
*/
public @Nullable ClipData getPrimaryClip() {
try {
- return mService.getPrimaryClip(mContext.getOpPackageName(), mContext.getUserId());
+ return mService.getPrimaryClip(
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag(),
+ mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Returns a description of the current primary clip on the clipboard
- * but not a copy of its data.
+ * Returns a description of the current primary clip on the clipboard but not a copy of its
+ * data.
*
- * <em>If the application is not the default IME or does not have input focus this return
+ * <p><em>If the application is not the default IME or does not have input focus this return
* {@code null}.</em>
*
* @see #setPrimaryClip(ClipData)
*/
public @Nullable ClipDescription getPrimaryClipDescription() {
try {
- return mService.getPrimaryClipDescription(mContext.getOpPackageName(),
+ return mService.getPrimaryClipDescription(
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag(),
mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -215,7 +231,10 @@ public class ClipboardManager extends android.text.ClipboardManager {
*/
public boolean hasPrimaryClip() {
try {
- return mService.hasPrimaryClip(mContext.getOpPackageName(), mContext.getUserId());
+ return mService.hasPrimaryClip(
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag(),
+ mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -226,7 +245,9 @@ public class ClipboardManager extends android.text.ClipboardManager {
if (mPrimaryClipChangedListeners.isEmpty()) {
try {
mService.addPrimaryClipChangedListener(
- mPrimaryClipChangedServiceListener, mContext.getOpPackageName(),
+ mPrimaryClipChangedServiceListener,
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag(),
mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -242,7 +263,9 @@ public class ClipboardManager extends android.text.ClipboardManager {
if (mPrimaryClipChangedListeners.isEmpty()) {
try {
mService.removePrimaryClipChangedListener(
- mPrimaryClipChangedServiceListener, mContext.getOpPackageName(),
+ mPrimaryClipChangedServiceListener,
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag(),
mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -280,7 +303,10 @@ public class ClipboardManager extends android.text.ClipboardManager {
@Deprecated
public boolean hasText() {
try {
- return mService.hasClipboardText(mContext.getOpPackageName(), mContext.getUserId());
+ return mService.hasClipboardText(
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag(),
+ mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -297,7 +323,10 @@ public class ClipboardManager extends android.text.ClipboardManager {
@RequiresPermission(Manifest.permission.SET_CLIP_SOURCE)
public String getPrimaryClipSource() {
try {
- return mService.getPrimaryClipSource(mContext.getOpPackageName(), mContext.getUserId());
+ return mService.getPrimaryClipSource(
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag(),
+ mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/IClipboard.aidl b/core/java/android/content/IClipboard.aidl
index 102b8e798a5c..46ece2bc3f5e 100644
--- a/core/java/android/content/IClipboard.aidl
+++ b/core/java/android/content/IClipboard.aidl
@@ -26,22 +26,22 @@ import android.content.IOnPrimaryClipChangedListener;
* {@hide}
*/
interface IClipboard {
- void setPrimaryClip(in ClipData clip, String callingPackage, int userId);
- void setPrimaryClipAsPackage(in ClipData clip, String callingPackage, int userId,
+ void setPrimaryClip(in ClipData clip, String callingPackage, String attributionTag, int userId);
+ void setPrimaryClipAsPackage(in ClipData clip, String callingPackage, String attributionTag, int userId,
String sourcePackage);
- void clearPrimaryClip(String callingPackage, int userId);
- ClipData getPrimaryClip(String pkg, int userId);
- ClipDescription getPrimaryClipDescription(String callingPackage, int userId);
- boolean hasPrimaryClip(String callingPackage, int userId);
+ void clearPrimaryClip(String callingPackage, String attributionTag, int userId);
+ ClipData getPrimaryClip(String pkg, String attributionTag, int userId);
+ ClipDescription getPrimaryClipDescription(String callingPackage, String attributionTag, int userId);
+ boolean hasPrimaryClip(String callingPackage, String attributionTag, int userId);
void addPrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener,
- String callingPackage, int userId);
+ String callingPackage, String attributionTag, int userId);
void removePrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener,
- String callingPackage, int userId);
+ String callingPackage, String attributionTag, int userId);
/**
* Returns true if the clipboard contains text; false otherwise.
*/
- boolean hasClipboardText(String callingPackage, int userId);
+ boolean hasClipboardText(String callingPackage, String attributionTag, int userId);
- String getPrimaryClipSource(String callingPackage, int userId);
+ String getPrimaryClipSource(String callingPackage, String attributionTag, int userId);
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 0673b3ad5b0a..a3d595ef6575 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -106,6 +106,24 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
public @interface LaunchMode {
}
+ /** @hide */
+ public static String launchModeToString(@LaunchMode int launchMode) {
+ switch(launchMode) {
+ case LAUNCH_MULTIPLE:
+ return "LAUNCH_MULTIPLE";
+ case LAUNCH_SINGLE_TOP:
+ return "LAUNCH_SINGLE_TOP";
+ case LAUNCH_SINGLE_TASK:
+ return "LAUNCH_SINGLE_TASK";
+ case LAUNCH_SINGLE_INSTANCE:
+ return "LAUNCH_SINGLE_INSTANCE";
+ case LAUNCH_SINGLE_INSTANCE_PER_TASK:
+ return "LAUNCH_SINGLE_INSTANCE_PER_TASK";
+ default:
+ return "unknown=" + launchMode;
+ }
+ }
+
/**
* The launch mode style requested by the activity. From the
* {@link android.R.attr#launchMode} attribute.
@@ -1585,7 +1603,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
+ " persistableMode=" + persistableModeToString());
}
if (launchMode != 0 || flags != 0 || privateFlags != 0 || theme != 0) {
- pw.println(prefix + "launchMode=" + launchMode
+ pw.println(prefix + "launchMode=" + launchModeToString(launchMode)
+ " flags=0x" + Integer.toHexString(flags)
+ " privateFlags=0x" + Integer.toHexString(privateFlags)
+ " theme=0x" + Integer.toHexString(theme));
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 44dc28d2b0fa..c15b3e0b80c3 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -713,6 +713,15 @@ public class PackageParser {
if (!checkUseInstalledOrHidden(flags, state, p.applicationInfo) || !p.isMatch(flags)) {
return null;
}
+
+ final ApplicationInfo applicationInfo;
+ if ((flags & (PackageManager.GET_ACTIVITIES | PackageManager.GET_RECEIVERS
+ | PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS)) != 0) {
+ applicationInfo = generateApplicationInfo(p, flags, state, userId);
+ } else {
+ applicationInfo = null;
+ }
+
PackageInfo pi = new PackageInfo();
pi.packageName = p.packageName;
pi.splitNames = p.splitNames;
@@ -773,7 +782,7 @@ public class PackageParser {
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(a.className)) {
continue;
}
- res[num++] = generateActivityInfo(a, flags, state, userId);
+ res[num++] = generateActivityInfo(a, flags, state, userId, applicationInfo);
}
}
pi.activities = ArrayUtils.trimToSize(res, num);
@@ -787,7 +796,7 @@ public class PackageParser {
for (int i = 0; i < N; i++) {
final Activity a = p.receivers.get(i);
if (isMatch(state, a.info, flags)) {
- res[num++] = generateActivityInfo(a, flags, state, userId);
+ res[num++] = generateActivityInfo(a, flags, state, userId, applicationInfo);
}
}
pi.receivers = ArrayUtils.trimToSize(res, num);
@@ -801,7 +810,7 @@ public class PackageParser {
for (int i = 0; i < N; i++) {
final Service s = p.services.get(i);
if (isMatch(state, s.info, flags)) {
- res[num++] = generateServiceInfo(s, flags, state, userId);
+ res[num++] = generateServiceInfo(s, flags, state, userId, applicationInfo);
}
}
pi.services = ArrayUtils.trimToSize(res, num);
@@ -815,7 +824,8 @@ public class PackageParser {
for (int i = 0; i < N; i++) {
final Provider pr = p.providers.get(i);
if (isMatch(state, pr.info, flags)) {
- res[num++] = generateProviderInfo(pr, flags, state, userId);
+ res[num++] = generateProviderInfo(pr, flags, state, userId,
+ applicationInfo);
}
}
pi.providers = ArrayUtils.trimToSize(res, num);
@@ -8216,6 +8226,11 @@ public class PackageParser {
@UnsupportedAppUsage
public static final ActivityInfo generateActivityInfo(Activity a, int flags,
FrameworkPackageUserState state, int userId) {
+ return generateActivityInfo(a, flags, state, userId, null);
+ }
+
+ private static ActivityInfo generateActivityInfo(Activity a, int flags,
+ FrameworkPackageUserState state, int userId, ApplicationInfo applicationInfo) {
if (a == null) return null;
if (!checkUseInstalledOrHidden(flags, state, a.owner.applicationInfo)) {
return null;
@@ -8227,7 +8242,12 @@ public class PackageParser {
// Make shallow copies so we can store the metadata safely
ActivityInfo ai = new ActivityInfo(a.info);
ai.metaData = a.metaData;
- ai.applicationInfo = generateApplicationInfo(a.owner, flags, state, userId);
+
+ if (applicationInfo == null) {
+ applicationInfo = generateApplicationInfo(a.owner, flags, state, userId);
+ }
+ ai.applicationInfo = applicationInfo;
+
return ai;
}
@@ -8308,6 +8328,11 @@ public class PackageParser {
@UnsupportedAppUsage
public static final ServiceInfo generateServiceInfo(Service s, int flags,
FrameworkPackageUserState state, int userId) {
+ return generateServiceInfo(s, flags, state, userId, null);
+ }
+
+ private static ServiceInfo generateServiceInfo(Service s, int flags,
+ FrameworkPackageUserState state, int userId, ApplicationInfo applicationInfo) {
if (s == null) return null;
if (!checkUseInstalledOrHidden(flags, state, s.owner.applicationInfo)) {
return null;
@@ -8319,7 +8344,12 @@ public class PackageParser {
// Make shallow copies so we can store the metadata safely
ServiceInfo si = new ServiceInfo(s.info);
si.metaData = s.metaData;
- si.applicationInfo = generateApplicationInfo(s.owner, flags, state, userId);
+
+ if (applicationInfo == null) {
+ applicationInfo = generateApplicationInfo(s.owner, flags, state, userId);
+ }
+ si.applicationInfo = applicationInfo;
+
return si;
}
@@ -8406,13 +8436,18 @@ public class PackageParser {
@UnsupportedAppUsage
public static final ProviderInfo generateProviderInfo(Provider p, int flags,
FrameworkPackageUserState state, int userId) {
+ return generateProviderInfo(p, flags, state, userId, null);
+ }
+
+ private static ProviderInfo generateProviderInfo(Provider p, int flags,
+ FrameworkPackageUserState state, int userId, ApplicationInfo applicationInfo) {
if (p == null) return null;
if (!checkUseInstalledOrHidden(flags, state, p.owner.applicationInfo)) {
return null;
}
if (!copyNeeded(flags, p.owner, state, p.metaData, userId)
&& ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
- || p.info.uriPermissionPatterns == null)) {
+ || p.info.uriPermissionPatterns == null)) {
updateApplicationInfo(p.info.applicationInfo, flags, state);
return p.info;
}
@@ -8422,7 +8457,12 @@ public class PackageParser {
if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
pi.uriPermissionPatterns = null;
}
- pi.applicationInfo = generateApplicationInfo(p.owner, flags, state, userId);
+
+ if (applicationInfo == null) {
+ applicationInfo = generateApplicationInfo(p.owner, flags, state, userId);
+ }
+ pi.applicationInfo = applicationInfo;
+
return pi;
}
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index d6e13ac90f82..9baa6ba2fb49 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -143,6 +143,22 @@ public class UserInfo implements Parcelable {
public static final int FLAG_PROFILE = 0x00001000;
/**
+ * Indicates that this user is created in ephemeral mode via
+ * {@link IUserManager} create user.
+ *
+ * When a user is created with {@link #FLAG_EPHEMERAL}, {@link #FLAG_EPHEMERAL_ON_CREATE}
+ * is set internally within the user manager.
+ *
+ * When {@link #FLAG_EPHEMERAL_ON_CREATE} is set {@link IUserManager.setUserEphemeral}
+ * has no effect because a user that was created ephemeral can never be made non-ephemeral.
+ *
+ * {@link #FLAG_EPHEMERAL_ON_CREATE} should NOT be set by client's of user manager
+ *
+ * @hide
+ */
+ public static final int FLAG_EPHEMERAL_ON_CREATE = 0x00002000;
+
+ /**
* @hide
*/
@IntDef(flag = true, prefix = "FLAG_", value = {
@@ -158,7 +174,8 @@ public class UserInfo implements Parcelable {
FLAG_DEMO,
FLAG_FULL,
FLAG_SYSTEM,
- FLAG_PROFILE
+ FLAG_PROFILE,
+ FLAG_EPHEMERAL_ON_CREATE
})
@Retention(RetentionPolicy.SOURCE)
public @interface UserInfoFlag {
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 439c6396f1d0..608e34bd07b4 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -420,7 +420,10 @@ public class CompatibilityInfo implements Parcelable {
* Translate a Rect in screen coordinates into the app window's coordinates.
*/
@UnsupportedAppUsage
- public void translateRectInScreenToAppWindow(Rect rect) {
+ public void translateRectInScreenToAppWindow(@Nullable Rect rect) {
+ if (rect == null) {
+ return;
+ }
rect.scale(applicationInvertedScale);
}
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index 7d8f2ff92200..8c71b363eb7b 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -89,7 +89,8 @@ public class AmbientDisplayConfiguration {
/** @hide */
public boolean pulseOnNotificationAvailable() {
- return ambientDisplayAvailable();
+ return mContext.getResources().getBoolean(R.bool.config_pulseOnNotificationsAvailable)
+ && ambientDisplayAvailable();
}
/** @hide */
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 69c6ba9a0f43..8bc11cbc61de 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -559,21 +559,18 @@ public final class DisplayManager {
* @see #DISPLAY_CATEGORY_PRESENTATION
*/
public Display[] getDisplays(String category) {
- boolean includeDisabledDisplays = (category != null
- && category.equals(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED));
- final int[] displayIds = mGlobal.getDisplayIds(includeDisabledDisplays);
+ final int[] displayIds = mGlobal.getDisplayIds();
synchronized (mLock) {
try {
- if (category != null && category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
+ if (category == null
+ || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) {
+ addAllDisplaysLocked(mTempDisplays, displayIds);
+ } else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL);
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL);
addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL);
- } else if ((category == null
- || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category))) {
- // All displays requested.
- addAllDisplaysLocked(mTempDisplays, displayIds);
}
return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
} finally {
@@ -1451,5 +1448,15 @@ public final class DisplayManager {
* @hide
*/
String KEY_HIGH_REFRESH_RATE_BLACKLIST = "high_refresh_rate_blacklist";
+
+ /**
+ * Key for the brightness throttling data as a String formatted:
+ * <displayId>,<no of throttling levels>,[<severity as string>,<brightness cap>]
+ * Where the latter part is repeated for each throttling level, and the entirety is repeated
+ * for each display, separated by a semicolon.
+ * For example:
+ * 123,1,critical,0.8;456,2,moderate,0.9,critical,0.7
+ */
+ String KEY_BRIGHTNESS_THROTTLING_DATA = "brightness_throttling_data";
}
}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index da3a5802ee55..74356ddecc76 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -206,16 +206,6 @@ public final class DisplayManagerGlobal {
*/
@UnsupportedAppUsage
public int[] getDisplayIds() {
- return getDisplayIds(/* includeDisabledDisplays= */ false);
- }
-
- /**
- * Gets all valid logical display ids and invalid ones if specified.
- *
- * @return An array containing all display ids.
- */
- @UnsupportedAppUsage
- public int[] getDisplayIds(boolean includeDisabledDisplays) {
try {
synchronized (mLock) {
if (USE_CACHE) {
@@ -224,8 +214,7 @@ public final class DisplayManagerGlobal {
}
}
- int[] displayIds =
- mDm.getDisplayIds(includeDisabledDisplays);
+ int[] displayIds = mDm.getDisplayIds();
if (USE_CACHE) {
mDisplayIdCache = displayIds;
}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index a4115d178f6f..ca3e58094400 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -36,7 +36,7 @@ import android.view.Surface;
interface IDisplayManager {
@UnsupportedAppUsage
DisplayInfo getDisplayInfo(int displayId);
- int[] getDisplayIds(boolean includeDisabled);
+ int[] getDisplayIds();
boolean isUidPresentOnDisplay(int uid, int displayId);
diff --git a/core/java/android/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java
index 3a042a5dee4d..f2525d17e30a 100644
--- a/core/java/android/hardware/radio/ProgramList.java
+++ b/core/java/android/hardware/radio/ProgramList.java
@@ -22,11 +22,11 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.ArrayMap;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -42,7 +42,7 @@ public final class ProgramList implements AutoCloseable {
private final Object mLock = new Object();
private final Map<ProgramSelector.Identifier, RadioManager.ProgramInfo> mPrograms =
- new HashMap<>();
+ new ArrayMap<>();
private final List<ListCallback> mListCallbacks = new ArrayList<>();
private final List<OnCompleteListener> mOnCompleteListeners = new ArrayList<>();
@@ -173,38 +173,69 @@ public final class ProgramList implements AutoCloseable {
}
}
- void apply(@NonNull Chunk chunk) {
+ void apply(Chunk chunk) {
+ List<ProgramSelector.Identifier> removedList = new ArrayList<>();
+ List<ProgramSelector.Identifier> changedList = new ArrayList<>();
+ List<ProgramList.ListCallback> listCallbacksCopied;
+ List<OnCompleteListener> onCompleteListenersCopied = new ArrayList<>();
synchronized (mLock) {
if (mIsClosed) return;
mIsComplete = false;
+ listCallbacksCopied = new ArrayList<>(mListCallbacks);
if (chunk.isPurge()) {
- new HashSet<>(mPrograms.keySet()).stream().forEach(id -> removeLocked(id));
+ Iterator<Map.Entry<ProgramSelector.Identifier, RadioManager.ProgramInfo>>
+ programsIterator = mPrograms.entrySet().iterator();
+ while (programsIterator.hasNext()) {
+ RadioManager.ProgramInfo removed = programsIterator.next().getValue();
+ if (removed != null) {
+ removedList.add(removed.getSelector().getPrimaryId());
+ }
+ programsIterator.remove();
+ }
}
- chunk.getRemoved().stream().forEach(id -> removeLocked(id));
- chunk.getModified().stream().forEach(info -> putLocked(info));
+ chunk.getRemoved().stream().forEach(id -> removeLocked(id, removedList));
+ chunk.getModified().stream().forEach(info -> putLocked(info, changedList));
if (chunk.isComplete()) {
mIsComplete = true;
- mOnCompleteListeners.forEach(cb -> cb.onComplete());
+ onCompleteListenersCopied = new ArrayList<>(mOnCompleteListeners);
+ }
+ }
+
+ for (int i = 0; i < removedList.size(); i++) {
+ for (int cbIndex = 0; cbIndex < listCallbacksCopied.size(); cbIndex++) {
+ listCallbacksCopied.get(cbIndex).onItemRemoved(removedList.get(i));
+ }
+ }
+ for (int i = 0; i < changedList.size(); i++) {
+ for (int cbIndex = 0; cbIndex < listCallbacksCopied.size(); cbIndex++) {
+ listCallbacksCopied.get(cbIndex).onItemChanged(changedList.get(i));
+ }
+ }
+ if (chunk.isComplete()) {
+ for (int cbIndex = 0; cbIndex < onCompleteListenersCopied.size(); cbIndex++) {
+ onCompleteListenersCopied.get(cbIndex).onComplete();
}
}
}
- private void putLocked(@NonNull RadioManager.ProgramInfo value) {
+ private void putLocked(RadioManager.ProgramInfo value,
+ List<ProgramSelector.Identifier> changedIdentifierList) {
ProgramSelector.Identifier key = value.getSelector().getPrimaryId();
mPrograms.put(Objects.requireNonNull(key), value);
ProgramSelector.Identifier sel = value.getSelector().getPrimaryId();
- mListCallbacks.forEach(cb -> cb.onItemChanged(sel));
+ changedIdentifierList.add(sel);
}
- private void removeLocked(@NonNull ProgramSelector.Identifier key) {
+ private void removeLocked(ProgramSelector.Identifier key,
+ List<ProgramSelector.Identifier> removedIdentifierList) {
RadioManager.ProgramInfo removed = mPrograms.remove(Objects.requireNonNull(key));
if (removed == null) return;
ProgramSelector.Identifier sel = removed.getSelector().getPrimaryId();
- mListCallbacks.forEach(cb -> cb.onItemRemoved(sel));
+ removedIdentifierList.add(sel);
}
/**
diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl
index e1ccc4fb740b..170df71385bb 100644
--- a/core/java/android/nfc/INfcTag.aidl
+++ b/core/java/android/nfc/INfcTag.aidl
@@ -46,6 +46,5 @@ interface INfcTag
int getMaxTransceiveLength(int technology);
boolean getExtendedLengthApdusSupported();
- void setTagUpToDate(long cookie);
boolean isTagUpToDate(long cookie);
}
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index 731d1ba78299..500038f14d9d 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -34,7 +34,6 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
-import android.os.SystemClock;
import java.io.IOException;
import java.util.Arrays;
@@ -119,17 +118,17 @@ public final class Tag implements Parcelable {
final String[] mTechStringList;
final Bundle[] mTechExtras;
final int mServiceHandle; // for use by NFC service, 0 indicates a mock
+ final long mCookie; // for accessibility checking
final INfcTag mTagService; // interface to NFC service, will be null if mock tag
int mConnectedTechnology;
- long mCookie;
/**
* Hidden constructor to be used by NFC service and internal classes.
* @hide
*/
public Tag(byte[] id, int[] techList, Bundle[] techListExtras, int serviceHandle,
- INfcTag tagService) {
+ long cookie, INfcTag tagService) {
if (techList == null) {
throw new IllegalArgumentException("rawTargets cannot be null");
}
@@ -139,20 +138,13 @@ public final class Tag implements Parcelable {
// Ensure mTechExtras is as long as mTechList
mTechExtras = Arrays.copyOf(techListExtras, techList.length);
mServiceHandle = serviceHandle;
+ mCookie = cookie;
mTagService = tagService;
-
mConnectedTechnology = -1;
- mCookie = SystemClock.elapsedRealtime();
if (tagService == null) {
return;
}
-
- try {
- tagService.setTagUpToDate(mCookie);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
}
/**
@@ -165,9 +157,10 @@ public final class Tag implements Parcelable {
* @return freshly constructed tag
* @hide
*/
- public static Tag createMockTag(byte[] id, int[] techList, Bundle[] techListExtras) {
+ public static Tag createMockTag(byte[] id, int[] techList, Bundle[] techListExtras,
+ long cookie) {
// set serviceHandle to 0 and tagService to null to indicate mock tag
- return new Tag(id, techList, techListExtras, 0, null);
+ return new Tag(id, techList, techListExtras, 0, cookie, null);
}
private String[] generateTechStringList(int[] techList) {
@@ -445,6 +438,7 @@ public final class Tag implements Parcelable {
dest.writeIntArray(mTechList);
dest.writeTypedArray(mTechExtras, 0);
dest.writeInt(mServiceHandle);
+ dest.writeLong(mCookie);
dest.writeInt(isMock);
if (isMock == 0) {
dest.writeStrongBinder(mTagService.asBinder());
@@ -463,6 +457,7 @@ public final class Tag implements Parcelable {
in.readIntArray(techList);
Bundle[] techExtras = in.createTypedArray(Bundle.CREATOR);
int serviceHandle = in.readInt();
+ long cookie = in.readLong();
int isMock = in.readInt();
if (isMock == 0) {
tagService = INfcTag.Stub.asInterface(in.readStrongBinder());
@@ -471,7 +466,7 @@ public final class Tag implements Parcelable {
tagService = null;
}
- return new Tag(id, techList, techExtras, serviceHandle, tagService);
+ return new Tag(id, techList, techExtras, serviceHandle, cookie, tagService);
}
@Override
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 3cde0319efd3..e5de3e157c88 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -131,4 +131,5 @@ interface IUserManager {
String getUserName();
long getUserStartRealtime();
long getUserUnlockRealtime();
+ boolean setUserEphemeral(int userId, boolean enableEphemeral);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 0ffdfc6cbcb1..d6566048f2c4 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2001,13 +2001,22 @@ public class UserManager {
* @return Whether guest user is always ephemeral
* @hide
*/
- @TestApi
- public static boolean isGuestUserEphemeral() {
+ public static boolean isGuestUserAlwaysEphemeral() {
return Resources.getSystem()
.getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
}
/**
+ * @return true, when we want to enable user manager API and UX to allow
+ * guest user ephemeral state change based on user input
+ * @hide
+ */
+ public static boolean isGuestUserAllowEphemeralStateChange() {
+ return Resources.getSystem()
+ .getBoolean(com.android.internal.R.bool.config_guestUserAllowEphemeralStateChange);
+ }
+
+ /**
* Checks whether the device is running in a headless system user mode.
*
* <p>Headless system user mode means the {@link #isSystemUser() system user} runs system
@@ -3431,6 +3440,20 @@ public class UserManager {
if (guest != null) {
Settings.Secure.putStringForUser(context.getContentResolver(),
Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
+
+ if (UserManager.isGuestUserAllowEphemeralStateChange()) {
+ // Mark guest as (changeably) ephemeral if REMOVE_GUEST_ON_EXIT is 1
+ // This is done so that a user via a UI controller can choose to
+ // make a guest as ephemeral or not.
+ // Settings.Global.REMOVE_GUEST_ON_EXIT holds the choice on what the guest state
+ // should be, with default being ephemeral.
+ boolean resetGuestOnExit = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.REMOVE_GUEST_ON_EXIT, 1) == 1;
+
+ if (resetGuestOnExit && !guest.isEphemeral()) {
+ setUserEphemeral(guest.id, true);
+ }
+ }
}
return guest;
} catch (ServiceSpecificException e) {
@@ -4948,6 +4971,31 @@ public class UserManager {
}
/**
+ * Set the user as ephemeral or non-ephemeral.
+ *
+ * If the user was initially created as ephemeral then this
+ * method has no effect and false is returned.
+ *
+ * @param userId the user's integer id
+ * @param enableEphemeral true: change user state to ephemeral,
+ * false: change user state to non-ephemeral
+ * @return true: user now has the desired ephemeral state,
+ * false: desired user ephemeral state could not be set
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS})
+ public boolean setUserEphemeral(@UserIdInt int userId, boolean enableEphemeral) {
+ try {
+ return mService.setUserEphemeral(userId, enableEphemeral);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Updates the context user's name.
*
* @param name the new name for the user
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 805fdc44b256..7964f7cd6908 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9141,6 +9141,57 @@ public final class Settings {
public static final String SCREENSAVER_ENABLED_COMPLICATIONS =
"screensaver_enabled_complications";
+
+ /**
+ * Default, indicates that the user has not yet started the dock setup flow.
+ *
+ * @hide
+ */
+ public static final int DOCK_SETUP_NOT_STARTED = 0;
+
+ /**
+ * Indicates that the user has started but not yet completed dock setup.
+ * One of the possible states for {@link #DOCK_SETUP_STATE}.
+ *
+ * @hide
+ */
+ public static final int DOCK_SETUP_STARTED = 1;
+
+ /**
+ * Indicates that the user has snoozed dock setup and will complete it later.
+ * One of the possible states for {@link #DOCK_SETUP_STATE}.
+ *
+ * @hide
+ */
+ public static final int DOCK_SETUP_PAUSED = 2;
+
+ /**
+ * Indicates that the user has completed dock setup.
+ * One of the possible states for {@link #DOCK_SETUP_STATE}.
+ *
+ * @hide
+ */
+ public static final int DOCK_SETUP_COMPLETED = 10;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ DOCK_SETUP_NOT_STARTED,
+ DOCK_SETUP_STARTED,
+ DOCK_SETUP_PAUSED,
+ DOCK_SETUP_COMPLETED
+ })
+ public @interface DockSetupState {
+ }
+
+ /**
+ * Defines the user's current state of dock setup.
+ * The possible states are defined in {@link DockSetupState}.
+ *
+ * @hide
+ */
+ public static final String DOCK_SETUP_STATE = "dock_setup_state";
+
/**
* The default NFC payment component
* @hide
@@ -10820,6 +10871,15 @@ public final class Settings {
"launcher_taskbar_education_showing";
/**
+ * Whether or not adaptive charging feature is enabled by user.
+ * Type: int (0 for false, 1 for true)
+ * Default: 1
+ *
+ * @hide
+ */
+ public static final String ADAPTIVE_CHARGING_ENABLED = "adaptive_charging_enabled";
+
+ /**
* These entries are considered common between the personal and the managed profile,
* since the managed profile doesn't get to change them.
*/
@@ -10945,6 +11005,14 @@ public final class Settings {
public static final String ADD_USERS_WHEN_LOCKED = "add_users_when_locked";
/**
+ * Whether guest user should be removed on exit from guest mode.
+ * <p>
+ * Type: int
+ * @hide
+ */
+ public static final String REMOVE_GUEST_ON_EXIT = "remove_guest_on_exit";
+
+ /**
* Whether applying ramping ringer on incoming phone call ringtone.
* <p>1 = apply ramping ringer
* <p>0 = do not apply ramping ringer
diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java
index bf64d06d4ff0..f6a7c8eb8c4b 100644
--- a/core/java/android/service/dreams/DreamActivity.java
+++ b/core/java/android/service/dreams/DreamActivity.java
@@ -21,8 +21,6 @@ import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
-import com.android.internal.R;
-
/**
* The Activity used by the {@link DreamService} to draw screensaver content
* on the screen. This activity runs in dream application's process, but is started by a
@@ -66,17 +64,4 @@ public class DreamActivity extends Activity {
callback.onActivityCreated(this);
}
}
-
- @Override
- public void onResume() {
- super.onResume();
- overridePendingTransition(R.anim.dream_activity_open_enter,
- R.anim.dream_activity_open_exit);
- }
-
- @Override
- public void finishAndRemoveTask() {
- super.finishAndRemoveTask();
- overridePendingTransition(0, R.anim.dream_activity_close_exit);
- }
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 1e22856c1bde..1df7dbc0cd0b 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -24,7 +24,6 @@ import static android.graphics.Matrix.MSKEW_X;
import static android.graphics.Matrix.MSKEW_Y;
import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
-import static android.view.ViewRootImpl.LOCAL_LAYOUT;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import android.animation.AnimationHandler;
@@ -41,7 +40,6 @@ import android.app.Service;
import android.app.WallpaperColors;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
-import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -260,8 +258,6 @@ public abstract class WallpaperService extends Service {
private final Point mLastSurfaceSize = new Point();
private final Matrix mTmpMatrix = new Matrix();
private final float[] mTmpValues = new float[9];
- private final WindowLayout mWindowLayout = new WindowLayout();
- private final Rect mTempRect = new Rect();
final WindowManager.LayoutParams mLayout
= new WindowManager.LayoutParams();
@@ -1100,8 +1096,7 @@ public abstract class WallpaperService extends Service {
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
final Configuration config = mMergedConfiguration.getMergedConfiguration();
- final WindowConfiguration winConfig = config.windowConfiguration;
- final Rect maxBounds = winConfig.getMaxBounds();
+ final Rect maxBounds = config.windowConfiguration.getMaxBounds();
if (myWidth == ViewGroup.LayoutParams.MATCH_PARENT
&& myHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
mLayout.width = myWidth;
@@ -1139,7 +1134,7 @@ public abstract class WallpaperService extends Service {
if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE,
mDisplay.getDisplayId(), mRequestedVisibilities, inputChannel,
- mInsetsState, mTempControls) < 0) {
+ mInsetsState, mTempControls, new Rect()) < 0) {
Log.w(TAG, "Failed to add window while updating wallpaper surface.");
return;
}
@@ -1158,29 +1153,9 @@ public abstract class WallpaperService extends Service {
} else {
mLayout.surfaceInsets.set(0, 0, 0, 0);
}
-
- int relayoutResult = 0;
- if (LOCAL_LAYOUT) {
- if (!mSurfaceControl.isValid()) {
- relayoutResult = mSession.updateVisibility(mWindow, mLayout,
- View.VISIBLE, mMergedConfiguration, mSurfaceControl,
- mInsetsState, mTempControls);
- }
-
- final Rect displayCutoutSafe = mTempRect;
- mInsetsState.getDisplayCutoutSafe(displayCutoutSafe);
- mWindowLayout.computeFrames(mLayout, mInsetsState, displayCutoutSafe,
- winConfig.getBounds(), winConfig.getWindowingMode(), mWidth,
- mHeight, mRequestedVisibilities, null /* attachedWindowFrame */,
- 1f /* compatScale */, mWinFrames);
-
- mSession.updateLayout(mWindow, mLayout, 0 /* flags */, mWinFrames, mWidth,
- mHeight);
- } else {
- relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight,
- View.VISIBLE, 0, mWinFrames, mMergedConfiguration,
- mSurfaceControl, mInsetsState, mTempControls, mSyncSeqIdBundle);
- }
+ final int relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight,
+ View.VISIBLE, 0, mWinFrames, mMergedConfiguration, mSurfaceControl,
+ mInsetsState, mTempControls, mSyncSeqIdBundle);
final int transformHint = SurfaceControl.rotationToBufferTransform(
(mDisplayInstallOrientation + mDisplay.getRotation()) % 4);
@@ -1229,7 +1204,7 @@ public abstract class WallpaperService extends Service {
null /* ignoringVisibilityState */, config.isScreenRound(),
false /* alwaysConsumeSystemBars */, mLayout.softInputMode,
mLayout.flags, SYSTEM_UI_FLAG_VISIBLE, mLayout.type,
- winConfig.getWindowingMode(), null /* typeSideMap */);
+ config.windowConfiguration.getWindowingMode(), null /* typeSideMap */);
if (!fixedSize) {
final Rect padding = mIWallpaperEngine.mDisplayPadding;
@@ -1539,8 +1514,9 @@ public abstract class WallpaperService extends Service {
// may have been destroyed so now we need to make
// sure it is re-created.
doOffsetsChanged(false);
- // force relayout to get new surface
- updateSurface(true, false, false);
+ // It will check mSurfaceCreated so no need to force relayout.
+ updateSurface(false /* forceRelayout */, false /* forceReport */,
+ false /* redrawNeeded */);
}
onVisibilityChanged(visible);
if (mReportedVisible && mFrozenRequested) {
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 24ded932b636..195cf82bc36e 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -83,6 +83,16 @@ public class FeatureFlagUtils {
public static final String SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE =
"settings_hide_second_layer_page_navigate_up_button_in_two_pane";
+ /** Flag to enable/disable guest mode UX changes as mentioned in b/214031645
+ * @hide
+ */
+ public static final String SETTINGS_GUEST_MODE_UX_CHANGES = "settings_guest_mode_ux_changes";
+
+ /** Support Clear Calling feature.
+ * @hide
+ */
+ public static final String SETTINGS_ENABLE_CLEAR_CALLING = "settings_enable_clear_calling";
+
private static final Map<String, String> DEFAULT_FLAGS;
static {
@@ -110,6 +120,8 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "true");
DEFAULT_FLAGS.put(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE, "true");
+ DEFAULT_FLAGS.put(SETTINGS_GUEST_MODE_UX_CHANGES, "true");
+ DEFAULT_FLAGS.put(SETTINGS_ENABLE_CLEAR_CALLING, "false");
}
private static final Set<String> PERSISTENT_FLAGS;
diff --git a/core/java/android/view/IDisplayWindowRotationCallback.aidl b/core/java/android/view/IDisplayChangeWindowCallback.aidl
index 1ffe2dde40f1..00a5b7b831ca 100644
--- a/core/java/android/view/IDisplayWindowRotationCallback.aidl
+++ b/core/java/android/view/IDisplayChangeWindowCallback.aidl
@@ -17,13 +17,15 @@
package android.view;
import android.window.WindowContainerTransaction;
+import android.window.DisplayAreaInfo;
/**
- * Interface to be invoked by the controller when it has finished preparing for a display rotation.
+ * Interface to be invoked by the controller when it has finished preparing for a display
+ * size change.
*
- * @see IDisplayWindowRotationController
+ * @see IDisplayChangeWindowController
* @hide
*/
-interface IDisplayWindowRotationCallback {
- void continueRotateDisplay(int targetRotation, in WindowContainerTransaction t);
+interface IDisplayChangeWindowCallback {
+ void continueDisplayChange(in WindowContainerTransaction t);
}
diff --git a/core/java/android/view/IDisplayWindowRotationController.aidl b/core/java/android/view/IDisplayChangeWindowController.aidl
index c1c7464c3168..8c0bb6a54528 100644
--- a/core/java/android/view/IDisplayWindowRotationController.aidl
+++ b/core/java/android/view/IDisplayChangeWindowController.aidl
@@ -16,11 +16,12 @@
package android.view;
-import android.view.IDisplayWindowRotationCallback;
+import android.view.IDisplayChangeWindowCallback;
+import android.window.DisplayAreaInfo;
/**
- * Singular controller of a "remote" display rotation. When a display rotation is started, WM
- * freezes the screen. It will then call into this controller and wait for a response via the
+ * Singular controller of a "remote" display change. When a display rotation or change is started,
+ * WM freezes the screen. It will then call into this controller and wait for a response via the
* callback.
*
* This needs to provide configuration changes because those changes need to be applied in sync
@@ -36,17 +37,18 @@ import android.view.IDisplayWindowRotationCallback;
*
* @hide
*/
-oneway interface IDisplayWindowRotationController {
+oneway interface IDisplayChangeWindowController {
/**
- * Called when WM needs to know how to update tasks in response to a display rotation.
- * If this isn't called, a timeout will continue the rotation in WM.
+ * Called when WM needs to know how to update tasks in response to a display change.
+ * If this isn't called, a timeout will continue the change in WM.
*
- * @param displayId the display that is rotating.
- * @param fromRotation the rotation the display is rotating from.
- * @param toRotation the rotation the display is rotating to.
+ * @param fromRotation the old rotation
+ * @param newRotation the new rotation
+ * @param newDisplayAreaInfo the new display area info after the change
* @param callback A callback to be called when this has calculated updated configs.
*/
- void onRotateDisplay(int displayId, int fromRotation, int toRotation,
- in IDisplayWindowRotationCallback callback);
+ void onDisplayChange(int displayId, int fromRotation, int toRotation,
+ in DisplayAreaInfo newDisplayAreaInfo, in IDisplayChangeWindowCallback callback);
+
}
diff --git a/core/java/android/view/IDisplayWindowInsetsController.aidl b/core/java/android/view/IDisplayWindowInsetsController.aidl
index f4a0dfaa4432..1940042a1052 100644
--- a/core/java/android/view/IDisplayWindowInsetsController.aidl
+++ b/core/java/android/view/IDisplayWindowInsetsController.aidl
@@ -16,6 +16,7 @@
package android.view;
+import android.content.ComponentName;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.InsetsVisibilities;
@@ -30,10 +31,11 @@ oneway interface IDisplayWindowInsetsController {
/**
* Called when top focused window changes to determine whether or not to take over insets
* control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false.
- * @param packageName: Passes the top package name
+ * @param component: Passes the top application component in the focused window.
* @param requestedVisibilities The insets visibilities requested by the focussed window.
*/
- void topFocusedWindowChanged(String packageName, in InsetsVisibilities insetsVisibilities);
+ void topFocusedWindowChanged(in ComponentName component,
+ in InsetsVisibilities insetsVisibilities);
/**
* @see IWindow#insetsChanged
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 61f524f51786..c4d307073d12 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -80,11 +80,6 @@ interface IRecentsAnimationController {
void setAnimationTargetsBehindSystemBars(boolean behindSystemBars);
/**
- * Hides the current input method if one is showing.
- */
- void hideCurrentInputMethod();
-
- /**
* Clean up the screenshot of previous task which was created during recents animation that
* was cancelled by a stack order change.
*
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index fb562d8e97db..acdff4fb52fd 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -39,7 +39,7 @@ import android.view.ICrossWindowBlurEnabledListener;
import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowListener;
import android.view.IDisplayFoldListener;
-import android.view.IDisplayWindowRotationController;
+import android.view.IDisplayChangeWindowController;
import android.view.IOnKeyguardExitResult;
import android.view.IPinnedTaskListener;
import android.view.IScrollCaptureResponseListener;
@@ -145,7 +145,7 @@ interface IWindowManager
* controller is called after the display has "frozen" for a rotation and display rotation will
* only continue once the controller has finished calculating associated configurations.
*/
- void setDisplayWindowRotationController(IDisplayWindowRotationController controller);
+ void setDisplayChangeWindowController(IDisplayChangeWindowController controller);
/**
* Adds a root container that a client shell can populate with its own windows (usually via
@@ -250,18 +250,6 @@ interface IWindowManager
*/
void refreshScreenCaptureDisabled();
- // These can only be called with the SET_ORIENTATION permission.
- /**
- * Update the current screen rotation based on the current state of
- * the world.
- * @param alwaysSendConfiguration Flag to force a new configuration to
- * be evaluated. This can be used when there are other parameters in
- * configuration that are changing.
- * @param forceRelayout If true, the window manager will always do a relayout
- * of its windows even if the rotation hasn't changed.
- */
- void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout);
-
/**
* Retrieve the current orientation of the primary screen.
* @return Constant as per {@link android.view.Surface.Rotation}.
@@ -442,11 +430,6 @@ interface IWindowManager
boolean isSafeModeEnabled();
/**
- * Enables the screen if all conditions are met.
- */
- void enableScreenIfNeeded();
-
- /**
* Clears the frame statistics for a given window.
*
* @param token The window token.
@@ -560,6 +543,21 @@ interface IWindowManager
boolean isWindowTraceEnabled();
/**
+ * Starts a transition trace.
+ */
+ void startTransitionTrace();
+
+ /**
+ * Stops a transition trace.
+ */
+ void stopTransitionTrace();
+
+ /**
+ * Returns true if transition trace is enabled.
+ */
+ boolean isTransitionTraceEnabled();
+
+ /**
* Gets the windowing mode of the display.
*
* @param displayId The id of the display.
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 649accd1126d..ef57b1ddd4b3 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -50,13 +50,15 @@ interface IWindowSession {
int addToDisplay(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, in InsetsVisibilities requestedVisibilities,
out InputChannel outInputChannel, out InsetsState insetsState,
- out InsetsSourceControl[] activeControls);
+ out InsetsSourceControl[] activeControls, out Rect attachedFrame);
int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, in int userId,
in InsetsVisibilities requestedVisibilities, out InputChannel outInputChannel,
- out InsetsState insetsState, out InsetsSourceControl[] activeControls);
+ out InsetsState insetsState, out InsetsSourceControl[] activeControls,
+ out Rect attachedFrame);
int addToDisplayWithoutInputChannel(IWindow window, in WindowManager.LayoutParams attrs,
- in int viewVisibility, in int layerStackId, out InsetsState insetsState);
+ in int viewVisibility, in int layerStackId, out InsetsState insetsState,
+ out Rect attachedFrame);
@UnsupportedAppUsage
void remove(IWindow window);
@@ -107,41 +109,6 @@ interface IWindowSession {
out InsetsState insetsState, out InsetsSourceControl[] activeControls,
out Bundle bundle);
- /**
- * Changes the view visibility and the attributes of a window. This should only be called when
- * the visibility of the root view is changed. This returns a valid surface if the root view is
- * visible. This also returns the latest information for the caller to compute its window frame.
- *
- * @param window The window being updated.
- * @param attrs If non-null, new attributes to apply to the window.
- * @param viewVisibility Window root view's visibility.
- * @param outMergedConfiguration New config container that holds global, override and merged
- * config for window, if it is now becoming visible and the merged configuration has changed
- * since it was last displayed.
- * @param outSurfaceControl Object in which is placed the new display surface.
- * @param outInsetsState The current insets state in the system.
- * @param outActiveControls The insets source controls for the caller to override the insets
- * state in the system.
- *
- * @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}.
- */
- int updateVisibility(IWindow window, in WindowManager.LayoutParams attrs, int viewVisibility,
- out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
- out InsetsState outInsetsState, out InsetsSourceControl[] outActiveControls);
-
- /**
- * Reports the layout results and the attributes of a window to the server.
- *
- * @param window The window being reported.
- * @param attrs If non-null, new attributes to apply to the window.
- * @param flags Request flags: {@link WindowManagerGlobal#RELAYOUT_INSETS_PENDING}.
- * @param clientFrames the window frames computed by the client.
- * @param requestedWidth The width the window wants to be.
- * @param requestedHeight The height the window wants to be.
- */
- oneway void updateLayout(IWindow window, in WindowManager.LayoutParams attrs, int flags,
- in ClientWindowFrames clientFrames, int requestedWidth, int requestedHeight);
-
/*
* Notify the window manager that an application is relaunching and
* windows should be prepared for replacement.
@@ -384,4 +351,9 @@ interface IWindowSession {
* Clears a touchable region set by {@link #setInsets}.
*/
void clearTouchableRegion(IWindow window);
+
+ /**
+ * Returns whether this window needs to cancel draw and retry later.
+ */
+ boolean cancelDraw(IWindow window);
}
diff --git a/core/java/android/view/InsetsFrameProvider.java b/core/java/android/view/InsetsFrameProvider.java
new file mode 100644
index 000000000000..eb8687c47bed
--- /dev/null
+++ b/core/java/android/view/InsetsFrameProvider.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Insets provided by a window.
+ *
+ * The insets frame will by default as the window frame size. If the providers are set, the
+ * calculation result based on the source size will be used as the insets frame.
+ * @hide
+ */
+public class InsetsFrameProvider implements Parcelable {
+
+ /**
+ * If specified in source field, the insets calculation will be based on the display frame.
+ */
+ public static final int SOURCE_DISPLAY = 0;
+
+ /**
+ * If specified in source field, the insets calculation will be based on the window bounds. The
+ * container bounds can sometimes be different from the window frame. For example, when a task
+ * bar needs the entire screen to be prepared to showing the apps, the window container can take
+ * the entire display, or display area, but the window frame, as a result of the layout, will
+ * stay small until it actually taking the entire display to draw their view.
+ */
+ public static final int SOURCE_CONTAINER_BOUNDS = 1;
+
+ /**
+ * If specified in source field, the insets calculation will be based on the window frame. This
+ * is also the default value of the source.
+ */
+ public static final int SOURCE_FRAME = 2;
+
+ private static final int HAS_INSETS_SIZE = 1;
+ private static final int HAS_INSETS_SIZE_OVERRIDE = 2;
+
+ private static Rect sTmpRect = new Rect();
+
+ /**
+ * The type of insets to provide.
+ */
+ public @InsetsState.InternalInsetsType int type;
+
+ /**
+ * The source of frame. By default, all adjustment will be based on the window frame, it
+ * can be set to window bounds or display bounds instead.
+ */
+ public int source = SOURCE_FRAME;
+
+ /**
+ * The provided insets size based on the source frame. The result will be used as the insets
+ * size to windows other than IME. Only one side should be set.
+ *
+ * For example, when the given source frame is (0, 0) - (100, 200), and the insetsSize is null,
+ * the source frame will be directly used as the final insets frame. If the insetsSize is set to
+ * (0, 0, 0, 50) instead, the insets frame will be a frame starting from the bottom side of the
+ * source frame with height of 50, i.e., (0, 150) - (100, 200).
+ */
+ public Insets insetsSize = null;
+
+ /**
+ * If null, the size set in insetsSize will be applied to all window types. If it contains
+ * element of some types, the insets reported to the window with that types will be overridden.
+ */
+ public InsetsSizeOverride[] insetsSizeOverrides = null;
+
+ public InsetsFrameProvider(int type) {
+ this(type, SOURCE_FRAME, null, null);
+ }
+
+ public InsetsFrameProvider(int type, Insets insetsSize) {
+ this(type, SOURCE_FRAME, insetsSize, null);
+ }
+
+ public InsetsFrameProvider(int type, int source, Insets insetsSize,
+ InsetsSizeOverride[] insetsSizeOverride) {
+ this.type = type;
+ this.source = source;
+ this.insetsSize = insetsSize;
+ this.insetsSizeOverrides = insetsSizeOverride;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(32);
+ sb.append("InsetsFrameProvider: {");
+ sb.append("type=").append(InsetsState.typeToString(type));
+ sb.append(", source=");
+ switch (source) {
+ case SOURCE_DISPLAY:
+ sb.append("SOURCE_DISPLAY");
+ break;
+ case SOURCE_CONTAINER_BOUNDS:
+ sb.append("SOURCE_CONTAINER_BOUNDS Bounds");
+ break;
+ case SOURCE_FRAME:
+ sb.append("SOURCE_FRAME");
+ break;
+ }
+ if (insetsSize != null) {
+ sb.append(", insetsSize=").append(insetsSize);
+ }
+ if (insetsSizeOverrides != null) {
+ sb.append(", insetsSizeOverrides=").append(Arrays.toString(insetsSizeOverrides));
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ public InsetsFrameProvider(Parcel in) {
+ int insetsSizeModified = in.readInt();
+ type = in.readInt();
+ source = in.readInt();
+ if ((insetsSizeModified & HAS_INSETS_SIZE) != 0) {
+ insetsSize = Insets.CREATOR.createFromParcel(in);
+ }
+ if ((insetsSizeModified & HAS_INSETS_SIZE_OVERRIDE) != 0) {
+ insetsSizeOverrides = in.createTypedArray(InsetsSizeOverride.CREATOR);
+ }
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ int insetsSizeModified = 0;
+ if (insetsSize != null) {
+ insetsSizeModified |= HAS_INSETS_SIZE;
+ }
+ if (insetsSizeOverrides != null) {
+ insetsSizeModified |= HAS_INSETS_SIZE_OVERRIDE;
+ }
+ out.writeInt(insetsSizeModified);
+ out.writeInt(type);
+ out.writeInt(source);
+ if (insetsSize != null) {
+ insetsSize.writeToParcel(out, flags);
+ }
+ if (insetsSizeOverrides != null) {
+ out.writeTypedArray(insetsSizeOverrides, flags);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ InsetsFrameProvider other = (InsetsFrameProvider) o;
+ return type == other.type && source == other.source
+ && Objects.equals(insetsSize, other.insetsSize)
+ && Arrays.equals(insetsSizeOverrides, other.insetsSizeOverrides);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, source, insetsSize, Arrays.hashCode(insetsSizeOverrides));
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<InsetsFrameProvider>
+ CREATOR = new Parcelable.Creator<InsetsFrameProvider>() {
+ @Override
+ public InsetsFrameProvider createFromParcel(Parcel in) {
+ return new InsetsFrameProvider(in);
+ }
+
+ @Override
+ public InsetsFrameProvider[] newArray(int size) {
+ return new InsetsFrameProvider[size];
+ }
+ };
+
+ public static void calculateInsetsFrame(Rect displayFrame, Rect containerBounds,
+ Rect displayCutoutSafe, Rect inOutFrame, int source, Insets insetsSize,
+ @WindowManager.LayoutParams.PrivateFlags int privateFlags) {
+ boolean extendByCutout = false;
+ if (source == InsetsFrameProvider.SOURCE_DISPLAY) {
+ inOutFrame.set(displayFrame);
+ } else if (source == InsetsFrameProvider.SOURCE_CONTAINER_BOUNDS) {
+ inOutFrame.set(containerBounds);
+ } else {
+ extendByCutout = (privateFlags & PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT) != 0;
+ }
+ if (insetsSize == null) {
+ return;
+ }
+ // Only one side of the provider shall be applied. Check in the order of left - top -
+ // right - bottom, only the first non-zero value will be applied.
+ if (insetsSize.left != 0) {
+ inOutFrame.right = inOutFrame.left + insetsSize.left;
+ } else if (insetsSize.top != 0) {
+ inOutFrame.bottom = inOutFrame.top + insetsSize.top;
+ } else if (insetsSize.right != 0) {
+ inOutFrame.left = inOutFrame.right - insetsSize.right;
+ } else if (insetsSize.bottom != 0) {
+ inOutFrame.top = inOutFrame.bottom - insetsSize.bottom;
+ } else {
+ inOutFrame.setEmpty();
+ }
+
+ if (extendByCutout) {
+ WindowLayout.extendFrameByCutout(displayCutoutSafe, displayFrame, inOutFrame, sTmpRect);
+ }
+ }
+
+ /**
+ * Class to describe the insets size to be provided to window with specific window type. If not
+ * used, same insets size will be sent as instructed in the insetsSize and source.
+ */
+ public static class InsetsSizeOverride implements Parcelable {
+ public final int windowType;
+ public Insets insetsSize;
+
+ protected InsetsSizeOverride(Parcel in) {
+ windowType = in.readInt();
+ insetsSize = in.readParcelable(null, android.graphics.Insets.class);
+ }
+
+ public InsetsSizeOverride(int type, Insets size) {
+ windowType = type;
+ insetsSize = size;
+ }
+
+ public static final Creator<InsetsSizeOverride> CREATOR =
+ new Creator<InsetsSizeOverride>() {
+ @Override
+ public InsetsSizeOverride createFromParcel(Parcel in) {
+ return new InsetsSizeOverride(in);
+ }
+
+ @Override
+ public InsetsSizeOverride[] newArray(int size) {
+ return new InsetsSizeOverride[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(windowType);
+ out.writeParcelable(insetsSize, flags);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(32);
+ sb.append("TypedInsetsSize: {");
+ sb.append("windowType=").append(windowType);
+ sb.append(", insetsSize=").append(insetsSize);
+ sb.append("}");
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(windowType, insetsSize);
+ }
+ }
+}
+
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index f6e4f6e7ef2b..c198098cb6ff 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -90,8 +90,10 @@ public class InsetsState implements Parcelable {
ITYPE_IME,
ITYPE_CLIMATE_BAR,
ITYPE_EXTRA_NAVIGATION_BAR,
- ITYPE_LOCAL_NAVIGATION_BAR_1,
- ITYPE_LOCAL_NAVIGATION_BAR_2
+ ITYPE_LEFT_GENERIC_OVERLAY,
+ ITYPE_TOP_GENERIC_OVERLAY,
+ ITYPE_RIGHT_GENERIC_OVERLAY,
+ ITYPE_BOTTOM_GENERIC_OVERLAY
})
public @interface InternalInsetsType {}
@@ -135,10 +137,12 @@ public class InsetsState implements Parcelable {
public static final int ITYPE_EXTRA_NAVIGATION_BAR = 21;
/** Additional types for local insets. **/
- public static final int ITYPE_LOCAL_NAVIGATION_BAR_1 = 22;
- public static final int ITYPE_LOCAL_NAVIGATION_BAR_2 = 23;
+ public static final int ITYPE_LEFT_GENERIC_OVERLAY = 22;
+ public static final int ITYPE_TOP_GENERIC_OVERLAY = 23;
+ public static final int ITYPE_RIGHT_GENERIC_OVERLAY = 24;
+ public static final int ITYPE_BOTTOM_GENERIC_OVERLAY = 25;
- static final int LAST_TYPE = ITYPE_LOCAL_NAVIGATION_BAR_2;
+ static final int LAST_TYPE = ITYPE_BOTTOM_GENERIC_OVERLAY;
public static final int SIZE = LAST_TYPE + 1;
// Derived types
@@ -404,27 +408,20 @@ public class InsetsState implements Parcelable {
if (source == null) {
continue;
}
- if (!canControlSide(frame, getInsetSide(
- source.calculateInsets(frame, true /* ignoreVisibility */)))) {
+ if (!canControlSource(frame, source)) {
blocked |= toPublicType(type);
}
}
return blocked;
}
- private boolean canControlSide(Rect frame, int side) {
- switch (side) {
- case ISIDE_LEFT:
- case ISIDE_RIGHT:
- return frame.left == mDisplayFrame.left && frame.right == mDisplayFrame.right;
- case ISIDE_TOP:
- case ISIDE_BOTTOM:
- return frame.top == mDisplayFrame.top && frame.bottom == mDisplayFrame.bottom;
- case ISIDE_FLOATING:
- return true;
- default:
- return false;
- }
+ private static boolean canControlSource(Rect frame, InsetsSource source) {
+ final Insets insets = source.calculateInsets(frame, true /* ignoreVisibility */);
+ final Rect sourceFrame = source.getFrame();
+ final int sourceWidth = sourceFrame.width();
+ final int sourceHeight = sourceFrame.height();
+ return insets.left == sourceWidth || insets.right == sourceWidth
+ || insets.top == sourceHeight || insets.bottom == sourceHeight;
}
private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
@@ -705,8 +702,12 @@ public class InsetsState implements Parcelable {
if ((types & Type.NAVIGATION_BARS) != 0) {
result.add(ITYPE_NAVIGATION_BAR);
result.add(ITYPE_EXTRA_NAVIGATION_BAR);
- result.add(ITYPE_LOCAL_NAVIGATION_BAR_1);
- result.add(ITYPE_LOCAL_NAVIGATION_BAR_2);
+ }
+ if ((types & Type.GENERIC_OVERLAYS) != 0) {
+ result.add(ITYPE_LEFT_GENERIC_OVERLAY);
+ result.add(ITYPE_TOP_GENERIC_OVERLAY);
+ result.add(ITYPE_RIGHT_GENERIC_OVERLAY);
+ result.add(ITYPE_BOTTOM_GENERIC_OVERLAY);
}
if ((types & Type.CAPTION_BAR) != 0) {
result.add(ITYPE_CAPTION_BAR);
@@ -747,9 +748,12 @@ public class InsetsState implements Parcelable {
return Type.STATUS_BARS;
case ITYPE_NAVIGATION_BAR:
case ITYPE_EXTRA_NAVIGATION_BAR:
- case ITYPE_LOCAL_NAVIGATION_BAR_1:
- case ITYPE_LOCAL_NAVIGATION_BAR_2:
return Type.NAVIGATION_BARS;
+ case ITYPE_LEFT_GENERIC_OVERLAY:
+ case ITYPE_TOP_GENERIC_OVERLAY:
+ case ITYPE_RIGHT_GENERIC_OVERLAY:
+ case ITYPE_BOTTOM_GENERIC_OVERLAY:
+ return Type.GENERIC_OVERLAYS;
case ITYPE_CAPTION_BAR:
return Type.CAPTION_BAR;
case ITYPE_IME:
@@ -868,10 +872,14 @@ public class InsetsState implements Parcelable {
return "ITYPE_CLIMATE_BAR";
case ITYPE_EXTRA_NAVIGATION_BAR:
return "ITYPE_EXTRA_NAVIGATION_BAR";
- case ITYPE_LOCAL_NAVIGATION_BAR_1:
- return "ITYPE_LOCAL_NAVIGATION_BAR_1";
- case ITYPE_LOCAL_NAVIGATION_BAR_2:
- return "ITYPE_LOCAL_NAVIGATION_BAR_2";
+ case ITYPE_LEFT_GENERIC_OVERLAY:
+ return "ITYPE_LEFT_GENERIC_OVERLAY";
+ case ITYPE_TOP_GENERIC_OVERLAY:
+ return "ITYPE_TOP_GENERIC_OVERLAY";
+ case ITYPE_RIGHT_GENERIC_OVERLAY:
+ return "ITYPE_RIGHT_GENERIC_OVERLAY";
+ case ITYPE_BOTTOM_GENERIC_OVERLAY:
+ return "ITYPE_BOTTOM_GENERIC_OVERLAY";
default:
return "ITYPE_UNKNOWN_" + type;
}
@@ -916,6 +924,11 @@ public class InsetsState implements Parcelable {
if (source == null && otherSource == null) {
continue;
}
+ if (excludeInvisibleImeFrames && i == ITYPE_IME
+ && ((source == null && !otherSource.isVisible())
+ || (otherSource == null && !source.isVisible()))) {
+ continue;
+ }
if (source == null || otherSource == null) {
return false;
}
diff --git a/core/java/android/view/RemoteAccessibilityController.java b/core/java/android/view/RemoteAccessibilityController.java
index 28b567d94e6e..b0911d789fe1 100644
--- a/core/java/android/view/RemoteAccessibilityController.java
+++ b/core/java/android/view/RemoteAccessibilityController.java
@@ -24,6 +24,8 @@ import android.os.RemoteException;
import android.util.Log;
import android.view.accessibility.IAccessibilityEmbeddedConnection;
+import java.lang.ref.WeakReference;
+
class RemoteAccessibilityController {
private static final String TAG = "RemoteAccessibilityController";
private int mHostId;
@@ -80,12 +82,17 @@ class RemoteAccessibilityController {
/**
* Wrapper of accessibility embedded connection for embedded view hierarchy.
*/
- private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient {
+ private static final class RemoteAccessibilityEmbeddedConnection
+ implements IBinder.DeathRecipient {
+ private final WeakReference<RemoteAccessibilityController> mController;
private final IAccessibilityEmbeddedConnection mConnection;
private final IBinder mLeashToken;
- RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection,
+ RemoteAccessibilityEmbeddedConnection(
+ RemoteAccessibilityController controller,
+ IAccessibilityEmbeddedConnection connection,
IBinder leashToken) {
+ mController = new WeakReference<>(controller);
mConnection = connection;
mLeashToken = leashToken;
}
@@ -109,9 +116,13 @@ class RemoteAccessibilityController {
@Override
public void binderDied() {
unlinkToDeath();
- runOnUiThread(() -> {
- if (mConnectionWrapper == this) {
- mConnectionWrapper = null;
+ RemoteAccessibilityController controller = mController.get();
+ if (controller == null) {
+ return;
+ }
+ controller.runOnUiThread(() -> {
+ if (controller.mConnectionWrapper == this) {
+ controller.mConnectionWrapper = null;
}
});
}
@@ -128,7 +139,7 @@ class RemoteAccessibilityController {
}
if (connection != null && leashToken != null) {
mConnectionWrapper =
- new RemoteAccessibilityEmbeddedConnection(connection, leashToken);
+ new RemoteAccessibilityEmbeddedConnection(this, connection, leashToken);
mConnectionWrapper.linkToDeath();
}
} catch (RemoteException e) {
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index e98d046e8c6c..a468951c5020 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -223,6 +223,12 @@ public class RemoteAnimationTarget implements Parcelable {
public boolean hasAnimatingParent;
/**
+ * Whether an activity has enabled {@link android.R.styleable#Animation_showBackdrop} for
+ * transition.
+ */
+ public boolean showBackdrop;
+
+ /**
* The background color of animation in case the task info is not available if the transition
* is activity level.
*/
@@ -287,6 +293,11 @@ public class RemoteAnimationTarget implements Parcelable {
windowType = in.readInt();
hasAnimatingParent = in.readBoolean();
backgroundColor = in.readInt();
+ showBackdrop = in.readBoolean();
+ }
+
+ public void setShowBackdrop(boolean shouldShowBackdrop) {
+ showBackdrop = shouldShowBackdrop;
}
@Override
@@ -316,6 +327,7 @@ public class RemoteAnimationTarget implements Parcelable {
dest.writeInt(windowType);
dest.writeBoolean(hasAnimatingParent);
dest.writeInt(backgroundColor);
+ dest.writeBoolean(showBackdrop);
}
public void dump(PrintWriter pw, String prefix) {
@@ -337,6 +349,7 @@ public class RemoteAnimationTarget implements Parcelable {
pw.print(prefix); pw.print("windowType="); pw.print(windowType);
pw.print(prefix); pw.print("hasAnimatingParent="); pw.print(hasAnimatingParent);
pw.print(prefix); pw.print("backgroundColor="); pw.print(backgroundColor);
+ pw.print(prefix); pw.print("showBackdrop="); pw.print(showBackdrop);
}
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/core/java/android/view/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java
index d70de74ee75f..c50f70a08063 100644
--- a/core/java/android/view/ScrollCaptureConnection.java
+++ b/core/java/android/view/ScrollCaptureConnection.java
@@ -214,7 +214,7 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub imple
@BinderThread
@Override
- public void close() {
+ public synchronized void close() {
Trace.instantForTrack(TRACE_TAG_GRAPHICS, TRACE_TRACK, "close");
if (mActive) {
Log.w(TAG, "close(): capture session still active! Ending now.");
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index a1ce39e974e3..92bdfdd4c558 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -121,6 +121,7 @@ public final class SurfaceControl implements Parcelable {
private static native void nativeSetAnimationTransaction(long transactionObj);
private static native void nativeSetEarlyWakeupStart(long transactionObj);
private static native void nativeSetEarlyWakeupEnd(long transactionObj);
+ private static native long nativeGetTransactionId(long transactionObj);
private static native void nativeSetLayer(long transactionObj, long nativeObject, int zorder);
private static native void nativeSetRelativeLayer(long transactionObj, long nativeObject,
@@ -3537,6 +3538,15 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * @hide
+ * @return The transaction's current id.
+ * The id changed every time the transaction is applied.
+ */
+ public long getId() {
+ return nativeGetTransactionId(mNativeObject);
+ }
+
+ /**
* Sets an arbitrary piece of metadata on the surface. This is a helper for int data.
* @hide
*/
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index f7bca5bfe188..d75ff2fc7dc2 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -29,8 +29,6 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
import android.window.WindowTokenClient;
-import android.view.InsetsState;
-import android.view.WindowManagerGlobal;
import android.view.accessibility.IAccessibilityEmbeddedConnection;
import java.util.Objects;
@@ -280,7 +278,7 @@ public class SurfaceControlViewHost {
public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d,
@NonNull WindowlessWindowManager wwm, boolean useSfChoreographer) {
mWm = wwm;
- mViewRoot = new ViewRootImpl(c, d, mWm, useSfChoreographer);
+ mViewRoot = new ViewRootImpl(c, d, mWm, new WindowlessWindowLayout(), useSfChoreographer);
addConfigCallback(c, d);
WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
@@ -310,7 +308,7 @@ public class SurfaceControlViewHost {
mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
mSurfaceControl, hostToken);
- mViewRoot = new ViewRootImpl(context, display, mWm);
+ mViewRoot = new ViewRootImpl(context, display, mWm, new WindowlessWindowLayout());
addConfigCallback(context, display);
WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 785735c2b1e1..c97eb73c120b 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -25,11 +25,11 @@ import android.graphics.BLASTBufferQueue;
import android.graphics.FrameInfo;
import android.graphics.HardwareRenderer;
import android.graphics.Picture;
-import android.graphics.Point;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
import android.os.Trace;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Surface.OutOfResourcesException;
import android.view.View.AttachInfo;
@@ -596,11 +596,18 @@ public final class ThreadedRenderer extends HardwareRenderer {
*/
void setLightCenter(AttachInfo attachInfo) {
// Adjust light position for window offsets.
- final Point displaySize = attachInfo.mPoint;
- attachInfo.mDisplay.getRealSize(displaySize);
- final float lightX = displaySize.x / 2f - attachInfo.mWindowLeft;
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ attachInfo.mDisplay.getRealMetrics(displayMetrics);
+ final float lightX = displayMetrics.widthPixels / 2f - attachInfo.mWindowLeft;
final float lightY = mLightY - attachInfo.mWindowTop;
- setLightSourceGeometry(lightX, lightY, mLightZ, mLightRadius);
+ // To prevent shadow distortion on larger screens, scale the z position of the light source
+ // relative to the smallest screen dimension.
+ final float zRatio = Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels)
+ / (450f * displayMetrics.density);
+ final float zWeightedAdjustment = (zRatio + 2) / 3f;
+ final float lightZ = mLightZ * zWeightedAdjustment;
+
+ setLightSourceGeometry(lightX, lightY, lightZ, mLightRadius);
}
/**
@@ -849,12 +856,18 @@ public final class ThreadedRenderer extends HardwareRenderer {
public void setLightCenter(final Display display,
final int windowLeft, final int windowTop) {
// Adjust light position for window offsets.
- final Point displaySize = new Point();
- display.getRealSize(displaySize);
- final float lightX = displaySize.x / 2f - windowLeft;
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ display.getRealMetrics(displayMetrics);
+ final float lightX = displayMetrics.widthPixels / 2f - windowLeft;
final float lightY = mLightY - windowTop;
-
- setLightSourceGeometry(lightX, lightY, mLightZ, mLightRadius);
+ // To prevent shadow distortion on larger screens, scale the z position of the light
+ // source relative to the smallest screen dimension.
+ final float zRatio = Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels)
+ / (450f * displayMetrics.density);
+ final float zWeightedAdjustment = (zRatio + 2) / 3f;
+ final float lightZ = mLightZ * zWeightedAdjustment;
+
+ setLightSourceGeometry(lightX, lightY, lightZ, mLightRadius);
}
public RenderNode getRootNode() {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a13872eef6b8..7be8dffdf023 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -60,13 +60,11 @@ import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowLayout.UNSPECIFIED_LENGTH;
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_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CONTROLLED;
@@ -77,13 +75,12 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
+import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
@@ -564,9 +561,7 @@ public final class ViewRootImpl implements ViewParent,
private final Rect mVisRect = new Rect(); // used to retrieve visible rect of focused view.
private final Rect mTempRect = new Rect();
- private final WindowLayout mWindowLayout = new WindowLayout();
-
- private ViewRootImpl mParentViewRoot;
+ private final WindowLayout mWindowLayout;
// This is used to reduce the race between window focus changes being dispatched from
// the window manager and input events coming through the input system.
@@ -601,9 +596,20 @@ public final class ViewRootImpl implements ViewParent,
*/
private boolean mSyncBuffer = false;
+ /**
+ * Flag to determine whether the client needs to check with WMS if it can draw. WMS will notify
+ * the client that it can't draw if we're still in the middle of a sync set that includes this
+ * window. Once the sync is complete, the window can resume drawing. This is to ensure we don't
+ * deadlock the client by trying to request draws when there may not be any buffers available.
+ */
+ private boolean mCheckIfCanDraw = false;
+
+ private boolean mDrewOnceForSync = false;
+
int mSyncSeqId = 0;
int mLastSyncSeqId = 0;
+ private boolean mUpdateSurfaceNeeded;
boolean mFullRedrawNeeded;
boolean mNewSurfaceNeeded;
boolean mForceNextWindowRelayout;
@@ -881,18 +887,20 @@ public final class ViewRootImpl implements ViewParent,
private String mTag = TAG;
public ViewRootImpl(Context context, Display display) {
- this(context, display, WindowManagerGlobal.getWindowSession(),
+ this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout(),
false /* useSfChoreographer */);
}
- public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session) {
- this(context, display, session, false /* useSfChoreographer */);
+ public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
+ WindowLayout windowLayout) {
+ this(context, display, session, windowLayout, false /* useSfChoreographer */);
}
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
- boolean useSfChoreographer) {
+ WindowLayout windowLayout, boolean useSfChoreographer) {
mContext = context;
mWindowSession = session;
+ mWindowLayout = windowLayout;
mDisplay = display;
mBasePackageName = context.getBasePackageName();
mThread = Thread.currentThread();
@@ -1184,7 +1192,6 @@ public final class ViewRootImpl implements ViewParent,
if (panelParentView != null) {
mAttachInfo.mPanelParentWindowToken
= panelParentView.getApplicationWindowToken();
- mParentViewRoot = panelParentView.getViewRootImpl();
}
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */
@@ -1215,14 +1222,21 @@ public final class ViewRootImpl implements ViewParent,
collectViewAttributes();
adjustLayoutParamsForCompatibility(mWindowAttributes);
controlInsetsForCompatibility(mWindowAttributes);
+
+ Rect attachedFrame = new Rect();
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
- mTempControls);
+ mTempControls, attachedFrame);
+ if (!attachedFrame.isValid()) {
+ attachedFrame = null;
+ }
if (mTranslator != null) {
mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
+ mTranslator.translateRectInScreenToAppWindow(attachedFrame);
}
+ mTmpFrames.attachedFrame = attachedFrame;
} catch (RemoteException e) {
mAdded = false;
mView = null;
@@ -1249,8 +1263,8 @@ public final class ViewRootImpl implements ViewParent,
mWindowLayout.computeFrames(mWindowAttributes, state,
displayCutoutSafe, winConfig.getBounds(), winConfig.getWindowingMode(),
UNSPECIFIED_LENGTH, UNSPECIFIED_LENGTH,
- mInsetsController.getRequestedVisibilities(),
- getAttachedWindowFrame(), 1f /* compactScale */, mTmpFrames);
+ mInsetsController.getRequestedVisibilities(), 1f /* compactScale */,
+ mTmpFrames);
setFrame(mTmpFrames.frame);
registerBackCallbackOnWindow();
if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
@@ -1372,14 +1386,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
- private Rect getAttachedWindowFrame() {
- final int type = mWindowAttributes.type;
- final boolean layoutAttached = (mParentViewRoot != null
- && type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW
- && type != TYPE_APPLICATION_ATTACHED_DIALOG);
- return layoutAttached ? mParentViewRoot.mWinFrame : null;
- }
-
/**
* Register any kind of listeners if setView was success.
*/
@@ -1737,16 +1743,20 @@ public final class ViewRootImpl implements ViewParent,
final Rect frame = frames.frame;
final Rect displayFrame = frames.displayFrame;
+ final Rect attachedFrame = frames.attachedFrame;
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWindow(frame);
mTranslator.translateRectInScreenToAppWindow(displayFrame);
+ mTranslator.translateRectInScreenToAppWindow(attachedFrame);
}
final boolean frameChanged = !mWinFrame.equals(frame);
final boolean configChanged = !mLastReportedMergedConfiguration.equals(mergedConfiguration);
+ final boolean attachedFrameChanged = LOCAL_LAYOUT
+ && !Objects.equals(mTmpFrames.attachedFrame, attachedFrame);
final boolean displayChanged = mDisplay.getDisplayId() != displayId;
final boolean resizeModeChanged = mResizeMode != resizeMode;
- if (msg == MSG_RESIZED && !frameChanged && !configChanged && !displayChanged
- && !resizeModeChanged && !forceNextWindowRelayout) {
+ if (msg == MSG_RESIZED && !frameChanged && !configChanged && !attachedFrameChanged
+ && !displayChanged && !resizeModeChanged && !forceNextWindowRelayout) {
return;
}
@@ -1764,6 +1774,9 @@ public final class ViewRootImpl implements ViewParent,
setFrame(frame);
mTmpFrames.displayFrame.set(displayFrame);
+ if (mTmpFrames.attachedFrame != null && attachedFrame != null) {
+ mTmpFrames.attachedFrame.set(attachedFrame);
+ }
if (mDragResizing && mUseMTRenderer) {
boolean fullscreen = frame.equals(mPendingBackDropFrame);
@@ -1773,7 +1786,7 @@ public final class ViewRootImpl implements ViewParent,
}
}
- mForceNextWindowRelayout = forceNextWindowRelayout;
+ mForceNextWindowRelayout |= forceNextWindowRelayout;
mPendingAlwaysConsumeSystemBars = args.argi2 != 0;
mSyncSeqId = args.argi4 > mSyncSeqId ? args.argi4 : mSyncSeqId;
@@ -2697,6 +2710,9 @@ public final class ViewRootImpl implements ViewParent,
mIsInTraversal = true;
mWillDrawSoon = true;
+ boolean cancelDraw = false;
+ boolean isSyncRequest = false;
+
boolean windowSizeMayChange = false;
WindowManager.LayoutParams lp = mWindowAttributes;
@@ -2785,6 +2801,7 @@ public final class ViewRootImpl implements ViewParent,
if (viewVisibilityChanged) {
mAttachInfo.mWindowVisibility = viewVisibility;
host.dispatchWindowVisibilityChanged(viewVisibility);
+ mAttachInfo.mTreeObserver.dispatchOnWindowVisibilityChange(viewVisibility);
if (viewUserVisibilityChanged) {
host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE);
}
@@ -2911,7 +2928,6 @@ public final class ViewRootImpl implements ViewParent,
final int surfaceGenerationId = mSurface.getGenerationId();
final boolean isViewVisible = viewVisibility == View.VISIBLE;
- final boolean windowRelayoutWasForced = mForceNextWindowRelayout;
boolean surfaceSizeChanged = false;
boolean surfaceCreated = false;
boolean surfaceDestroyed = false;
@@ -2975,6 +2991,8 @@ public final class ViewRootImpl implements ViewParent,
mViewFrameInfo.flags |= FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED;
}
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
+ cancelDraw = (relayoutResult & RELAYOUT_RES_CANCEL_AND_REDRAW)
+ == RELAYOUT_RES_CANCEL_AND_REDRAW;
final boolean dragResizing = mPendingDragResizing;
if (mSyncSeqId > mLastSyncSeqId) {
mLastSyncSeqId = mSyncSeqId;
@@ -2983,6 +3001,10 @@ public final class ViewRootImpl implements ViewParent,
}
reportNextDraw();
mSyncBuffer = true;
+ isSyncRequest = true;
+ if (!cancelDraw) {
+ mDrewOnceForSync = false;
+ }
}
final boolean surfaceControlChanged =
@@ -3015,6 +3037,8 @@ public final class ViewRootImpl implements ViewParent,
!mFirst, INVALID_DISPLAY /* same display */);
updatedConfiguration = true;
}
+ final boolean updateSurfaceNeeded = mUpdateSurfaceNeeded;
+ mUpdateSurfaceNeeded = false;
surfaceSizeChanged = false;
if (!mLastSurfaceSize.equals(mSurfaceSize)) {
@@ -3093,8 +3117,7 @@ public final class ViewRootImpl implements ViewParent,
if (isHardwareEnabled()) {
mAttachInfo.mThreadedRenderer.destroy();
}
- } else if ((surfaceReplaced
- || surfaceSizeChanged || windowRelayoutWasForced)
+ } else if ((surfaceReplaced || surfaceSizeChanged || updateSurfaceNeeded)
&& mSurfaceHolder == null
&& mAttachInfo.mThreadedRenderer != null
&& mSurface.isValid()) {
@@ -3270,6 +3293,19 @@ public final class ViewRootImpl implements ViewParent,
}
}
} else {
+ // If a relayout isn't going to happen, we still need to check if this window can draw
+ // when mCheckIfCanDraw is set. This is because it means we had a sync in the past, but
+ // have not been told by WMS that the sync is complete and that we can continue to draw
+ if (mCheckIfCanDraw) {
+ try {
+ cancelDraw = mWindowSession.cancelDraw(mWindow);
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "cancelDraw returned " + cancelDraw);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
// Not the first pass and no window/insets/visibility change but the window
// may have moved and we need check that and if so to update the left and right
// in the attach info. We translate only the window frame since on window move
@@ -3488,9 +3524,13 @@ public final class ViewRootImpl implements ViewParent,
reportNextDraw();
}
- boolean cancelAndRedraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw();
+ mCheckIfCanDraw = isSyncRequest || cancelDraw;
+
+ boolean cancelAndRedraw =
+ mAttachInfo.mTreeObserver.dispatchOnPreDraw() || (cancelDraw && mDrewOnceForSync);
if (!cancelAndRedraw) {
createSyncIfNeeded();
+ mDrewOnceForSync = true;
}
if (!isViewVisible) {
@@ -3719,6 +3759,7 @@ public final class ViewRootImpl implements ViewParent,
}
}
mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
+ mOnBackInvokedDispatcher.onWindowFocusChanged(hasWindowFocus);
// NOTE: there's no view visibility (appeared / disapparead) events when the windows focus
// is lost, so we don't need to to force a flush - there might be other events such as
@@ -5226,6 +5267,21 @@ public final class ViewRootImpl implements ViewParent,
throw new IllegalArgumentException("No merged config provided.");
}
+ final int lastRotation = mLastReportedMergedConfiguration.getMergedConfiguration()
+ .windowConfiguration.getRotation();
+ final int newRotation = mergedConfiguration.getMergedConfiguration()
+ .windowConfiguration.getRotation();
+ if (lastRotation != newRotation) {
+ // Trigger ThreadedRenderer#updateSurface() if the surface control doesn't change.
+ // Because even if the actual surface size is not changed, e.g. flip 180 degrees,
+ // the buffers may still have content in previous rotation. And the next draw may
+ // not update all regions, that causes some afterimages to flicker.
+ mUpdateSurfaceNeeded = true;
+ if (!mIsInTraversal) {
+ mForceNextWindowRelayout = true;
+ }
+ }
+
Configuration globalConfig = mergedConfiguration.getGlobalConfiguration();
final Configuration overrideConfig = mergedConfiguration.getOverrideConfiguration();
if (DEBUG_CONFIGURATION) Log.v(mTag,
@@ -7998,69 +8054,20 @@ public final class ViewRootImpl implements ViewParent,
final int requestedWidth = (int) (mView.getMeasuredWidth() * appScale + 0.5f);
final int requestedHeight = (int) (mView.getMeasuredHeight() * appScale + 0.5f);
- int relayoutResult = 0;
- WindowConfiguration winConfig = getConfiguration().windowConfiguration;
- if (LOCAL_LAYOUT) {
- if (mFirst || viewVisibility != mViewVisibility) {
- relayoutResult = mWindowSession.updateVisibility(mWindow, params, viewVisibility,
- mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mTempControls);
- if (mTranslator != null) {
- mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
- mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
- }
- mInsetsController.onStateChanged(mTempInsets);
- mInsetsController.onControlsChanged(mTempControls);
-
- mPendingAlwaysConsumeSystemBars =
- (relayoutResult & RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;
- }
- final InsetsState state = mInsetsController.getState();
- final Rect displayCutoutSafe = mTempRect;
- state.getDisplayCutoutSafe(displayCutoutSafe);
- if (mWindowAttributes.type == TYPE_APPLICATION_STARTING) {
- // TODO(b/210378379): Remove the special logic.
- // Letting starting window use the window bounds from the pending config is for the
- // fixed rotation, because the config is not overridden before the starting window
- // is created.
- winConfig = mPendingMergedConfiguration.getMergedConfiguration()
- .windowConfiguration;
- }
- mWindowLayout.computeFrames(mWindowAttributes, state, displayCutoutSafe,
- winConfig.getBounds(), winConfig.getWindowingMode(), requestedWidth,
- requestedHeight, mInsetsController.getRequestedVisibilities(),
- getAttachedWindowFrame(), 1f /* compatScale */, mTmpFrames);
-
- mWindowSession.updateLayout(mWindow, params,
- insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mTmpFrames,
- requestedWidth, requestedHeight);
-
- } else {
- relayoutResult = mWindowSession.relayout(mWindow, params,
- requestedWidth, requestedHeight, viewVisibility,
- insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
- mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
- mTempControls, mRelayoutBundle);
- final int maybeSyncSeqId = mRelayoutBundle.getInt("seqid");
- if (maybeSyncSeqId > 0) {
- mSyncSeqId = maybeSyncSeqId;
- }
-
- if (mTranslator != null) {
- mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
- mTranslator.translateRectInScreenToAppWindow(mTmpFrames.displayFrame);
- mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
- mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
- }
- mInsetsController.onStateChanged(mTempInsets);
- mInsetsController.onControlsChanged(mTempControls);
-
- mPendingAlwaysConsumeSystemBars =
- (relayoutResult & RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;
+ int relayoutResult = mWindowSession.relayout(mWindow, params,
+ requestedWidth, requestedHeight, viewVisibility,
+ insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
+ mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
+ mTempControls, mRelayoutBundle);
+ final int maybeSyncSeqId = mRelayoutBundle.getInt("seqid");
+ if (maybeSyncSeqId > 0) {
+ mSyncSeqId = maybeSyncSeqId;
}
final int transformHint = SurfaceControl.rotationToBufferTransform(
(mDisplayInstallOrientation + mDisplay.getRotation()) % 4);
+ final WindowConfiguration winConfig = getConfiguration().windowConfiguration;
WindowLayout.computeSurfaceSize(mWindowAttributes, winConfig.getMaxBounds(), requestedWidth,
requestedHeight, mTmpFrames.frame, mPendingDragResizing, mSurfaceSize);
@@ -8109,10 +8116,23 @@ public final class ViewRootImpl implements ViewParent,
destroySurface();
}
+ mPendingAlwaysConsumeSystemBars =
+ (relayoutResult & RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;
+
if (restore) {
params.restore();
}
+
+ if (mTranslator != null) {
+ mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
+ mTranslator.translateRectInScreenToAppWindow(mTmpFrames.displayFrame);
+ mTranslator.translateRectInScreenToAppWindow(mTmpFrames.attachedFrame);
+ mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
+ mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
+ }
setFrame(mTmpFrames.frame);
+ mInsetsController.onStateChanged(mTempInsets);
+ mInsetsController.onControlsChanged(mTempControls);
return relayoutResult;
}
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 5a99ab2f5bdb..ed8350afc109 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -43,6 +43,7 @@ public final class ViewTreeObserver {
// Recursive listeners use CopyOnWriteArrayList
private CopyOnWriteArrayList<OnWindowFocusChangeListener> mOnWindowFocusListeners;
private CopyOnWriteArrayList<OnWindowAttachListener> mOnWindowAttachListeners;
+ private CopyOnWriteArrayList<OnWindowVisibilityChangeListener> mOnWindowVisibilityListeners;
private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
@UnsupportedAppUsage
private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
@@ -106,6 +107,21 @@ public final class ViewTreeObserver {
}
/**
+ * Interface definition for a callback to be invoked when the view hierarchy's window
+ * visibility changes.
+ *
+ * @hide
+ */
+ public interface OnWindowVisibilityChangeListener {
+ /**
+ * Callback method to be invoked when the window visibility changes in the view tree.
+ *
+ * @param visibility The new visibility of the window.
+ */
+ void onWindowVisibilityChanged(@View.Visibility int visibility);
+ }
+
+ /**
* Interface definition for a callback to be invoked when the focus state within
* the view tree changes.
*/
@@ -386,6 +402,14 @@ public final class ViewTreeObserver {
}
}
+ if (observer.mOnWindowVisibilityListeners != null) {
+ if (mOnWindowVisibilityListeners != null) {
+ mOnWindowVisibilityListeners.addAll(observer.mOnWindowVisibilityListeners);
+ } else {
+ mOnWindowVisibilityListeners = observer.mOnWindowVisibilityListeners;
+ }
+ }
+
if (observer.mOnGlobalFocusListeners != null) {
if (mOnGlobalFocusListeners != null) {
mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners);
@@ -540,6 +564,49 @@ public final class ViewTreeObserver {
}
/**
+ * Register a callback to be invoked when the window visibility changes.
+ *
+ * @param listener The callback to add
+ *
+ * @throws IllegalStateException If {@link #isAlive()} returns false
+ *
+ * @hide
+ */
+ public void addOnWindowVisibilityChangeListener(
+ @NonNull OnWindowVisibilityChangeListener listener) {
+ checkIsAlive();
+
+ if (mOnWindowVisibilityListeners == null) {
+ mOnWindowVisibilityListeners =
+ new CopyOnWriteArrayList<OnWindowVisibilityChangeListener>();
+ }
+
+ mOnWindowVisibilityListeners.add(listener);
+ }
+
+ /**
+ * Remove a previously installed window visibility callback.
+ *
+ * @param victim The callback to remove
+ *
+ * @throws IllegalStateException If {@link #isAlive()} returns false
+ *
+ * @see #addOnWindowVisibilityChangeListener(
+ * android.view.ViewTreeObserver.OnWindowVisibilityChangeListener)
+ *
+ * @hide
+ */
+ public void removeOnWindowVisibilityChangeListener(
+ @NonNull OnWindowVisibilityChangeListener victim) {
+ checkIsAlive();
+ if (mOnWindowVisibilityListeners == null) {
+ return;
+ }
+
+ mOnWindowVisibilityListeners.remove(victim);
+ }
+
+ /*
* Register a callback to be invoked when the focus state within the view tree changes.
*
* @param listener The callback to add
@@ -1026,6 +1093,23 @@ public final class ViewTreeObserver {
}
/**
+ * Notifies registered listeners that window visibility has changed.
+ */
+ void dispatchOnWindowVisibilityChange(int visibility) {
+ // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
+ // perform the dispatching. The iterator is a safe guard against listeners that
+ // could mutate the list by calling the various add/remove methods. This prevents
+ // the array from being modified while we iterate it.
+ final CopyOnWriteArrayList<OnWindowVisibilityChangeListener> listeners =
+ mOnWindowVisibilityListeners;
+ if (listeners != null && listeners.size() > 0) {
+ for (OnWindowVisibilityChangeListener listener : listeners) {
+ listener.onWindowVisibilityChanged(visibility);
+ }
+ }
+ }
+
+ /**
* Notifies registered listeners that focus has changed.
*/
@UnsupportedAppUsage
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index c846175699f2..c1dddbee408f 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -1423,9 +1423,11 @@ public final class WindowInsets {
static final int DISPLAY_CUTOUT = 1 << 7;
- static final int LAST = 1 << 8;
- static final int SIZE = 9;
- static final int WINDOW_DECOR = LAST;
+ static final int WINDOW_DECOR = 1 << 8;
+
+ static final int GENERIC_OVERLAYS = 1 << 9;
+ static final int LAST = GENERIC_OVERLAYS;
+ static final int SIZE = 10;
static int indexOf(@InsetsType int type) {
switch (type) {
@@ -1447,6 +1449,8 @@ public final class WindowInsets {
return 7;
case WINDOW_DECOR:
return 8;
+ case GENERIC_OVERLAYS:
+ return 9;
default:
throw new IllegalArgumentException("type needs to be >= FIRST and <= LAST,"
+ " type=" + type);
@@ -1482,6 +1486,9 @@ public final class WindowInsets {
if ((types & WINDOW_DECOR) != 0) {
result.append("windowDecor |");
}
+ if ((types & GENERIC_OVERLAYS) != 0) {
+ result.append("genericOverlays |");
+ }
if (result.length() > 0) {
result.delete(result.length() - 2, result.length());
}
@@ -1494,7 +1501,8 @@ public final class WindowInsets {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {STATUS_BARS, NAVIGATION_BARS, CAPTION_BAR, IME, WINDOW_DECOR,
- SYSTEM_GESTURES, MANDATORY_SYSTEM_GESTURES, TAPPABLE_ELEMENT, DISPLAY_CUTOUT})
+ SYSTEM_GESTURES, MANDATORY_SYSTEM_GESTURES, TAPPABLE_ELEMENT, DISPLAY_CUTOUT,
+ GENERIC_OVERLAYS})
public @interface InsetsType {
}
@@ -1586,7 +1594,7 @@ public final class WindowInsets {
* {@link #navigationBars()}, but not {@link #ime()}.
*/
public static @InsetsType int systemBars() {
- return STATUS_BARS | NAVIGATION_BARS | CAPTION_BAR;
+ return STATUS_BARS | NAVIGATION_BARS | CAPTION_BAR | GENERIC_OVERLAYS;
}
/**
diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java
index c320b262ebd7..57a0330e3c18 100644
--- a/core/java/android/view/WindowLayout.java
+++ b/core/java/android/view/WindowLayout.java
@@ -16,8 +16,6 @@
package android.view;
-import static android.view.Gravity.DISPLAY_CLIP_HORIZONTAL;
-import static android.view.Gravity.DISPLAY_CLIP_VERTICAL;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -66,14 +64,15 @@ public class WindowLayout {
public void computeFrames(WindowManager.LayoutParams attrs, InsetsState state,
Rect displayCutoutSafe, Rect windowBounds, @WindowingMode int windowingMode,
int requestedWidth, int requestedHeight, InsetsVisibilities requestedVisibilities,
- Rect attachedWindowFrame, float compatScale, ClientWindowFrames outFrames) {
+ float compatScale, ClientWindowFrames frames) {
final int type = attrs.type;
final int fl = attrs.flags;
final int pfl = attrs.privateFlags;
final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) == FLAG_LAYOUT_IN_SCREEN;
- final Rect outDisplayFrame = outFrames.displayFrame;
- final Rect outParentFrame = outFrames.parentFrame;
- final Rect outFrame = outFrames.frame;
+ final Rect attachedWindowFrame = frames.attachedFrame;
+ final Rect outDisplayFrame = frames.displayFrame;
+ final Rect outParentFrame = frames.parentFrame;
+ final Rect outFrame = frames.frame;
// Compute bounds restricted by insets
final Insets insets = state.calculateInsets(windowBounds, attrs.getFitInsetsTypes(),
@@ -104,7 +103,7 @@ public class WindowLayout {
final DisplayCutout cutout = state.getDisplayCutout();
final Rect displayCutoutSafeExceptMaybeBars = mTempDisplayCutoutSafeExceptMaybeBarsRect;
displayCutoutSafeExceptMaybeBars.set(displayCutoutSafe);
- outFrames.isParentFrameClippedByDisplayCutout = false;
+ frames.isParentFrameClippedByDisplayCutout = false;
if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS && !cutout.isEmpty()) {
// Ensure that windows with a non-ALWAYS display cutout mode are laid out in
// the cutout safe zone.
@@ -167,7 +166,7 @@ public class WindowLayout {
if (!attachedInParent && !floatingInScreenWindow) {
mTempRect.set(outParentFrame);
outParentFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
- outFrames.isParentFrameClippedByDisplayCutout = !mTempRect.equals(outParentFrame);
+ frames.isParentFrameClippedByDisplayCutout = !mTempRect.equals(outParentFrame);
}
outDisplayFrame.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
}
@@ -274,25 +273,14 @@ public class WindowLayout {
Gravity.applyDisplay(attrs.gravity, outDisplayFrame, outFrame);
}
- if (extendedByCutout && !displayCutoutSafe.contains(outFrame)) {
- mTempRect.set(outFrame);
-
- // Move the frame into displayCutoutSafe.
- final int clipFlags = DISPLAY_CLIP_VERTICAL | DISPLAY_CLIP_HORIZONTAL;
- Gravity.applyDisplay(attrs.gravity & ~clipFlags, displayCutoutSafe,
+ if (extendedByCutout) {
+ extendFrameByCutout(displayCutoutSafe, outDisplayFrame, outFrame,
mTempRect);
-
- if (mTempRect.intersect(outDisplayFrame)) {
- outFrame.union(mTempRect);
- }
}
- if (DEBUG) Log.d(TAG, "computeWindowFrames " + attrs.getTitle()
- + " outFrames=" + outFrames
+ if (DEBUG) Log.d(TAG, "computeFrames " + attrs.getTitle()
+ + " frames=" + frames
+ " windowBounds=" + windowBounds.toShortString()
- + " attachedWindowFrame=" + (attachedWindowFrame != null
- ? attachedWindowFrame.toShortString()
- : "null")
+ " requestedWidth=" + requestedWidth
+ " requestedHeight=" + requestedHeight
+ " compatScale=" + compatScale
@@ -303,6 +291,21 @@ public class WindowLayout {
+ " requestedVisibilities=" + requestedVisibilities);
}
+ public static void extendFrameByCutout(Rect displayCutoutSafe,
+ Rect displayFrame, Rect inOutFrame, Rect tempRect) {
+ if (displayCutoutSafe.contains(inOutFrame)) {
+ return;
+ }
+ tempRect.set(inOutFrame);
+
+ // Move the frame into displayCutoutSafe.
+ Gravity.applyDisplay(0 /* gravity */, displayCutoutSafe, tempRect);
+
+ if (tempRect.intersect(displayFrame)) {
+ inOutFrame.union(tempRect);
+ }
+ }
+
public static void computeSurfaceSize(WindowManager.LayoutParams attrs, Rect maxBounds,
int requestedWidth, int requestedHeight, Rect winFrame, boolean dragResizing,
Point outSurfaceSize) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 00052f6015d5..63d42c0ca915 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -98,7 +98,6 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
-import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -341,6 +340,18 @@ public interface WindowManager extends ViewManager {
int TRANSIT_OLD_TASK_FRAGMENT_CHANGE = 30;
/**
+ * A dream activity is being opened.
+ * @hide
+ */
+ int TRANSIT_OLD_DREAM_ACTIVITY_OPEN = 31;
+
+ /**
+ * A dream activity is being closed.
+ * @hide
+ */
+ int TRANSIT_OLD_DREAM_ACTIVITY_CLOSE = 32;
+
+ /**
* @hide
*/
@IntDef(prefix = { "TRANSIT_OLD_" }, value = {
@@ -368,7 +379,9 @@ public interface WindowManager extends ViewManager {
TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
TRANSIT_OLD_TASK_FRAGMENT_OPEN,
TRANSIT_OLD_TASK_FRAGMENT_CLOSE,
- TRANSIT_OLD_TASK_FRAGMENT_CHANGE
+ TRANSIT_OLD_TASK_FRAGMENT_CHANGE,
+ TRANSIT_OLD_DREAM_ACTIVITY_OPEN,
+ TRANSIT_OLD_DREAM_ACTIVITY_CLOSE
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionOldType {}
@@ -2354,6 +2367,14 @@ public interface WindowManager extends ViewManager {
public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 0x00000010;
/**
+ * Flag to allow this window to have unrestricted gesture exclusion.
+ *
+ * @see View#setSystemGestureExclusionRects(List)
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION = 0x00000020;
+
+ /**
* Never animate position changes of the window.
*
* {@hide}
@@ -2573,11 +2594,13 @@ public interface WindowManager extends ViewManager {
PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED,
PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS,
SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
+ PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION,
PRIVATE_FLAG_NO_MOVE_ANIMATION,
PRIVATE_FLAG_COMPATIBLE_WINDOW,
PRIVATE_FLAG_SYSTEM_ERROR,
PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR,
+ PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT,
PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY,
PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH,
PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME,
@@ -2619,6 +2642,10 @@ public interface WindowManager extends ViewManager {
equals = SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
name = "SHOW_FOR_ALL_USERS"),
@ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION,
+ equals = PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION,
+ name = "UNRESTRICTED_GESTURE_EXCLUSION"),
+ @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_NO_MOVE_ANIMATION,
equals = PRIVATE_FLAG_NO_MOVE_ANIMATION,
name = "NO_MOVE_ANIMATION"),
@@ -2639,6 +2666,10 @@ public interface WindowManager extends ViewManager {
equals = PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR,
name = "FORCE_STATUS_BAR_VISIBLE"),
@ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT,
+ equals = PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT,
+ name = "LAYOUT_SIZE_EXTENDED_BY_CUTOUT"),
+ @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY,
equals = PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY,
name = "FORCE_DECOR_VIEW_VISIBILITY"),
@@ -3589,34 +3620,17 @@ public interface WindowManager extends ViewManager {
private boolean mFitInsetsIgnoringVisibility = false;
/**
- * {@link InsetsState.InternalInsetsType}s to be applied to the window
- * If {@link #type} has the predefined insets (like {@link #TYPE_STATUS_BAR} or
- * {@link #TYPE_NAVIGATION_BAR}), this field will be ignored.
+ * If set, the specified insets types will be provided by the window and the insets frame
+ * will be calculated based on the provider's parameters. The insets types and the array
+ * should not be modified after the window is added. If multiple layout parameters are
+ * provided for different rotations in {@link LayoutParams#paramsForRotation}, the types in
+ * the providedInsets array should be the same in all rotations, including the base one.
+ * All other params can be adjusted at runtime.
+ * See {@link InsetsFrameProvider}.
*
- * <p>Note: provide only one inset corresponding to the window type (like
- * {@link InsetsState.InternalInsetsType#ITYPE_STATUS_BAR} or
- * {@link InsetsState.InternalInsetsType#ITYPE_NAVIGATION_BAR})</p>
* @hide
*/
- public @InsetsState.InternalInsetsType int[] providesInsetsTypes;
-
- /**
- * If specified, the insets provided by this window will be our window frame minus the
- * insets specified by providedInternalInsets for each type. This should not be used
- * together with {@link WindowState#mGivenContentInsets}. If both of them are set, both will
- * be applied.
- *
- * @hide
- */
- public Insets[] providedInternalInsets;
-
- /**
- * If specified, the insets provided by this window for the IME will be our window frame
- * minus the insets specified by providedInternalImeInsets.
- *
- * @hide
- */
- public Insets[] providedInternalImeInsets;
+ public InsetsFrameProvider[] providedInsets;
/**
* If specified, the frame that used to calculate relative {@link RoundedCorner} will be
@@ -3748,6 +3762,18 @@ public interface WindowManager extends ViewManager {
}
}
+ /**
+ * @see #paramsForRotation
+ * @hide
+ */
+ public LayoutParams forRotation(int rotation) {
+ if (paramsForRotation == null || paramsForRotation.length <= rotation
+ || paramsForRotation[rotation] == null) {
+ return this;
+ }
+ return paramsForRotation[rotation];
+ }
+
public LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = TYPE_APPLICATION;
@@ -3996,32 +4022,10 @@ public interface WindowManager extends ViewManager {
out.writeBoolean(mFitInsetsIgnoringVisibility);
out.writeBoolean(preferMinimalPostProcessing);
out.writeInt(mBlurBehindRadius);
- if (providesInsetsTypes != null) {
- out.writeInt(providesInsetsTypes.length);
- out.writeIntArray(providesInsetsTypes);
- } else {
- out.writeInt(0);
- }
- if (providedInternalInsets != null) {
- out.writeInt(providedInternalInsets.length);
- out.writeTypedArray(providedInternalInsets, 0 /* parcelableFlags */);
- } else {
- out.writeInt(0);
- }
- if (providedInternalImeInsets != null) {
- out.writeInt(providedInternalImeInsets.length);
- out.writeTypedArray(providedInternalImeInsets, 0 /* parcelableFlags */);
- } else {
- out.writeInt(0);
- }
out.writeBoolean(insetsRoundedCornerFrame);
- if (paramsForRotation != null) {
- checkNonRecursiveParams();
- out.writeInt(paramsForRotation.length);
- out.writeTypedArray(paramsForRotation, 0 /* parcelableFlags */);
- } else {
- out.writeInt(0);
- }
+ out.writeTypedArray(providedInsets, 0 /* parcelableFlags */);
+ checkNonRecursiveParams();
+ out.writeTypedArray(paramsForRotation, 0 /* parcelableFlags */);
}
public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR
@@ -4088,27 +4092,9 @@ public interface WindowManager extends ViewManager {
mFitInsetsIgnoringVisibility = in.readBoolean();
preferMinimalPostProcessing = in.readBoolean();
mBlurBehindRadius = in.readInt();
- int insetsTypesLength = in.readInt();
- if (insetsTypesLength > 0) {
- providesInsetsTypes = new int[insetsTypesLength];
- in.readIntArray(providesInsetsTypes);
- }
- int providedInternalInsetsLength = in.readInt();
- if (providedInternalInsetsLength > 0) {
- providedInternalInsets = new Insets[providedInternalInsetsLength];
- in.readTypedArray(providedInternalInsets, Insets.CREATOR);
- }
- int providedInternalImeInsetsLength = in.readInt();
- if (providedInternalImeInsetsLength > 0) {
- providedInternalImeInsets = new Insets[providedInternalImeInsetsLength];
- in.readTypedArray(providedInternalImeInsets, Insets.CREATOR);
- }
insetsRoundedCornerFrame = in.readBoolean();
- int paramsForRotationLength = in.readInt();
- if (paramsForRotationLength > 0) {
- paramsForRotation = new LayoutParams[paramsForRotationLength];
- in.readTypedArray(paramsForRotation, LayoutParams.CREATOR);
- }
+ providedInsets = in.createTypedArray(InsetsFrameProvider.CREATOR);
+ paramsForRotation = in.createTypedArray(LayoutParams.CREATOR);
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -4400,18 +4386,8 @@ public interface WindowManager extends ViewManager {
changes |= LAYOUT_CHANGED;
}
- if (!Arrays.equals(providesInsetsTypes, o.providesInsetsTypes)) {
- providesInsetsTypes = o.providesInsetsTypes;
- changes |= LAYOUT_CHANGED;
- }
-
- if (!Arrays.equals(providedInternalInsets, o.providedInternalInsets)) {
- providedInternalInsets = o.providedInternalInsets;
- changes |= LAYOUT_CHANGED;
- }
-
- if (!Arrays.equals(providedInternalImeInsets, o.providedInternalImeInsets)) {
- providedInternalImeInsets = o.providedInternalImeInsets;
+ if (!Arrays.equals(providedInsets, o.providedInsets)) {
+ providedInsets = o.providedInsets;
changes |= LAYOUT_CHANGED;
}
@@ -4613,28 +4589,12 @@ public interface WindowManager extends ViewManager {
sb.append(System.lineSeparator());
sb.append(prefix).append(" fitIgnoreVis");
}
- if (providesInsetsTypes != null) {
- sb.append(System.lineSeparator());
- sb.append(prefix).append(" insetsTypes=");
- for (int i = 0; i < providesInsetsTypes.length; ++i) {
- if (i > 0) sb.append(' ');
- sb.append(InsetsState.typeToString(providesInsetsTypes[i]));
- }
- }
- if (providedInternalInsets != null) {
- sb.append(System.lineSeparator());
- sb.append(" providedInternalInsets=");
- for (int i = 0; i < providedInternalInsets.length; ++i) {
- if (i > 0) sb.append(' ');
- sb.append((providedInternalInsets[i]));
- }
- }
- if (providedInternalImeInsets != null) {
+ if (providedInsets != null) {
sb.append(System.lineSeparator());
- sb.append(" providedInternalImeInsets=");
- for (int i = 0; i < providedInternalImeInsets.length; ++i) {
+ sb.append(" providedInsets=");
+ for (int i = 0; i < providedInsets.length; ++i) {
if (i > 0) sb.append(' ');
- sb.append((providedInternalImeInsets[i]));
+ sb.append((providedInsets[i]));
}
}
if (insetsRoundedCornerFrame) {
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index aae930edb729..d37756551db3 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -83,6 +83,11 @@ public final class WindowManagerGlobal {
public static final int RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS = 1 << 3;
/**
+ * The window manager has told the window it cannot draw this frame and should retry again.
+ */
+ public static final int RELAYOUT_RES_CANCEL_AND_REDRAW = 1 << 4;
+
+ /**
* Flag for relayout: the client will be later giving
* internal insets; as a result, the window will not impact other window
* layouts until the insets are given.
@@ -386,7 +391,7 @@ public final class WindowManagerGlobal {
root = new ViewRootImpl(view.getContext(), display);
} else {
root = new ViewRootImpl(view.getContext(), display,
- windowlessSession);
+ windowlessSession, new WindowlessWindowLayout());
}
view.setLayoutParams(wparams);
@@ -399,9 +404,10 @@ public final class WindowManagerGlobal {
try {
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
+ final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);
// BadTokenException or InvalidDisplayException, clean up.
- if (index >= 0) {
- removeViewLocked(index, true);
+ if (viewIndex >= 0) {
+ removeViewLocked(viewIndex, true);
}
throw e;
}
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 4d07171d3086..43d427db2c75 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -237,10 +237,6 @@ public interface WindowManagerPolicyConstants {
*/
int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1;
- // TODO(b/207185041): Remove this divider workaround after we full remove leagacy split and
- // make app pair split only have single root then we can just attach the
- // divider to the single root task in shell.
- int SPLIT_DIVIDER_LAYER = TYPE_LAYER_MULTIPLIER * 3;
int WATERMARK_LAYER = TYPE_LAYER_MULTIPLIER * 100;
int STRICT_MODE_LAYER = TYPE_LAYER_MULTIPLIER * 101;
int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
diff --git a/core/java/android/view/WindowlessWindowLayout.java b/core/java/android/view/WindowlessWindowLayout.java
new file mode 100644
index 000000000000..5bec5b6b6722
--- /dev/null
+++ b/core/java/android/view/WindowlessWindowLayout.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app.WindowConfiguration.WindowingMode;
+import android.graphics.Rect;
+import android.window.ClientWindowFrames;
+
+/**
+ * Computes window frames for the windowless window.
+ * @hide
+ */
+public class WindowlessWindowLayout extends WindowLayout {
+
+ @Override
+ public void computeFrames(WindowManager.LayoutParams attrs, InsetsState state,
+ Rect displayCutoutSafe, Rect windowBounds, @WindowingMode int windowingMode,
+ int requestedWidth, int requestedHeight, InsetsVisibilities requestedVisibilities,
+ float compatScale, ClientWindowFrames frames) {
+ frames.frame.set(0, 0, attrs.width, attrs.height);
+ frames.displayFrame.set(frames.frame);
+ frames.parentFrame.set(frames.frame);
+ }
+}
+
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index a212348a2315..94da2741f71a 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -149,7 +149,7 @@ public class WindowlessWindowManager implements IWindowSession {
public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls) {
+ InsetsSourceControl[] outActiveControls, Rect outAttachedFrame) {
final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
.setFormat(attrs.format)
.setBLASTLayer()
@@ -181,6 +181,7 @@ public class WindowlessWindowManager implements IWindowSession {
synchronized (this) {
mStateForWindow.put(window.asBinder(), state);
}
+ outAttachedFrame.set(0, 0, -1, -1);
final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE |
WindowManagerGlobal.ADD_FLAG_USE_BLAST;
@@ -196,15 +197,15 @@ public class WindowlessWindowManager implements IWindowSession {
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls) {
+ InsetsSourceControl[] outActiveControls, Rect outAttachedFrame) {
return addToDisplay(window, attrs, viewVisibility, displayId, requestedVisibilities,
- outInputChannel, outInsetsState, outActiveControls);
+ outInputChannel, outInsetsState, outActiveControls, outAttachedFrame);
}
@Override
public int addToDisplayWithoutInputChannel(android.view.IWindow window,
android.view.WindowManager.LayoutParams attrs, int viewVisibility, int layerStackId,
- android.view.InsetsState insetsState) {
+ android.view.InsetsState insetsState, Rect outAttachedFrame) {
return 0;
}
@@ -337,21 +338,6 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
- public int updateVisibility(IWindow window, WindowManager.LayoutParams inAttrs,
- int viewVisibility, MergedConfiguration outMergedConfiguration,
- SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls) {
- // TODO(b/161810301): Finish the implementation.
- return 0;
- }
-
- @Override
- public void updateLayout(IWindow window, WindowManager.LayoutParams inAttrs, int flags,
- ClientWindowFrames clientWindowFrames, int requestedWidth, int requestedHeight) {
- // TODO(b/161810301): Finish the implementation.
- }
-
- @Override
public void prepareToReplaceWindows(android.os.IBinder appToken, boolean childrenOnly) {
}
@@ -552,4 +538,9 @@ public class WindowlessWindowManager implements IWindowSession {
}
}
}
+
+ @Override
+ public boolean cancelDraw(IWindow window) {
+ return false;
+ }
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 23393ffe885c..2268bef2c1d9 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6440,9 +6440,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public void setText(CharSequence text, BufferType type) {
setText(text, type, true, 0);
- if (mCharWrapper != null) {
- mCharWrapper.mChars = null;
- }
+ // drop any potential mCharWrappper leaks
+ mCharWrapper = null;
}
@UnsupportedAppUsage
@@ -6653,11 +6652,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* since the TextView has no way to know that the text
* has changed and that it needs to invalidate and re-layout.
*
+ * @throws NullPointerException if text is null
+ * @throws IndexOutOfBoundsException if start or start+len are not in 0 to text.length
+ *
* @param text char array to be displayed
* @param start start index in the char array
* @param len length of char count after {@code start}
*/
- public final void setText(char[] text, int start, int len) {
+ public final void setText(/* @NonNull */ char[] text, int start, int len) {
int oldlen = 0;
if (start < 0 || len < 0 || start + len > text.length) {
@@ -13888,16 +13890,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private static class CharWrapper implements CharSequence, GetChars, GraphicsOperations {
+ @NonNull
private char[] mChars;
private int mStart, mLength;
- public CharWrapper(char[] chars, int start, int len) {
+ CharWrapper(@NonNull char[] chars, int start, int len) {
mChars = chars;
mStart = start;
mLength = len;
}
- /* package */ void set(char[] chars, int start, int len) {
+ /* package */ void set(@NonNull char[] chars, int start, int len) {
mChars = chars;
mStart = start;
mLength = len;
diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
index 51f3fe2551ad..929e81ed9044 100644
--- a/core/java/android/window/ClientWindowFrames.java
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -17,6 +17,7 @@
package android.window;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
@@ -40,6 +41,12 @@ public class ClientWindowFrames implements Parcelable {
*/
public final @NonNull Rect parentFrame = new Rect();
+ /**
+ * The frame this window attaches to. If this is not null, this is the frame of the parent
+ * window.
+ */
+ public @Nullable Rect attachedFrame;
+
public boolean isParentFrameClippedByDisplayCutout;
public ClientWindowFrames() {
@@ -49,6 +56,9 @@ public class ClientWindowFrames implements Parcelable {
frame.set(other.frame);
displayFrame.set(other.displayFrame);
parentFrame.set(other.parentFrame);
+ if (other.attachedFrame != null) {
+ attachedFrame = new Rect(other.attachedFrame);
+ }
isParentFrameClippedByDisplayCutout = other.isParentFrameClippedByDisplayCutout;
}
@@ -61,6 +71,7 @@ public class ClientWindowFrames implements Parcelable {
frame.readFromParcel(in);
displayFrame.readFromParcel(in);
parentFrame.readFromParcel(in);
+ attachedFrame = in.readTypedObject(Rect.CREATOR);
isParentFrameClippedByDisplayCutout = in.readBoolean();
}
@@ -69,6 +80,7 @@ public class ClientWindowFrames implements Parcelable {
frame.writeToParcel(dest, flags);
displayFrame.writeToParcel(dest, flags);
parentFrame.writeToParcel(dest, flags);
+ dest.writeTypedObject(attachedFrame, flags);
dest.writeBoolean(isParentFrameClippedByDisplayCutout);
}
@@ -78,6 +90,7 @@ public class ClientWindowFrames implements Parcelable {
return "ClientWindowFrames{frame=" + frame.toShortString(sb)
+ " display=" + displayFrame.toShortString(sb)
+ " parentFrame=" + parentFrame.toShortString(sb)
+ + (attachedFrame != null ? " attachedFrame=" + attachedFrame.toShortString() : "")
+ " parentClippedByDisplayCutout=" + isParentFrameClippedByDisplayCutout + "}";
}
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index 5924844aa3b2..f1a052b61c59 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -81,8 +81,10 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc
@OnBackInvokedDispatcher.Priority int priority,
@NonNull OnBackInvokedCallback callback) {
final Bundle bundle = new Bundle();
+ // Always invoke back for ime without checking the window focus.
final IOnBackInvokedCallback iCallback =
- new WindowOnBackInvokedDispatcher.OnBackInvokedCallbackWrapper(callback);
+ new WindowOnBackInvokedDispatcher.OnBackInvokedCallbackWrapper(callback,
+ () -> true);
bundle.putBinder(RESULT_KEY_CALLBACK, iCallback.asBinder());
bundle.putInt(RESULT_KEY_PRIORITY, priority);
bundle.putInt(RESULT_KEY_ID, callback.hashCode());
diff --git a/core/java/android/window/PictureInPictureSurfaceTransaction.java b/core/java/android/window/PictureInPictureSurfaceTransaction.java
index 0a751c38cfff..3b1fdf5e4c38 100644
--- a/core/java/android/window/PictureInPictureSurfaceTransaction.java
+++ b/core/java/android/window/PictureInPictureSurfaceTransaction.java
@@ -51,6 +51,8 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable {
private final Rect mWindowCrop;
+ private boolean mShouldDisableCanAffectSystemUiFlags;
+
private PictureInPictureSurfaceTransaction(Parcel in) {
mAlpha = in.readFloat();
mPosition = in.readTypedObject(PointF.CREATOR);
@@ -60,6 +62,7 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable {
mCornerRadius = in.readFloat();
mShadowRadius = in.readFloat();
mWindowCrop = in.readTypedObject(Rect.CREATOR);
+ mShouldDisableCanAffectSystemUiFlags = in.readBoolean();
}
private PictureInPictureSurfaceTransaction(float alpha, @Nullable PointF position,
@@ -84,6 +87,7 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable {
this(other.mAlpha, other.mPosition,
other.mFloat9, other.mRotation, other.mCornerRadius, other.mShadowRadius,
other.mWindowCrop);
+ mShouldDisableCanAffectSystemUiFlags = other.mShouldDisableCanAffectSystemUiFlags;
}
/** @return {@link Matrix} from {@link #mFloat9} */
@@ -103,6 +107,16 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable {
return mShadowRadius > 0;
}
+ /** Sets the internal {@link #mShouldDisableCanAffectSystemUiFlags}. */
+ public void setShouldDisableCanAffectSystemUiFlags(boolean shouldDisable) {
+ mShouldDisableCanAffectSystemUiFlags = shouldDisable;
+ }
+
+ /** @return {@code true} if we should disable Task#setCanAffectSystemUiFlags. */
+ public boolean getShouldDisableCanAffectSystemUiFlags() {
+ return mShouldDisableCanAffectSystemUiFlags;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -114,13 +128,16 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable {
&& Objects.equals(mRotation, that.mRotation)
&& Objects.equals(mCornerRadius, that.mCornerRadius)
&& Objects.equals(mShadowRadius, that.mShadowRadius)
- && Objects.equals(mWindowCrop, that.mWindowCrop);
+ && Objects.equals(mWindowCrop, that.mWindowCrop)
+ && mShouldDisableCanAffectSystemUiFlags
+ == that.mShouldDisableCanAffectSystemUiFlags;
}
@Override
public int hashCode() {
return Objects.hash(mAlpha, mPosition, Arrays.hashCode(mFloat9),
- mRotation, mCornerRadius, mShadowRadius, mWindowCrop);
+ mRotation, mCornerRadius, mShadowRadius, mWindowCrop,
+ mShouldDisableCanAffectSystemUiFlags);
}
@Override
@@ -137,6 +154,7 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable {
out.writeFloat(mCornerRadius);
out.writeFloat(mShadowRadius);
out.writeTypedObject(mWindowCrop, 0 /* flags */);
+ out.writeBoolean(mShouldDisableCanAffectSystemUiFlags);
}
@Override
@@ -150,6 +168,7 @@ public final class PictureInPictureSurfaceTransaction implements Parcelable {
+ " cornerRadius=" + mCornerRadius
+ " shadowRadius=" + mShadowRadius
+ " crop=" + mWindowCrop
+ + " shouldDisableCanAffectSystemUiFlags" + mShouldDisableCanAffectSystemUiFlags
+ ")";
}
diff --git a/core/java/android/window/SizeConfigurationBuckets.java b/core/java/android/window/SizeConfigurationBuckets.java
index f474f0a76cc6..998bec0892ae 100644
--- a/core/java/android/window/SizeConfigurationBuckets.java
+++ b/core/java/android/window/SizeConfigurationBuckets.java
@@ -104,24 +104,15 @@ public final class SizeConfigurationBuckets implements Parcelable {
/**
* Get the changes between two configurations but don't count changes in sizes if they don't
* cross boundaries that are important to the app.
- *
- * This is a static helper to deal with null `buckets`. When no buckets have been specified,
- * this actually filters out all 3 size-configs. This is legacy behavior.
*/
public static int filterDiff(int diff, @NonNull Configuration oldConfig,
@NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets buckets) {
- final boolean nonSizeLayoutFieldsUnchanged =
- areNonSizeLayoutFieldsUnchanged(oldConfig.screenLayout, newConfig.screenLayout);
if (buckets == null) {
- // Only unflip CONFIG_SCREEN_LAYOUT if non-size-related attributes of screen layout do
- // not change.
- if (nonSizeLayoutFieldsUnchanged) {
- return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE
- | CONFIG_SCREEN_LAYOUT);
- } else {
- return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE);
- }
+ return diff;
}
+
+ final boolean nonSizeLayoutFieldsUnchanged =
+ areNonSizeLayoutFieldsUnchanged(oldConfig.screenLayout, newConfig.screenLayout);
if ((diff & CONFIG_SCREEN_SIZE) != 0) {
final boolean crosses = buckets.crossesHorizontalSizeThreshold(oldConfig.screenWidthDp,
newConfig.screenWidthDp)
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 51da61ffb9c9..1f3841ad8b58 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -107,8 +107,14 @@ public final class TransitionInfo implements Parcelable {
*/
public static final int FLAG_DISPLAY_HAS_ALERT_WINDOWS = 1 << 7;
+ /** The container is an input-method window. */
+ public static final int FLAG_IS_INPUT_METHOD = 1 << 8;
+
+ /** The container is ActivityEmbedding embedded. */
+ public static final int FLAG_IS_EMBEDDED = 1 << 9;
+
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
- public static final int FLAG_FIRST_CUSTOM = 1 << 8;
+ public static final int FLAG_FIRST_CUSTOM = 1 << 10;
/** @hide */
@IntDef(prefix = { "FLAG_" }, value = {
@@ -121,6 +127,8 @@ public final class TransitionInfo implements Parcelable {
FLAG_IS_DISPLAY,
FLAG_OCCLUDES_KEYGUARD,
FLAG_DISPLAY_HAS_ALERT_WINDOWS,
+ FLAG_IS_INPUT_METHOD,
+ FLAG_IS_EMBEDDED,
FLAG_FIRST_CUSTOM
})
public @interface ChangeFlags {}
@@ -300,6 +308,9 @@ public final class TransitionInfo implements Parcelable {
if ((flags & FLAG_IS_WALLPAPER) != 0) {
sb.append("IS_WALLPAPER");
}
+ if ((flags & FLAG_IS_INPUT_METHOD) != 0) {
+ sb.append("IS_INPUT_METHOD");
+ }
if ((flags & FLAG_TRANSLUCENT) != 0) {
sb.append((sb.length() == 0 ? "" : "|") + "TRANSLUCENT");
}
@@ -318,6 +329,9 @@ public final class TransitionInfo implements Parcelable {
if ((flags & FLAG_DISPLAY_HAS_ALERT_WINDOWS) != 0) {
sb.append((sb.length() == 0 ? "" : "|") + "DISPLAY_HAS_ALERT_WINDOWS");
}
+ if ((flags & FLAG_IS_EMBEDDED) != 0) {
+ sb.append((sb.length() == 0 ? "" : "|") + "IS_EMBEDDED");
+ }
if ((flags & FLAG_FIRST_CUSTOM) != 0) {
sb.append((sb.length() == 0 ? "" : "|") + "FIRST_CUSTOM");
}
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index 48211a8234ee..14046945ede0 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -16,6 +16,8 @@
package android.window;
+import static android.view.WindowManager.transitTypeToString;
+
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
@@ -366,7 +368,7 @@ public final class TransitionRequestInfo implements Parcelable {
// String fieldNameToString() { ... }
return "TransitionRequestInfo { " +
- "type = " + mType + ", " +
+ "type = " + transitTypeToString(mType) + ", " +
"triggerTask = " + mTriggerTask + ", " +
"remoteTransition = " + mRemoteTransition + ", " +
"displayChange = " + mDisplayChange +
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 7dc039d44f95..3bffa890122a 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -348,8 +348,10 @@ public final class WindowContainerTransaction implements Parcelable {
* @param currentParent of the tasks to perform the operation no.
* {@code null} will perform the operation on the display.
* @param newParent for the tasks. {@code null} will perform the operation on the display.
- * @param windowingModes of the tasks to reparent.
- * @param activityTypes of the tasks to reparent.
+ * @param windowingModes of the tasks to reparent. {@code null} ignore this attribute when
+ * perform the operation.
+ * @param activityTypes of the tasks to reparent. {@code null} ignore this attribute when
+ * perform the operation.
* @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
* the bottom.
*/
@@ -384,16 +386,27 @@ public final class WindowContainerTransaction implements Parcelable {
*/
@NonNull
public WindowContainerTransaction setAdjacentRoots(
- @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2,
- boolean moveTogether) {
+ @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) {
mHierarchyOps.add(HierarchyOp.createForAdjacentRoots(
root1.asBinder(),
- root2.asBinder(),
- moveTogether));
+ root2.asBinder()));
return this;
}
/**
+ * This is temp function for compatible with old cts tests suite and it equal to
+ * {@link #setAdjacentRoots(WindowContainerToken, WindowContainerToken).
+ * @deprecated should use {@link #setAdjacentRoots(WindowContainerToken, WindowContainerToken)}
+ */
+ @Deprecated
+ @NonNull
+ public WindowContainerTransaction setAdjacentRoots(
+ @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2,
+ boolean moveTogether) {
+ return setAdjacentRoots(root1, root2);
+ }
+
+ /**
* Sets the container as launch adjacent flag root. Task starting with
* {@link FLAG_ACTIVITY_LAUNCH_ADJACENT} will be launching to.
*/
@@ -1106,9 +1119,6 @@ public final class WindowContainerTransaction implements Parcelable {
private boolean mReparentTopOnly;
- // TODO(b/207185041): Remove this once having a single-top root for split screen.
- private boolean mMoveAdjacentTogether;
-
@Nullable
private int[] mWindowingModes;
@@ -1171,12 +1181,10 @@ public final class WindowContainerTransaction implements Parcelable {
}
/** Create a hierarchy op for setting adjacent root tasks. */
- public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2,
- boolean moveTogether) {
+ public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS)
.setContainer(root1)
.setReparentContainer(root2)
- .setMoveAdjacentTogether(moveTogether)
.build();
}
@@ -1223,7 +1231,6 @@ public final class WindowContainerTransaction implements Parcelable {
mInsetsProviderFrame = copy.mInsetsProviderFrame;
mToTop = copy.mToTop;
mReparentTopOnly = copy.mReparentTopOnly;
- mMoveAdjacentTogether = copy.mMoveAdjacentTogether;
mWindowingModes = copy.mWindowingModes;
mActivityTypes = copy.mActivityTypes;
mLaunchOptions = copy.mLaunchOptions;
@@ -1245,7 +1252,6 @@ public final class WindowContainerTransaction implements Parcelable {
}
mToTop = in.readBoolean();
mReparentTopOnly = in.readBoolean();
- mMoveAdjacentTogether = in.readBoolean();
mWindowingModes = in.createIntArray();
mActivityTypes = in.createIntArray();
mLaunchOptions = in.readBundle();
@@ -1300,10 +1306,6 @@ public final class WindowContainerTransaction implements Parcelable {
return mReparentTopOnly;
}
- public boolean getMoveAdjacentTogether() {
- return mMoveAdjacentTogether;
- }
-
public int[] getWindowingModes() {
return mWindowingModes;
}
@@ -1356,8 +1358,7 @@ public final class WindowContainerTransaction implements Parcelable {
return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
return "{SetAdjacentRoot: container=" + mContainer
- + " adjacentRoot=" + mReparent + " mMoveAdjacentTogether="
- + mMoveAdjacentTogether + "}";
+ + " adjacentRoot=" + mReparent + "}";
case HIERARCHY_OP_TYPE_LAUNCH_TASK:
return "{LaunchTask: " + mLaunchOptions + "}";
case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
@@ -1413,7 +1414,6 @@ public final class WindowContainerTransaction implements Parcelable {
}
dest.writeBoolean(mToTop);
dest.writeBoolean(mReparentTopOnly);
- dest.writeBoolean(mMoveAdjacentTogether);
dest.writeIntArray(mWindowingModes);
dest.writeIntArray(mActivityTypes);
dest.writeBundle(mLaunchOptions);
@@ -1458,8 +1458,6 @@ public final class WindowContainerTransaction implements Parcelable {
private boolean mReparentTopOnly;
- private boolean mMoveAdjacentTogether;
-
@Nullable
private int[] mWindowingModes;
@@ -1515,11 +1513,6 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
- Builder setMoveAdjacentTogether(boolean moveAdjacentTogether) {
- mMoveAdjacentTogether = moveAdjacentTogether;
- return this;
- }
-
Builder setWindowingModes(@Nullable int[] windowingModes) {
mWindowingModes = windowingModes;
return this;
@@ -1570,7 +1563,6 @@ public final class WindowContainerTransaction implements Parcelable {
hierarchyOp.mInsetsProviderFrame = mInsetsProviderFrame;
hierarchyOp.mToTop = mToTop;
hierarchyOp.mReparentTopOnly = mReparentTopOnly;
- hierarchyOp.mMoveAdjacentTogether = mMoveAdjacentTogether;
hierarchyOp.mLaunchOptions = mLaunchOptions;
hierarchyOp.mActivityIntent = mActivityIntent;
hierarchyOp.mPendingIntent = mPendingIntent;
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index d147524d3b3d..02c5ebcc184e 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -32,6 +32,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;
import java.util.TreeMap;
+import java.util.function.Supplier;
/**
* Provides window based implementation of {@link OnBackInvokedDispatcher}.
@@ -64,6 +65,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
private final TreeMap<Integer, ArrayList<OnBackInvokedCallback>>
mOnBackInvokedCallbacks = new TreeMap<>();
private final Checker mChecker;
+ private boolean mHasFocus;
public WindowOnBackInvokedDispatcher(boolean applicationCallBackEnabled) {
mChecker = new Checker(applicationCallBackEnabled);
@@ -189,7 +191,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
.ImeOnBackInvokedCallback
? ((ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback)
callback).getIOnBackInvokedCallback()
- : new OnBackInvokedCallbackWrapper(callback);
+ : new OnBackInvokedCallbackWrapper(callback, this::hasFocus);
callbackInfo = new OnBackInvokedCallbackInfo(iCallback, priority);
}
mWindowSession.setOnBackInvokedCallbackInfo(mWindow, callbackInfo);
@@ -198,6 +200,17 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
}
}
+ /**
+ * Called when window focus changed.
+ */
+ public void onWindowFocusChanged(boolean hasFocus) {
+ mHasFocus = hasFocus;
+ }
+
+ private boolean hasFocus() {
+ return mHasFocus;
+ }
+
public OnBackInvokedCallback getTopCallback() {
if (mAllCallbacks.isEmpty()) {
return null;
@@ -221,9 +234,11 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
private final WeakReference<OnBackInvokedCallback> mCallback;
-
- OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) {
+ private final Supplier<Boolean> mHasFocus;
+ OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback,
+ @NonNull Supplier<Boolean> hasFocus) {
mCallback = new WeakReference<>(callback);
+ mHasFocus = hasFocus;
}
@Override
@@ -263,7 +278,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
if (callback == null) {
return;
}
-
+ if (!mHasFocus.get()) {
+ Log.w(TAG, "Skip back invoke due to current focus has lost.");
+ return;
+ }
callback.onBackInvoked();
});
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 66abe30d0123..f691300960be 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -30,6 +30,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.SharedElementCallback;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionManager;
@@ -101,7 +102,10 @@ import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Space;
@@ -197,6 +201,8 @@ public class ChooserActivity extends ResolverActivity implements
private static final String PLURALS_COUNT = "count";
private static final String PLURALS_FILE_NAME = "file_name";
+ private static final String IMAGE_EDITOR_SHARED_ELEMENT = "screenshot_preview_image";
+
private boolean mIsAppPredictorComponentAvailable;
private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache;
private Map<ChooserTarget, ShortcutInfo> mDirectShareShortcutInfoCache;
@@ -250,6 +256,11 @@ public class ChooserActivity extends ResolverActivity implements
private static final int DEFAULT_LIST_VIEW_UPDATE_DELAY_MS = 125;
+ private static final int URI_PERMISSION_INTENT_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
+
@VisibleForTesting
int mListViewUpdateDelayMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SHARESHEET_LIST_VIEW_UPDATE_DELAY,
@@ -305,6 +316,8 @@ public class ChooserActivity extends ResolverActivity implements
private boolean mRemoveSharedElements = false;
+ private View mContentView = null;
+
private class ContentPreviewCoordinator {
private static final int IMAGE_FADE_IN_MILLIS = 150;
private static final int IMAGE_LOAD_TIMEOUT = 1;
@@ -990,6 +1003,7 @@ public class ChooserActivity extends ResolverActivity implements
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume: " + getComponentName().flattenToShortString());
+ maybeCancelFinishAnimation();
}
@Override
@@ -1002,6 +1016,7 @@ public class ChooserActivity extends ResolverActivity implements
mShouldDisplayLandscape = shouldDisplayLandscape(newConfig.orientation);
mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row);
+ mChooserMultiProfilePagerAdapter.setMaxTargetsPerRow(mMaxTargetsPerRow);
adjustPreviewWidth(newConfig.orientation, null);
updateStickyContentPreview();
updateTabPadding();
@@ -1084,6 +1099,10 @@ public class ChooserActivity extends ResolverActivity implements
final ComponentName cn = getEditSharingComponent();
final Intent resolveIntent = new Intent(originalIntent);
+ // Retain only URI permission grant flags if present. Other flags may prevent the scene
+ // transition animation from running (i.e FLAG_ACTIVITY_NO_ANIMATION,
+ // FLAG_ACTIVITY_NEW_TASK, FLAG_ACTIVITY_NEW_DOCUMENT) but also not needed.
+ resolveIntent.setFlags(originalIntent.getFlags() & URI_PERMISSION_INTENT_FLAGS);
resolveIntent.setComponent(cn);
resolveIntent.setAction(Intent.ACTION_EDIT);
final ResolveInfo ri = getPackageManager().resolveActivity(
@@ -1100,7 +1119,6 @@ public class ChooserActivity extends ResolverActivity implements
return dri;
}
-
@VisibleForTesting
protected TargetInfo getNearbySharingTarget(Intent originalIntent) {
final ComponentName cn = getNearbySharingComponent();
@@ -1203,15 +1221,30 @@ public class ChooserActivity extends ResolverActivity implements
"",
-1,
false);
+ View firstImgView = getFirstVisibleImgPreviewView();
// Action bar is user-independent, always start as primary
- safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
- finish();
+ if (firstImgView == null) {
+ safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
+ finish();
+ } else {
+ ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(
+ this, firstImgView, IMAGE_EDITOR_SHARED_ELEMENT);
+ safelyStartActivityAsUser(
+ ti, getPersonalProfileUserHandle(), options.toBundle());
+ startFinishAnimation();
+ }
}
);
b.setId(R.id.chooser_edit_button);
return b;
}
+ @Nullable
+ private View getFirstVisibleImgPreviewView() {
+ View firstImage = findViewById(R.id.content_preview_image_1_large);
+ return firstImage != null && firstImage.isVisibleToUser() ? firstImage : null;
+ }
+
private void addActionButton(ViewGroup parent, Button b) {
if (b == null) return;
final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(
@@ -1559,6 +1592,14 @@ public class ChooserActivity extends ResolverActivity implements
}
@Override
+ protected void onStop() {
+ super.onStop();
+ if (maybeCancelFinishAnimation()) {
+ finish();
+ }
+ }
+
+ @Override
protected void onDestroy() {
super.onDestroy();
@@ -2886,6 +2927,30 @@ public class ChooserActivity extends ResolverActivity implements
.setSubtype(previewType));
}
+ private void startFinishAnimation() {
+ View rootView = findRootView();
+ rootView.startAnimation(new FinishAnimation(this, rootView));
+ }
+
+ private boolean maybeCancelFinishAnimation() {
+ View rootView = findRootView();
+ Animation animation = rootView.getAnimation();
+ if (animation instanceof FinishAnimation) {
+ boolean hasEnded = animation.hasEnded();
+ animation.cancel();
+ rootView.clearAnimation();
+ return !hasEnded;
+ }
+ return false;
+ }
+
+ private View findRootView() {
+ if (mContentView == null) {
+ mContentView = findViewById(android.R.id.content);
+ }
+ return mContentView;
+ }
+
abstract static class ViewHolderBase extends RecyclerView.ViewHolder {
private int mViewType;
@@ -3986,6 +4051,66 @@ public class ChooserActivity extends ResolverActivity implements
}
}
+ /**
+ * Used in combination with the scene transition when launching the image editor
+ */
+ private static class FinishAnimation extends AlphaAnimation implements
+ Animation.AnimationListener {
+ private Activity mActivity;
+ private View mRootView;
+ private final float mFromAlpha;
+
+ FinishAnimation(Activity activity, View rootView) {
+ super(rootView.getAlpha(), 0.0f);
+ mActivity = activity;
+ mRootView = rootView;
+ mFromAlpha = rootView.getAlpha();
+ setInterpolator(new LinearInterpolator());
+ long duration = activity.getWindow().getTransitionBackgroundFadeDuration();
+ setDuration(duration);
+ // The scene transition animation looks better when it's not overlapped with this
+ // fade-out animation thus the delay.
+ // It is most likely that the image editor will cause this activity to stop and this
+ // animation will be cancelled in the background without running (i.e. we'll animate
+ // only when this activity remains partially visible after the image editor launch).
+ setStartOffset(duration);
+ super.setAnimationListener(this);
+ }
+
+ @Override
+ public void setAnimationListener(AnimationListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void cancel() {
+ mRootView.setAlpha(mFromAlpha);
+ cleanup();
+ super.cancel();
+ }
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ if (mActivity != null) {
+ mActivity.finish();
+ cleanup();
+ }
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ private void cleanup() {
+ mActivity = null;
+ mRootView = null;
+ }
+ }
+
@Override
protected void maybeLogProfileChange() {
getChooserActivityLogger().logShareheetProfileChanged();
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index e6cc62472f5c..df1130be5ec6 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -93,6 +93,10 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd
return profileDescriptor;
}
+ public void setMaxTargetsPerRow(int maxTargetsPerRow) {
+ mMaxTargetsPerRow = maxTargetsPerRow;
+ }
+
RecyclerView getListViewForIndex(int index) {
return getItem(index).recyclerView;
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 40429c609150..e9e437fda8f2 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1314,7 +1314,7 @@ public class ResolverActivity extends Activity implements
StrictMode.disableDeathOnFileUriExposure();
try {
UserHandle currentUserHandle = mMultiProfilePagerAdapter.getCurrentUserHandle();
- safelyStartActivityInternal(cti, currentUserHandle);
+ safelyStartActivityInternal(cti, currentUserHandle, null);
} finally {
StrictMode.enableDeathOnFileUriExposure();
}
@@ -1327,18 +1327,23 @@ public class ResolverActivity extends Activity implements
*/
@VisibleForTesting
public void safelyStartActivityAsUser(TargetInfo cti, UserHandle user) {
+ safelyStartActivityAsUser(cti, user, null);
+ }
+
+ protected void safelyStartActivityAsUser(
+ TargetInfo cti, UserHandle user, @Nullable Bundle options) {
// We're dispatching intents that might be coming from legacy apps, so
// don't kill ourselves.
StrictMode.disableDeathOnFileUriExposure();
try {
- safelyStartActivityInternal(cti, user);
+ safelyStartActivityInternal(cti, user, options);
} finally {
StrictMode.enableDeathOnFileUriExposure();
}
}
-
- private void safelyStartActivityInternal(TargetInfo cti, UserHandle user) {
+ private void safelyStartActivityInternal(
+ TargetInfo cti, UserHandle user, @Nullable Bundle options) {
// If the target is suspended, the activity will not be successfully launched.
// Do not unregister from package manager updates in this case
if (!cti.isSuspended() && mRegistered) {
@@ -1356,14 +1361,14 @@ public class ResolverActivity extends Activity implements
Toast.makeText(this, mProfileSwitchMessage, Toast.LENGTH_LONG).show();
}
if (!mSafeForwardingMode) {
- if (cti.startAsUser(this, null, user)) {
+ if (cti.startAsUser(this, options, user)) {
onActivityStarted(cti);
maybeLogCrossProfileTargetLaunch(cti, user);
}
return;
}
try {
- if (cti.startAsCaller(this, null, user.getIdentifier())) {
+ if (cti.startAsCaller(this, options, user.getIdentifier())) {
onActivityStarted(cti);
maybeLogCrossProfileTargetLaunch(cti, user);
}
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 2ed0f981692e..b32afb44852a 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -265,6 +265,7 @@ public class ResolverListAdapter extends BaseAdapter {
return mResolverListController.getResolversForIntent(
/* shouldGetResolvedFilter= */ true,
mResolverListCommunicator.shouldGetActivityMetadata(),
+ mResolverListCommunicator.shouldGetOnlyDefaultActivities(),
mIntents);
}
}
@@ -416,8 +417,9 @@ public class ResolverListAdapter extends BaseAdapter {
if (ii == null) {
continue;
}
- ActivityInfo ai = ii.resolveActivityInfo(
- mPm, 0);
+ // Because of AIDL bug, resolveActivityInfo can't accept subclasses of Intent.
+ final Intent rii = (ii.getClass() == Intent.class) ? ii : new Intent(ii);
+ ActivityInfo ai = rii.resolveActivityInfo(mPm, 0);
if (ai == null) {
Log.w(TAG, "No activity found for " + ii);
continue;
@@ -727,6 +729,7 @@ public class ResolverListAdapter extends BaseAdapter {
protected List<ResolvedComponentInfo> getResolversForUser(UserHandle userHandle) {
return mResolverListController.getResolversForIntentAsUser(true,
mResolverListCommunicator.shouldGetActivityMetadata(),
+ mResolverListCommunicator.shouldGetOnlyDefaultActivities(),
mIntents, userHandle);
}
@@ -820,6 +823,12 @@ public class ResolverListAdapter extends BaseAdapter {
boolean shouldGetActivityMetadata();
+ /**
+ * @return true to filter only apps that can handle
+ * {@link android.content.Intent#CATEGORY_DEFAULT} intents
+ */
+ default boolean shouldGetOnlyDefaultActivities() { return true; };
+
Intent getTargetIntent();
void onHandlePackagesChanged(ResolverListAdapter listAdapter);
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index 27573631b2ce..01dcf9624ed5 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -110,17 +110,19 @@ public class ResolverListController {
public List<ResolverActivity.ResolvedComponentInfo> getResolversForIntent(
boolean shouldGetResolvedFilter,
boolean shouldGetActivityMetadata,
+ boolean shouldGetOnlyDefaultActivities,
List<Intent> intents) {
return getResolversForIntentAsUser(shouldGetResolvedFilter, shouldGetActivityMetadata,
- intents, mUserHandle);
+ shouldGetOnlyDefaultActivities, intents, mUserHandle);
}
public List<ResolverActivity.ResolvedComponentInfo> getResolversForIntentAsUser(
boolean shouldGetResolvedFilter,
boolean shouldGetActivityMetadata,
+ boolean shouldGetOnlyDefaultActivities,
List<Intent> intents,
UserHandle userHandle) {
- int baseFlags = PackageManager.MATCH_DEFAULT_ONLY
+ int baseFlags = (shouldGetOnlyDefaultActivities ? PackageManager.MATCH_DEFAULT_ONLY : 0)
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
@@ -134,12 +136,15 @@ public class ResolverListController {
int baseFlags) {
List<ResolverActivity.ResolvedComponentInfo> resolvedComponents = null;
for (int i = 0, N = intents.size(); i < N; i++) {
- final Intent intent = intents.get(i);
+ Intent intent = intents.get(i);
int flags = baseFlags;
if (intent.isWebIntent()
|| (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
flags |= PackageManager.MATCH_INSTANT;
}
+ // Because of AIDL bug, queryIntentActivitiesAsUser can't accept subclasses of Intent.
+ intent = (intent.getClass() == Intent.class) ? intent : new Intent(
+ intent);
final List<ResolveInfo> infos = mpm.queryIntentActivitiesAsUser(intent, flags,
userHandle);
if (infos != null) {
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 825b486d0635..dbfa4d35974b 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -26,10 +26,12 @@ import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;
import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_CANCEL;
import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_END;
+import static com.android.internal.jank.InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UiThread;
import android.graphics.HardwareRendererObserver;
import android.os.Handler;
import android.os.Trace;
@@ -85,8 +87,9 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
public @interface Reasons {
}
+ @VisibleForTesting
+ public final InteractionJankMonitor mMonitor;
private final HardwareRendererObserver mObserver;
- private SurfaceControl mSurfaceControl;
private final int mTraceThresholdMissedFrames;
private final int mTraceThresholdFrameTimeMillis;
private final ThreadedRendererWrapper mRendererWrapper;
@@ -99,17 +102,17 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
private final Handler mHandler;
private final ChoreographerWrapper mChoreographer;
private final StatsLogWrapper mStatsLog;
- private final Object mLock = InteractionJankMonitor.getInstance().getLock();
private final boolean mDeferMonitoring;
+ private final FrameTrackerListener mListener;
@VisibleForTesting
public final boolean mSurfaceOnly;
+ private SurfaceControl mSurfaceControl;
private long mBeginVsyncId = INVALID_ID;
private long mEndVsyncId = INVALID_ID;
private boolean mMetricsFinalized;
private boolean mCancelled = false;
- private FrameTrackerListener mListener;
private boolean mTracingStarted = false;
private Runnable mWaitForFinishTimedOut;
@@ -142,16 +145,52 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
this.jankType = jankType;
this.isFirstFrame = isFirstFrame;
}
+
+ @Override
+ public String toString() {
+ StringBuilder str = new StringBuilder();
+ switch (jankType) {
+ case JANK_NONE:
+ str.append("JANK_NONE");
+ break;
+ case JANK_APP_DEADLINE_MISSED:
+ str.append("JANK_APP_DEADLINE_MISSED");
+ break;
+ case JANK_SURFACEFLINGER_DEADLINE_MISSED:
+ str.append("JANK_SURFACEFLINGER_DEADLINE_MISSED");
+ break;
+ case JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED:
+ str.append("JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED");
+ break;
+ case DISPLAY_HAL:
+ str.append("DISPLAY_HAL");
+ break;
+ case PREDICTION_ERROR:
+ str.append("PREDICTION_ERROR");
+ break;
+ case SURFACE_FLINGER_SCHEDULING:
+ str.append("SURFACE_FLINGER_SCHEDULING");
+ break;
+ default:
+ str.append("UNKNOWN: ").append(jankType);
+ break;
+ }
+ str.append(", ").append(frameVsyncId);
+ str.append(", ").append(totalDurationNanos);
+ return str.toString();
+ }
}
- public FrameTracker(@NonNull Session session, @NonNull Handler handler,
- @Nullable ThreadedRendererWrapper renderer, @Nullable ViewRootWrapper viewRootWrapper,
+ public FrameTracker(@NonNull InteractionJankMonitor monitor, @NonNull Session session,
+ @NonNull Handler handler, @Nullable ThreadedRendererWrapper renderer,
+ @Nullable ViewRootWrapper viewRootWrapper,
@NonNull SurfaceControlWrapper surfaceControlWrapper,
@NonNull ChoreographerWrapper choreographer,
@Nullable FrameMetricsWrapper metrics,
@NonNull StatsLogWrapper statsLog,
int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis,
@Nullable FrameTrackerListener listener, @NonNull Configuration config) {
+ mMonitor = monitor;
mSurfaceOnly = config.isSurfaceOnly();
mSession = session;
mHandler = handler;
@@ -186,17 +225,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() {
@Override
public void surfaceCreated(SurfaceControl.Transaction t) {
- synchronized (mLock) {
+ getHandler().runWithScissors(() -> {
if (mSurfaceControl == null) {
mSurfaceControl = mViewRoot.getSurfaceControl();
if (mBeginVsyncId != INVALID_ID) {
- mSurfaceControlWrapper.addJankStatsListener(
- FrameTracker.this, mSurfaceControl);
- markEvent("FT#deferMonitoring");
- postTraceStartMarker();
+ // Previous begin invocation is not successfully, begin it again.
+ begin();
}
}
- }
+ }, EXECUTOR_TASK_TIMEOUT);
}
@Override
@@ -208,18 +245,16 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
// Wait a while to give the system a chance for the remaining
// frames to arrive, then force finish the session.
- mHandler.postDelayed(() -> {
- synchronized (mLock) {
- if (DEBUG) {
- Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
- + ", finalized=" + mMetricsFinalized
- + ", info=" + mJankInfos.size()
- + ", vsync=" + mBeginVsyncId);
- }
- if (!mMetricsFinalized) {
- end(REASON_END_SURFACE_DESTROYED);
- finish();
- }
+ getHandler().postDelayed(() -> {
+ if (DEBUG) {
+ Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
+ + ", finalized=" + mMetricsFinalized
+ + ", info=" + mJankInfos.size()
+ + ", vsync=" + mBeginVsyncId);
+ }
+ if (!mMetricsFinalized) {
+ end(REASON_END_SURFACE_DESTROYED);
+ finish();
}
}, 50);
}
@@ -230,35 +265,42 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
}
}
+ @VisibleForTesting
+ public Handler getHandler() {
+ return mHandler;
+ }
+
/**
* Begin a trace session of the CUJ.
*/
+ @UiThread
public void begin() {
- synchronized (mLock) {
- final long currentVsync = mChoreographer.getVsyncId();
- // In normal case, we should begin at the next frame,
- // the id of the next frame is not simply increased by 1,
- // but we can exclude the current frame at least.
+ final long currentVsync = mChoreographer.getVsyncId();
+ // In normal case, we should begin at the next frame,
+ // the id of the next frame is not simply increased by 1,
+ // but we can exclude the current frame at least.
+ if (mBeginVsyncId == INVALID_ID) {
mBeginVsyncId = mDeferMonitoring ? currentVsync + 1 : currentVsync;
+ }
+ if (mSurfaceControl != null) {
if (DEBUG) {
Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId
- + ", defer=" + mDeferMonitoring);
+ + ", defer=" + mDeferMonitoring + ", current=" + currentVsync);
}
- if (mSurfaceControl != null) {
- if (mDeferMonitoring) {
- markEvent("FT#deferMonitoring");
- // Normal case, we begin the instrument from the very beginning,
- // will exclude the first frame.
- postTraceStartMarker();
- } else {
- // If we don't begin the instrument from the very beginning,
- // there is no need to skip the frame where the begin invocation happens.
- beginInternal();
- }
- mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
+ if (mDeferMonitoring && currentVsync < mBeginVsyncId) {
+ markEvent("FT#deferMonitoring");
+ // Normal case, we begin the instrument from the very beginning,
+ // will exclude the first frame.
+ postTraceStartMarker(this::beginInternal);
+ } else {
+ // If we don't begin the instrument from the very beginning,
+ // there is no need to skip the frame where the begin invocation happens.
+ beginInternal();
}
- if (!mSurfaceOnly) {
- mRendererWrapper.addObserver(mObserver);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "begin: defer beginning since the surface is not ready for CUJ="
+ + mSession.getName());
}
}
}
@@ -267,89 +309,89 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
* Start trace section at appropriate time.
*/
@VisibleForTesting
- public void postTraceStartMarker() {
- mChoreographer.mChoreographer.postCallback(
- Choreographer.CALLBACK_INPUT, this::beginInternal, null);
+ public void postTraceStartMarker(Runnable action) {
+ mChoreographer.mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, action, null);
}
+ @UiThread
private void beginInternal() {
- synchronized (mLock) {
- if (mCancelled || mEndVsyncId != INVALID_ID) {
- return;
- }
- mTracingStarted = true;
- markEvent("FT#begin");
- Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
+ if (mCancelled || mEndVsyncId != INVALID_ID) {
+ return;
+ }
+ mTracingStarted = true;
+ markEvent("FT#begin");
+ Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
+ mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
+ if (!mSurfaceOnly) {
+ mRendererWrapper.addObserver(mObserver);
}
}
/**
* End the trace session of the CUJ.
*/
+ @UiThread
public boolean end(@Reasons int reason) {
- synchronized (mLock) {
- if (mCancelled || mEndVsyncId != INVALID_ID) return false;
- mEndVsyncId = mChoreographer.getVsyncId();
- // Cancel the session if:
- // 1. The session begins and ends at the same vsync id.
- // 2. The session never begun.
- if (mBeginVsyncId == INVALID_ID) {
- return cancel(REASON_CANCEL_NOT_BEGUN);
- } else if (mEndVsyncId <= mBeginVsyncId) {
- return cancel(REASON_CANCEL_SAME_VSYNC);
- } else {
- if (DEBUG) {
- Log.d(TAG, "end: " + mSession.getName()
- + ", end=" + mEndVsyncId + ", reason=" + reason);
- }
- markEvent("FT#end#" + reason);
- Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
- mSession.setReason(reason);
-
- // We don't remove observer here,
- // will remove it when all the frame metrics in this duration are called back.
- // See onFrameMetricsAvailable for the logic of removing the observer.
- // Waiting at most 10 seconds for all callbacks to finish.
- mWaitForFinishTimedOut = () -> {
- Log.e(TAG, "force finish cuj because of time out:" + mSession.getName());
- finish();
- };
- mHandler.postDelayed(mWaitForFinishTimedOut, TimeUnit.SECONDS.toMillis(10));
- notifyCujEvent(ACTION_SESSION_END);
- return true;
+ if (mCancelled || mEndVsyncId != INVALID_ID) return false;
+ mEndVsyncId = mChoreographer.getVsyncId();
+ // Cancel the session if:
+ // 1. The session begins and ends at the same vsync id.
+ // 2. The session never begun.
+ if (mBeginVsyncId == INVALID_ID) {
+ return cancel(REASON_CANCEL_NOT_BEGUN);
+ } else if (mEndVsyncId <= mBeginVsyncId) {
+ return cancel(REASON_CANCEL_SAME_VSYNC);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "end: " + mSession.getName()
+ + ", end=" + mEndVsyncId + ", reason=" + reason);
}
+ markEvent("FT#end#" + reason);
+ Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
+ mSession.setReason(reason);
+
+ // We don't remove observer here,
+ // will remove it when all the frame metrics in this duration are called back.
+ // See onFrameMetricsAvailable for the logic of removing the observer.
+ // Waiting at most 10 seconds for all callbacks to finish.
+ mWaitForFinishTimedOut = () -> {
+ Log.e(TAG, "force finish cuj because of time out:" + mSession.getName());
+ finish();
+ };
+ getHandler().postDelayed(mWaitForFinishTimedOut, TimeUnit.SECONDS.toMillis(10));
+ notifyCujEvent(ACTION_SESSION_END);
+ return true;
}
}
/**
* Cancel the trace session of the CUJ.
*/
+ @UiThread
public boolean cancel(@Reasons int reason) {
- synchronized (mLock) {
- final boolean cancelFromEnd =
- reason == REASON_CANCEL_NOT_BEGUN || reason == REASON_CANCEL_SAME_VSYNC;
- if (mCancelled || (mEndVsyncId != INVALID_ID && !cancelFromEnd)) return false;
- mCancelled = true;
- markEvent("FT#cancel#" + reason);
- // We don't need to end the trace section if it never begun.
- if (mTracingStarted) {
- Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
- }
-
- // Always remove the observers in cancel call to avoid leakage.
- removeObservers();
+ final boolean cancelFromEnd =
+ reason == REASON_CANCEL_NOT_BEGUN || reason == REASON_CANCEL_SAME_VSYNC;
+ if (mCancelled || (mEndVsyncId != INVALID_ID && !cancelFromEnd)) return false;
+ mCancelled = true;
+ markEvent("FT#cancel#" + reason);
+ // We don't need to end the trace section if it has never begun.
+ if (mTracingStarted) {
+ Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
+ }
- if (DEBUG) {
- Log.d(TAG, "cancel: " + mSession.getName() + ", begin=" + mBeginVsyncId
- + ", end=" + mEndVsyncId + ", reason=" + reason);
- }
+ // Always remove the observers in cancel call to avoid leakage.
+ removeObservers();
- mSession.setReason(reason);
- // Notify the listener the session has been cancelled.
- // We don't notify the listeners if the session never begun.
- notifyCujEvent(ACTION_SESSION_CANCEL);
- return true;
+ if (DEBUG) {
+ Log.d(TAG, "cancel: " + mSession.getName() + ", begin=" + mBeginVsyncId
+ + ", end=" + mEndVsyncId + ", reason=" + reason);
}
+
+ mSession.setReason(reason);
+ // Notify the listener the session has been cancelled.
+ // We don't notify the listeners if the session never begun.
+ notifyCujEvent(ACTION_SESSION_CANCEL);
+ return true;
}
private void markEvent(String desc) {
@@ -364,8 +406,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
@Override
public void onJankDataAvailable(SurfaceControl.JankData[] jankData) {
- synchronized (mLock) {
- if (mCancelled) {
+ postCallback(() -> {
+ if (mCancelled || mMetricsFinalized) {
return;
}
@@ -384,10 +426,19 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
}
}
processJankInfos();
- }
+ });
}
- private @Nullable JankInfo findJankInfo(long frameVsyncId) {
+ /**
+ * For easier argument capture.
+ */
+ @VisibleForTesting
+ public void postCallback(Runnable callback) {
+ getHandler().post(callback);
+ }
+
+ @Nullable
+ private JankInfo findJankInfo(long frameVsyncId) {
return mJankInfos.get((int) frameVsyncId);
}
@@ -400,8 +451,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
@Override
public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
- synchronized (mLock) {
- if (mCancelled) {
+ postCallback(() -> {
+ if (mCancelled || mMetricsFinalized) {
return;
}
@@ -426,9 +477,10 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
frameVsyncId, totalDurationNanos, isFirstFrame));
}
processJankInfos();
- }
+ });
}
+ @UiThread
private boolean hasReceivedCallbacksAfterEnd() {
if (mEndVsyncId == INVALID_ID) {
return false;
@@ -451,6 +503,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
return false;
}
+ @UiThread
private void processJankInfos() {
if (mMetricsFinalized) {
return;
@@ -467,9 +520,12 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
: info.hwuiCallbackFired && info.surfaceControlCallbackFired;
}
+ @UiThread
private void finish() {
- mHandler.removeCallbacks(mWaitForFinishTimedOut);
+ getHandler().removeCallbacks(mWaitForFinishTimedOut);
mWaitForFinishTimedOut = null;
+ if (mMetricsFinalized || mCancelled) return;
+ markEvent("FT#finish#" + mJankInfos.size());
mMetricsFinalized = true;
// The tracing has been ended, remove the observer, see if need to trigger perfetto.
@@ -496,7 +552,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
totalFramesCount++;
boolean missedFrame = false;
if ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0) {
- Log.w(TAG, "Missed App frame:" + info.jankType);
+ Log.w(TAG, "Missed App frame:" + info + ", CUJ=" + mSession.getName());
missedAppFramesCount++;
missedFrame = true;
}
@@ -505,7 +561,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
|| (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0
|| (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0
|| (info.jankType & PREDICTION_ERROR) != 0) {
- Log.w(TAG, "Missed SF frame:" + info.jankType);
+ Log.w(TAG, "Missed SF frame:" + info + ", CUJ=" + mSession.getName());
missedSfFramesCount++;
missedFrame = true;
}
@@ -520,13 +576,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
// TODO (b/174755489): Early latch currently gets fired way too often, so we have
// to ignore it for now.
if (!mSurfaceOnly && !info.hwuiCallbackFired) {
- Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId);
+ Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId
+ + ", CUJ=" + mSession.getName());
}
}
if (!mSurfaceOnly && info.hwuiCallbackFired) {
maxFrameTimeNanos = Math.max(info.totalDurationNanos, maxFrameTimeNanos);
if (!info.surfaceControlCallbackFired) {
- Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId);
+ Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId
+ + ", CUJ=" + mSession.getName());
}
}
}
@@ -586,6 +644,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
* Remove all the registered listeners, observers and callbacks.
*/
@VisibleForTesting
+ @UiThread
public void removeObservers() {
mSurfaceControlWrapper.removeJankStatsListener(this);
if (!mSurfaceOnly) {
@@ -601,7 +660,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
* Trigger the prefetto daemon.
*/
public void triggerPerfetto() {
- InteractionJankMonitor.getInstance().trigger(mSession);
+ mMonitor.trigger(mSession);
}
/**
@@ -666,10 +725,18 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
mViewRoot = viewRoot;
}
+ /**
+ * {@link ViewRootImpl#addSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback)}
+ * @param callback {@link ViewRootImpl.SurfaceChangedCallback}
+ */
public void addSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback callback) {
mViewRoot.addSurfaceChangedCallback(callback);
}
+ /**
+ * {@link ViewRootImpl#removeSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback)}
+ * @param callback {@link ViewRootImpl.SurfaceChangedCallback}
+ */
public void removeSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback callback) {
mViewRoot.removeSurfaceChangedCallback(callback);
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 65e7abf674c3..8f10a5e3f049 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -53,6 +53,8 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
@@ -74,15 +76,21 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_TO_SHOW_INFO_WITH_ACTIONS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_SHOW_FUNCTION_SCREEN_WITH_ACTIONS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TAKE_SCREENSHOT;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__UNFOLD_ANIM;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_DIALOG_OPEN;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__VOLUME_CONTROL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.UiThread;
+import android.annotation.WorkerThread;
import android.content.Context;
import android.os.Build;
+import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.provider.DeviceConfig;
@@ -93,6 +101,7 @@ import android.view.Choreographer;
import android.view.SurfaceControl;
import android.view.View;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
@@ -126,6 +135,7 @@ public class InteractionJankMonitor {
private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(2L);
+ static final long EXECUTOR_TASK_TIMEOUT = 500;
private static final String SETTINGS_ENABLED_KEY = "enabled";
private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
private static final String SETTINGS_THRESHOLD_MISSED_FRAMES_KEY =
@@ -202,6 +212,11 @@ public class InteractionJankMonitor {
public static final int CUJ_VOLUME_CONTROL = 55;
public static final int CUJ_BIOMETRIC_PROMPT_TRANSITION = 56;
public static final int CUJ_SETTINGS_TOGGLE = 57;
+ public static final int CUJ_SHADE_DIALOG_OPEN = 58;
+ public static final int CUJ_USER_DIALOG_OPEN = 59;
+ public static final int CUJ_TASKBAR_EXPAND = 60;
+ public static final int CUJ_TASKBAR_COLLAPSE = 61;
+ public static final int CUJ_SHADE_CLEAR_ALL = 62;
private static final int NO_STATSD_LOGGING = -1;
@@ -268,6 +283,11 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__VOLUME_CONTROL,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BIOMETRIC_PROMPT_TRANSITION,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_TOGGLE,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_DIALOG_OPEN,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL,
};
private static volatile InteractionJankMonitor sInstance;
@@ -275,13 +295,14 @@ public class InteractionJankMonitor {
private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
this::updateProperties;
- private final FrameMetricsWrapper mMetrics;
+ @GuardedBy("mLock")
private final SparseArray<FrameTracker> mRunningTrackers;
+ @GuardedBy("mLock")
private final SparseArray<Runnable> mTimeoutActions;
private final HandlerThread mWorker;
private final Object mLock = new Object();
- private boolean mEnabled = DEFAULT_ENABLED;
+ private volatile boolean mEnabled = DEFAULT_ENABLED;
private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
private int mTraceThresholdMissedFrames = DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES;
private int mTraceThresholdFrameTimeMillis = DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS;
@@ -346,6 +367,11 @@ public class InteractionJankMonitor {
CUJ_VOLUME_CONTROL,
CUJ_BIOMETRIC_PROMPT_TRANSITION,
CUJ_SETTINGS_TOGGLE,
+ CUJ_SHADE_DIALOG_OPEN,
+ CUJ_USER_DIALOG_OPEN,
+ CUJ_TASKBAR_EXPAND,
+ CUJ_TASKBAR_COLLAPSE,
+ CUJ_SHADE_CLEAR_ALL
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -378,9 +404,7 @@ public class InteractionJankMonitor {
mRunningTrackers = new SparseArray<>();
mTimeoutActions = new SparseArray<>();
mWorker = worker;
- mMetrics = new FrameMetricsWrapper();
mWorker.start();
- mEnabled = DEFAULT_ENABLED;
mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
// Post initialization to the background in case we're running on the main
@@ -393,10 +417,7 @@ public class InteractionJankMonitor {
DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR,
new HandlerExecutor(mWorker.getThreadHandler()),
mPropertiesChangedListener);
- }
-
- Object getLock() {
- return mLock;
+ mEnabled = DEFAULT_ENABLED;
}
/**
@@ -413,27 +434,27 @@ public class InteractionJankMonitor {
view == null ? null : new ThreadedRendererWrapper(view.getThreadedRenderer());
final ViewRootWrapper viewRoot =
view == null ? null : new ViewRootWrapper(view.getViewRootImpl());
-
final SurfaceControlWrapper surfaceControl = new SurfaceControlWrapper();
final ChoreographerWrapper choreographer =
new ChoreographerWrapper(Choreographer.getInstance());
+ final FrameTrackerListener eventsListener = (s, act) -> handleCujEvents(act, s);
+ final FrameMetricsWrapper frameMetrics = new FrameMetricsWrapper();
- synchronized (mLock) {
- FrameTrackerListener eventsListener = (s, act) -> handleCujEvents(act, s);
- return new FrameTracker(session, mWorker.getThreadHandler(),
- threadedRenderer, viewRoot, surfaceControl, choreographer,
- mMetrics, new FrameTracker.StatsLogWrapper(),
- mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis,
- eventsListener, config);
- }
+ return new FrameTracker(this, session, config.getHandler(), threadedRenderer, viewRoot,
+ surfaceControl, choreographer, frameMetrics, new FrameTracker.StatsLogWrapper(),
+ mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis,
+ eventsListener, config);
}
+ @UiThread
private void handleCujEvents(String action, Session session) {
// Clear the running and timeout tasks if the end / cancel was fired within the tracker.
// Or we might have memory leaks.
if (needRemoveTasks(action, session)) {
- removeTimeout(session.getCuj());
- removeTracker(session.getCuj());
+ getTracker(session.getCuj()).getHandler().runWithScissors(() -> {
+ removeTimeout(session.getCuj());
+ removeTracker(session.getCuj());
+ }, EXECUTOR_TASK_TIMEOUT);
}
}
@@ -450,7 +471,7 @@ public class InteractionJankMonitor {
synchronized (mLock) {
Runnable timeout = mTimeoutActions.get(cujType);
if (timeout != null) {
- mWorker.getThreadHandler().removeCallbacks(timeout);
+ getTracker(cujType).getHandler().removeCallbacks(timeout);
mTimeoutActions.remove(cujType);
}
}
@@ -475,9 +496,7 @@ public class InteractionJankMonitor {
*/
public boolean begin(View v, @CujType int cujType) {
try {
- return beginInternal(
- Configuration.Builder.withView(cujType, v)
- .build());
+ return begin(Configuration.Builder.withView(cujType, v));
} catch (IllegalArgumentException ex) {
Log.d(TAG, "Build configuration failed!", ex);
return false;
@@ -488,35 +507,42 @@ public class InteractionJankMonitor {
* Begins a trace session.
*
* @param builder the builder of the configurations for instrumenting the CUJ.
- * @return boolean true if the tracker is started successfully, false otherwise.
+ * @return boolean true if the tracker is begun successfully, false otherwise.
*/
public boolean begin(@NonNull Configuration.Builder builder) {
try {
- return beginInternal(builder.build());
+ final Configuration config = builder.build();
+ final TrackerResult result = new TrackerResult();
+ final boolean success = config.getHandler().runWithScissors(
+ () -> result.mResult = beginInternal(config), EXECUTOR_TASK_TIMEOUT);
+ if (!success) {
+ Log.d(TAG, "begin failed due to timeout, CUJ=" + getNameOfCuj(config.mCujType));
+ return false;
+ }
+ return result.mResult;
} catch (IllegalArgumentException ex) {
Log.d(TAG, "Build configuration failed!", ex);
return false;
}
}
+ @UiThread
private boolean beginInternal(@NonNull Configuration conf) {
- synchronized (mLock) {
- int cujType = conf.mCujType;
- if (!shouldMonitor(cujType)) return false;
- FrameTracker tracker = getTracker(cujType);
- // Skip subsequent calls if we already have an ongoing tracing.
- if (tracker != null) return false;
-
- // begin a new trace session.
- tracker = createFrameTracker(conf, new Session(cujType, conf.mTag));
- mRunningTrackers.put(cujType, tracker);
- tracker.begin();
-
- // Cancel the trace if we don't get an end() call in specified duration.
- scheduleTimeoutAction(
- cujType, conf.mTimeout, () -> cancel(cujType, REASON_CANCEL_TIMEOUT));
- return true;
- }
+ int cujType = conf.mCujType;
+ if (!shouldMonitor(cujType)) return false;
+ FrameTracker tracker = getTracker(cujType);
+ // Skip subsequent calls if we already have an ongoing tracing.
+ if (tracker != null) return false;
+
+ // begin a new trace session.
+ tracker = createFrameTracker(conf, new Session(cujType, conf.mTag));
+ putTracker(cujType, tracker);
+ tracker.begin();
+
+ // Cancel the trace if we don't get an end() call in specified duration.
+ scheduleTimeoutAction(
+ cujType, conf.mTimeout, () -> cancel(cujType, REASON_CANCEL_TIMEOUT));
+ return true;
}
/**
@@ -545,8 +571,10 @@ public class InteractionJankMonitor {
*/
@VisibleForTesting
public void scheduleTimeoutAction(@CujType int cuj, long timeout, Runnable action) {
- mTimeoutActions.put(cuj, action);
- mWorker.getThreadHandler().postDelayed(action, timeout);
+ synchronized (mLock) {
+ mTimeoutActions.put(cuj, action);
+ getTracker(cuj).getHandler().postDelayed(action, timeout);
+ }
}
/**
@@ -556,18 +584,35 @@ public class InteractionJankMonitor {
* @return boolean true if the tracker is ended successfully, false otherwise.
*/
public boolean end(@CujType int cujType) {
- synchronized (mLock) {
- // remove the timeout action first.
- removeTimeout(cujType);
- FrameTracker tracker = getTracker(cujType);
- // Skip this call since we haven't started a trace yet.
- if (tracker == null) return false;
- // if the end call doesn't return true, another thread is handling end of the cuj.
- if (tracker.end(REASON_END_NORMAL)) {
- removeTracker(cujType);
+ FrameTracker tracker = getTracker(cujType);
+ // Skip this call since we haven't started a trace yet.
+ if (tracker == null) return false;
+ try {
+ final TrackerResult result = new TrackerResult();
+ final boolean success = tracker.getHandler().runWithScissors(
+ () -> result.mResult = endInternal(cujType), EXECUTOR_TASK_TIMEOUT);
+ if (!success) {
+ Log.d(TAG, "end failed due to timeout, CUJ=" + getNameOfCuj(cujType));
+ return false;
}
- return true;
+ return result.mResult;
+ } catch (IllegalArgumentException ex) {
+ Log.d(TAG, "Execute end task failed!", ex);
+ return false;
+ }
+ }
+
+ @UiThread
+ private boolean endInternal(@CujType int cujType) {
+ // remove the timeout action first.
+ removeTimeout(cujType);
+ FrameTracker tracker = getTracker(cujType);
+ if (tracker == null) return false;
+ // if the end call doesn't return true, another thread is handling end of the cuj.
+ if (tracker.end(REASON_END_NORMAL)) {
+ removeTracker(cujType);
}
+ return true;
}
/**
@@ -586,39 +631,66 @@ public class InteractionJankMonitor {
*/
@VisibleForTesting
public boolean cancel(@CujType int cujType, @Reasons int reason) {
- synchronized (mLock) {
- // remove the timeout action first.
- removeTimeout(cujType);
- FrameTracker tracker = getTracker(cujType);
- // Skip this call since we haven't started a trace yet.
- if (tracker == null) return false;
- // if the cancel call doesn't return true, another thread is handling cancel of the cuj.
- if (tracker.cancel(reason)) {
- removeTracker(cujType);
+ FrameTracker tracker = getTracker(cujType);
+ // Skip this call since we haven't started a trace yet.
+ if (tracker == null) return false;
+ try {
+ final TrackerResult result = new TrackerResult();
+ final boolean success = tracker.getHandler().runWithScissors(
+ () -> result.mResult = cancelInternal(cujType, reason), EXECUTOR_TASK_TIMEOUT);
+ if (!success) {
+ Log.d(TAG, "cancel failed due to timeout, CUJ=" + getNameOfCuj(cujType));
+ return false;
}
- return true;
+ return result.mResult;
+ } catch (IllegalArgumentException ex) {
+ Log.d(TAG, "Execute cancel task failed!", ex);
+ return false;
+ }
+ }
+
+ @UiThread
+ private boolean cancelInternal(@CujType int cujType, @Reasons int reason) {
+ // remove the timeout action first.
+ removeTimeout(cujType);
+ FrameTracker tracker = getTracker(cujType);
+ if (tracker == null) return false;
+ // if the cancel call doesn't return true, another thread is handling cancel of the cuj.
+ if (tracker.cancel(reason)) {
+ removeTracker(cujType);
+ }
+ return true;
+ }
+
+ private void putTracker(@CujType int cuj, @NonNull FrameTracker tracker) {
+ synchronized (mLock) {
+ mRunningTrackers.put(cuj, tracker);
}
}
private FrameTracker getTracker(@CujType int cuj) {
- return mRunningTrackers.get(cuj);
+ synchronized (mLock) {
+ return mRunningTrackers.get(cuj);
+ }
}
private void removeTracker(@CujType int cuj) {
- mRunningTrackers.remove(cuj);
+ synchronized (mLock) {
+ mRunningTrackers.remove(cuj);
+ }
}
+ @WorkerThread
private void updateProperties(DeviceConfig.Properties properties) {
- synchronized (mLock) {
- mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
- DEFAULT_SAMPLING_INTERVAL);
- mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
- mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY,
- DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES);
- mTraceThresholdFrameTimeMillis = properties.getInt(
- SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY,
- DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS);
- }
+ mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
+ DEFAULT_SAMPLING_INTERVAL);
+ mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY,
+ DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES);
+ mTraceThresholdFrameTimeMillis = properties.getInt(
+ SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY,
+ DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS);
+ // The memory visibility is powered by the volatile field, mEnabled.
+ mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
}
@VisibleForTesting
@@ -780,10 +852,24 @@ public class InteractionJankMonitor {
return "BIOMETRIC_PROMPT_TRANSITION";
case CUJ_SETTINGS_TOGGLE:
return "SETTINGS_TOGGLE";
+ case CUJ_SHADE_DIALOG_OPEN:
+ return "SHADE_DIALOG_OPEN";
+ case CUJ_USER_DIALOG_OPEN:
+ return "USER_DIALOG_OPEN";
+ case CUJ_TASKBAR_EXPAND:
+ return "TASKBAR_EXPAND";
+ case CUJ_TASKBAR_COLLAPSE:
+ return "TASKBAR_COLLAPSE";
+ case CUJ_SHADE_CLEAR_ALL:
+ return "SHADE_CLEAR_ALL";
}
return "UNKNOWN";
}
+ private static class TrackerResult {
+ private boolean mResult;
+ }
+
/**
* Configurations used while instrumenting the CUJ. <br/>
* <b>It may refer to an attached view, don't use static reference for any purpose.</b>
@@ -797,6 +883,7 @@ public class InteractionJankMonitor {
private final SurfaceControl mSurfaceControl;
private final @CujType int mCujType;
private final boolean mDeferMonitor;
+ private final Handler mHandler;
/**
* A builder for building Configuration. {@link #setView(View)} is essential
@@ -940,6 +1027,7 @@ public class InteractionJankMonitor {
mSurfaceControl = surfaceControl;
mDeferMonitor = deferMonitor;
validate();
+ mHandler = mSurfaceOnly ? mContext.getMainThreadHandler() : mView.getHandler();
}
private void validate() {
@@ -988,20 +1076,25 @@ public class InteractionJankMonitor {
return mSurfaceControl;
}
- View getView() {
+ @VisibleForTesting
+ /**
+ * @return a view which is attached to the view tree.
+ */
+ public View getView() {
return mView;
}
- Context getContext() {
- return mContext;
- }
-
/**
* @return true if the monitoring should be deferred to the next frame, false otherwise.
*/
public boolean shouldDeferMonitor() {
return mDeferMonitor;
}
+
+ @VisibleForTesting
+ public Handler getHandler() {
+ return mHandler;
+ }
}
/**
@@ -1054,7 +1147,8 @@ public class InteractionJankMonitor {
mReason = reason;
}
- public @Reasons int getReason() {
+ @Reasons
+ public int getReason() {
return mReason;
}
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index ea5797d752d7..a352063aa6ee 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -42,6 +42,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
@@ -309,6 +310,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
R.integer.dock_enter_exit_duration);
mForceWindowDrawsBarBackgrounds = context.getResources().getBoolean(
R.bool.config_forceWindowDrawsStatusBarBackground)
+ && params.type != TYPE_INPUT_METHOD
&& context.getApplicationInfo().targetSdkVersion >= N;
mSemiTransparentBarColor = context.getResources().getColor(
R.color.system_bar_background_semi_transparent, null /* theme */);
@@ -1164,8 +1166,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
0 /* sideInset */, animate && !disallowAnimate,
mForceWindowDrawsBarBackgrounds, controller);
boolean oldDrawLegacy = mDrawLegacyNavigationBarBackground;
- mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible
- && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
+ mDrawLegacyNavigationBarBackground =
+ (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
mDrawLegacyNavigationBarBackgroundHandled =
mWindow.onDrawLegacyNavigationBarBackgroundChanged(
@@ -1206,7 +1208,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
boolean hideNavigation = (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
|| !(controller == null || controller.isRequestedVisible(ITYPE_NAVIGATION_BAR));
boolean decorFitsSystemWindows = mWindow.mDecorFitsSystemWindows;
- boolean forceConsumingNavBar = (mForceWindowDrawsBarBackgrounds
+ boolean forceConsumingNavBar =
+ ((mForceWindowDrawsBarBackgrounds || mDrawLegacyNavigationBarBackgroundHandled)
&& (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
&& (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
&& decorFitsSystemWindows
@@ -1437,8 +1440,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
int size, boolean verticalBar, boolean seascape, int sideMargin, boolean animate,
boolean force, WindowInsetsController controller) {
state.present = state.attributes.isPresent(
- controller.isRequestedVisible(state.attributes.insetsType),
- mWindow.getAttributes().flags, force);
+ (controller.isRequestedVisible(state.attributes.insetsType)
+ || mLastShouldAlwaysConsumeSystemBars),
+ mWindow.getAttributes().flags, force);
boolean show = state.attributes.isVisible(state.present, color,
mWindow.getAttributes().flags, force);
boolean showView = show && !isResizing() && !mHasCaption && size > 0;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 9c0fad902a52..fb38bba8ee16 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -915,6 +915,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
}
+ if (!st.hasPanelItems()) {
+ // Ensure that |st.decorView| has its actual content. Otherwise, an empty window can be
+ // created and cause ANR.
+ return;
+ }
+
st.isHandled = false;
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 377dfd0fb137..295dc545ef4b 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -25,6 +25,7 @@ import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_UNSET;
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -258,11 +259,17 @@ public class TransitionAnimation {
}
return null;
}
-
- /** Load animation by attribute Id from a specific AnimationStyle resource. */
+ /**
+ * Load animation by attribute Id from a specific AnimationStyle resource.
+ *
+ * @param translucent {@code true} if we're sure that the animation is applied on a translucent
+ * window container, {@code false} otherwise.
+ * @param transit {@link TransitionOldType} for the app transition of this animation, or
+ * {@link TransitionOldType#TRANSIT_OLD_UNSET} if app transition type is unknown.
+ */
@Nullable
- public Animation loadAnimationAttr(String packageName, int animStyleResId, int animAttr,
- boolean translucent) {
+ private Animation loadAnimationAttr(String packageName, int animStyleResId, int animAttr,
+ boolean translucent, @TransitionOldType int transit) {
if (animStyleResId == 0) {
return null;
}
@@ -278,6 +285,8 @@ public class TransitionAnimation {
}
if (translucent) {
resId = updateToTranslucentAnimIfNeeded(resId);
+ } else if (transit != TRANSIT_OLD_UNSET) {
+ resId = updateToTranslucentAnimIfNeeded(resId, transit);
}
if (ResourceId.isValid(resId)) {
return loadAnimationSafely(context, resId, mTag);
@@ -285,6 +294,15 @@ public class TransitionAnimation {
return null;
}
+
+ /** Load animation by attribute Id from a specific AnimationStyle resource. */
+ @Nullable
+ public Animation loadAnimationAttr(String packageName, int animStyleResId, int animAttr,
+ boolean translucent) {
+ return loadAnimationAttr(packageName, animStyleResId, animAttr, translucent,
+ TRANSIT_OLD_UNSET);
+ }
+
/** Load animation by attribute Id from android package. */
@Nullable
public Animation loadDefaultAnimationAttr(int animAttr, boolean translucent) {
@@ -292,6 +310,13 @@ public class TransitionAnimation {
translucent);
}
+ /** Load animation by attribute Id from android package. */
+ @Nullable
+ public Animation loadDefaultAnimationAttr(int animAttr, @TransitionOldType int transit) {
+ return loadAnimationAttr(DEFAULT_PACKAGE, mDefaultWindowAnimationStyleResId, animAttr,
+ false /* translucent */, transit);
+ }
+
@Nullable
private AttributeCache.Entry getCachedAnimations(LayoutParams lp) {
if (mDebug) {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index e93a7854f1cd..65bec0e405ea 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -33,6 +33,7 @@ import android.view.InsetsVisibilities;
import com.android.internal.statusbar.IAddTileResultCallback;
import com.android.internal.statusbar.IUndoMediaTransferCallback;
+import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.view.AppearanceRegion;
@@ -202,10 +203,12 @@ oneway interface IStatusBar
* @param behavior the behavior of the focused window.
* @param requestedVisibilities the collection of the requested visibilities of system insets.
* @param packageName the package name of the focused app.
+ * @param letterboxDetails a set of letterbox details of apps visible on the screen.
*/
void onSystemBarAttributesChanged(int displayId, int appearance,
in AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- int behavior, in InsetsVisibilities requestedVisibilities, String packageName);
+ int behavior, in InsetsVisibilities requestedVisibilities, String packageName,
+ in LetterboxDetails[] letterboxDetails);
/**
* Notifies System UI to show transient bars. The transient bars are system bars, e.g., status
diff --git a/core/java/com/android/internal/statusbar/LetterboxDetails.aidl b/core/java/com/android/internal/statusbar/LetterboxDetails.aidl
new file mode 100644
index 000000000000..7875796cd3ba
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/LetterboxDetails.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.statusbar;
+
+parcelable LetterboxDetails;
diff --git a/core/java/com/android/internal/statusbar/LetterboxDetails.java b/core/java/com/android/internal/statusbar/LetterboxDetails.java
new file mode 100644
index 000000000000..5d14ee347482
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/LetterboxDetails.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.statusbar;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.InsetsFlags;
+import android.view.ViewDebug;
+import android.view.WindowInsetsController.Appearance;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Details about the letterbox state of an app.
+ */
+@DataClass(
+ genParcelable = true,
+ genAidl = true,
+ genToString = true,
+ genGetters = false,
+ genEqualsHashCode = true
+)
+public class LetterboxDetails implements Parcelable {
+
+ @NonNull
+ private final Rect mLetterboxInnerBounds;
+ @NonNull
+ private final Rect mLetterboxFullBounds;
+ private final int mAppAppearance;
+
+ /**
+ * Returns the bounds of the inner letterbox (app content).
+ *
+ * <p>When an app is letterboxed, it is not using the full bounds of its window. Here we return
+ * the bounds that are being used for the app content.
+ *
+ * <pre>
+ * +-------+---------+-------+
+ * | | | |
+ * | | | |
+ * | Outer | Inner | Outer |
+ * | | | |
+ * | | | |
+ * +-------+-------- +-------+
+ * <pre>
+ */
+ @NonNull
+ public Rect getLetterboxInnerBounds() {
+ return mLetterboxInnerBounds;
+ }
+
+ /**
+ * Returns the full bounds of the letterbox.
+ *
+ * <p>These are the entire bounds of the window where the app is placed. We cannot assume that
+ * the full bounds are the bounds of the screen, as the app can be in split-screen, or can have
+ * some margin due to display cutouts.
+ *
+ * <pre>
+ * ---- Full bounds width ----
+ * +-------+---------+-------+ |
+ * | | | | |
+ * | | | | |
+ * | Outer | Inner | Outer | + Full bounds height
+ * | | | | |
+ * | | | | |
+ * +-------+-------- +-------+ |
+ * </pre>
+ */
+ @NonNull
+ public Rect getLetterboxFullBounds() {
+ return mLetterboxFullBounds;
+ }
+
+ /**
+ * Returns the {@link Appearance} of the inner letterbox (app content).
+ */
+ @Appearance
+ public int getAppAppearance() {
+ return mAppAppearance;
+ }
+
+ /** Returns a string representation of the {@link #getAppAppearance()} property. */
+ public String appAppearanceToString() {
+ return ViewDebug.flagsToString(InsetsFlags.class, "appearance", mAppAppearance);
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/statusbar/LetterboxDetails.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public LetterboxDetails(
+ @NonNull Rect letterboxInnerBounds,
+ @NonNull Rect letterboxFullBounds,
+ int appAppearance) {
+ this.mLetterboxInnerBounds = letterboxInnerBounds;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mLetterboxInnerBounds);
+ this.mLetterboxFullBounds = letterboxFullBounds;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mLetterboxFullBounds);
+ this.mAppAppearance = appAppearance;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "LetterboxDetails { " +
+ "letterboxInnerBounds = " + mLetterboxInnerBounds + ", " +
+ "letterboxFullBounds = " + mLetterboxFullBounds + ", " +
+ "appAppearance = " + appAppearanceToString() +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(LetterboxDetails other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ LetterboxDetails that = (LetterboxDetails) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Objects.equals(mLetterboxInnerBounds, that.mLetterboxInnerBounds)
+ && java.util.Objects.equals(mLetterboxFullBounds, that.mLetterboxFullBounds)
+ && mAppAppearance == that.mAppAppearance;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mLetterboxInnerBounds);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mLetterboxFullBounds);
+ _hash = 31 * _hash + mAppAppearance;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeTypedObject(mLetterboxInnerBounds, flags);
+ dest.writeTypedObject(mLetterboxFullBounds, flags);
+ dest.writeInt(mAppAppearance);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected LetterboxDetails(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ Rect letterboxInnerBounds = (Rect) in.readTypedObject(Rect.CREATOR);
+ Rect letterboxFullBounds = (Rect) in.readTypedObject(Rect.CREATOR);
+ int appAppearance = in.readInt();
+
+ this.mLetterboxInnerBounds = letterboxInnerBounds;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mLetterboxInnerBounds);
+ this.mLetterboxFullBounds = letterboxFullBounds;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mLetterboxFullBounds);
+ this.mAppAppearance = appAppearance;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<LetterboxDetails> CREATOR
+ = new Parcelable.Creator<LetterboxDetails>() {
+ @Override
+ public LetterboxDetails[] newArray(int size) {
+ return new LetterboxDetails[size];
+ }
+
+ @Override
+ public LetterboxDetails createFromParcel(@NonNull Parcel in) {
+ return new LetterboxDetails(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1656941109526L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/com/android/internal/statusbar/LetterboxDetails.java",
+ inputSignatures = "private final @android.annotation.NonNull android.graphics.Rect mLetterboxInnerBounds\nprivate final @android.annotation.NonNull android.graphics.Rect mLetterboxFullBounds\nprivate final int mAppAppearance\npublic @android.annotation.NonNull android.graphics.Rect getLetterboxInnerBounds()\npublic @android.annotation.NonNull android.graphics.Rect getLetterboxFullBounds()\npublic @android.view.WindowInsetsController.Appearance int getAppAppearance()\npublic java.lang.String appAppearanceToString()\nclass LetterboxDetails extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genToString=true, genGetters=false, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
index 4dcc82e2e572..8b898f01554f 100644
--- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
+++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
@@ -43,12 +43,14 @@ public final class RegisterStatusBarResult implements Parcelable {
public final InsetsVisibilities mRequestedVisibilities;
public final String mPackageName;
public final int[] mTransientBarTypes;
+ public final LetterboxDetails[] mLetterboxDetails;
public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1,
int appearance, AppearanceRegion[] appearanceRegions, int imeWindowVis,
int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2, IBinder imeToken,
boolean navbarColorManagedByIme, int behavior, InsetsVisibilities requestedVisibilities,
- String packageName, @NonNull int[] transientBarTypes) {
+ String packageName, @NonNull int[] transientBarTypes,
+ LetterboxDetails[] letterboxDetails) {
mIcons = new ArrayMap<>(icons);
mDisabledFlags1 = disabledFlags1;
mAppearance = appearance;
@@ -63,6 +65,7 @@ public final class RegisterStatusBarResult implements Parcelable {
mRequestedVisibilities = requestedVisibilities;
mPackageName = packageName;
mTransientBarTypes = transientBarTypes;
+ mLetterboxDetails = letterboxDetails;
}
@Override
@@ -86,6 +89,7 @@ public final class RegisterStatusBarResult implements Parcelable {
dest.writeTypedObject(mRequestedVisibilities, 0);
dest.writeString(mPackageName);
dest.writeIntArray(mTransientBarTypes);
+ dest.writeParcelableArray(mLetterboxDetails, flags);
}
/**
@@ -112,10 +116,13 @@ public final class RegisterStatusBarResult implements Parcelable {
source.readTypedObject(InsetsVisibilities.CREATOR);
final String packageName = source.readString();
final int[] transientBarTypes = source.createIntArray();
+ final LetterboxDetails[] letterboxDetails =
+ source.readParcelableArray(null, LetterboxDetails.class);
return new RegisterStatusBarResult(icons, disabledFlags1, appearance,
appearanceRegions, imeWindowVis, imeBackDisposition, showImeSwitcher,
disabledFlags2, imeToken, navbarColorManagedByIme, behavior,
- requestedVisibilities, packageName, transientBarTypes);
+ requestedVisibilities, packageName, transientBarTypes,
+ letterboxDetails);
}
@Override
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 1cdc10866f2c..af9c5a5cc0d5 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -53,7 +53,7 @@ public class ResolverDrawerLayout extends ViewGroup {
/**
* Max width of the whole drawer layout
*/
- private int mMaxWidth;
+ private final int mMaxWidth;
/**
* Max total visible height of views not marked always-show when in the closed/initial state
@@ -187,8 +187,10 @@ public class ResolverDrawerLayout extends ViewGroup {
}
public void setSmallCollapsed(boolean smallCollapsed) {
- mSmallCollapsed = smallCollapsed;
- requestLayout();
+ if (mSmallCollapsed != smallCollapsed) {
+ mSmallCollapsed = smallCollapsed;
+ requestLayout();
+ }
}
public boolean isSmallCollapsed() {
@@ -200,9 +202,10 @@ public class ResolverDrawerLayout extends ViewGroup {
}
public void setShowAtTop(boolean showOnTop) {
- mShowAtTop = showOnTop;
- invalidate();
- requestLayout();
+ if (mShowAtTop != showOnTop) {
+ mShowAtTop = showOnTop;
+ requestLayout();
+ }
}
public boolean getShowAtTop() {
@@ -220,6 +223,9 @@ public class ResolverDrawerLayout extends ViewGroup {
public void setCollapsibleHeightReserved(int heightPixels) {
final int oldReserved = mCollapsibleHeightReserved;
mCollapsibleHeightReserved = heightPixels;
+ if (oldReserved != mCollapsibleHeightReserved) {
+ requestLayout();
+ }
final int dReserved = mCollapsibleHeightReserved - oldReserved;
if (dReserved != 0 && mIsDragging) {
@@ -255,7 +261,7 @@ public class ResolverDrawerLayout extends ViewGroup {
if (getShowAtTop()) {
// Keep the drawer fully open.
- mCollapseOffset = 0;
+ setCollapseOffset(0);
return false;
}
@@ -264,9 +270,9 @@ public class ResolverDrawerLayout extends ViewGroup {
if (remainClosed && (oldCollapsibleHeight < mCollapsibleHeight
&& mCollapseOffset == oldCollapsibleHeight)) {
// Stay closed even at the new height.
- mCollapseOffset = mCollapsibleHeight;
+ setCollapseOffset(mCollapsibleHeight);
} else {
- mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight);
+ setCollapseOffset(Math.min(mCollapseOffset, mCollapsibleHeight));
}
final boolean isCollapsedNew = mCollapseOffset != 0;
if (isCollapsedOld != isCollapsedNew) {
@@ -274,11 +280,18 @@ public class ResolverDrawerLayout extends ViewGroup {
}
} else {
// Start out collapsed at first unless we restored state for otherwise
- mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight;
+ setCollapseOffset(mOpenOnLayout ? 0 : mCollapsibleHeight);
}
return true;
}
+ private void setCollapseOffset(float collapseOffset) {
+ if (mCollapseOffset != collapseOffset) {
+ mCollapseOffset = collapseOffset;
+ requestLayout();
+ }
+ }
+
private int getMaxCollapsedHeight() {
return (isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight)
+ mCollapsibleHeightReserved;
@@ -420,8 +433,7 @@ public class ResolverDrawerLayout extends ViewGroup {
case MotionEvent.ACTION_POINTER_DOWN: {
final int pointerIndex = ev.getActionIndex();
- final int pointerId = ev.getPointerId(pointerIndex);
- mActivePointerId = pointerId;
+ mActivePointerId = ev.getPointerId(pointerIndex);
mInitialTouchX = ev.getX(pointerIndex);
mInitialTouchY = mLastTouchY = ev.getY(pointerIndex);
}
@@ -924,7 +936,7 @@ public class ResolverDrawerLayout extends ViewGroup {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int sourceWidth = MeasureSpec.getSize(widthMeasureSpec);
int widthSize = sourceWidth;
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// Single-use layout; just ignore the mode and use available space.
// Clamp to maxWidth.
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index 4af28ea24361..1520ea5c6831 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -41,7 +41,12 @@ struct {
static JNIEnv* getenv(JavaVM* vm) {
JNIEnv* env;
- if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ auto result = vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+ if (result == JNI_EDETACHED) {
+ if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
+ LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
+ }
+ } else if (result != JNI_OK) {
LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
}
return env;
@@ -60,28 +65,22 @@ public:
}
~TransactionHangCallbackWrapper() {
- if (mTransactionHangObject) {
- getenv()->DeleteGlobalRef(mTransactionHangObject);
+ if (mTransactionHangObject != nullptr) {
+ getenv(mVm)->DeleteGlobalRef(mTransactionHangObject);
mTransactionHangObject = nullptr;
}
}
void onTransactionHang(bool isGpuHang) {
if (mTransactionHangObject) {
- getenv()->CallVoidMethod(mTransactionHangObject,
- gTransactionHangCallback.onTransactionHang, isGpuHang);
+ getenv(mVm)->CallVoidMethod(mTransactionHangObject,
+ gTransactionHangCallback.onTransactionHang, isGpuHang);
}
}
private:
JavaVM* mVm;
jobject mTransactionHangObject;
-
- JNIEnv* getenv() {
- JNIEnv* env;
- mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
- return env;
- }
};
static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName,
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index f388fec2cd78..b1610d790222 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -580,6 +580,11 @@ static void nativeSetEarlyWakeupEnd(JNIEnv* env, jclass clazz, jlong transaction
transaction->setEarlyWakeupEnd();
}
+static jlong nativeGetTransactionId(JNIEnv* env, jclass clazz, jlong transactionObj) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ return transaction->getId();
+}
+
static void nativeSetLayer(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint zorder) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -2103,6 +2108,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetEarlyWakeupStart },
{"nativeSetEarlyWakeupEnd", "(J)V",
(void*)nativeSetEarlyWakeupEnd },
+ {"nativeGetTransactionId", "(J)J",
+ (void*)nativeGetTransactionId },
{"nativeSetLayer", "(JJI)V",
(void*)nativeSetLayer },
{"nativeSetRelativeLayer", "(JJJI)V",
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 152d729da3b6..505ef30949ce 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -116,6 +116,7 @@ message KeyguardControllerProto {
repeated KeyguardOccludedProto keyguard_occluded_states = 2 [deprecated=true];
optional bool aod_showing = 3;
repeated KeyguardPerDisplayProto keyguard_per_display = 4;
+ optional bool keyguard_going_away = 5;
}
message KeyguardOccludedProto {
@@ -132,6 +133,7 @@ message KeyguardPerDisplayProto {
optional bool keyguard_showing = 2;
optional bool aod_showing = 3;
optional bool keyguard_occluded = 4;
+ optional bool keyguard_going_away = 5;
}
/* represents PhoneWindowManager */
@@ -576,7 +578,7 @@ message InsetsSourceProviderProto {
optional WindowStateProto pending_control_target = 6;
optional WindowStateProto fake_control_target = 7;
optional .android.view.SurfaceControlProto captured_leash = 8;
- optional .android.graphics.RectProto ime_overridden_frame = 9;
+ optional .android.graphics.RectProto ime_overridden_frame = 9 [deprecated=true];
optional bool is_leash_ready_for_dispatching = 10;
optional bool client_visible = 11;
optional bool server_visible = 12;
diff --git a/core/proto/android/server/windowmanagertransitiontrace.proto b/core/proto/android/server/windowmanagertransitiontrace.proto
new file mode 100644
index 000000000000..4161f6554fb6
--- /dev/null
+++ b/core/proto/android/server/windowmanagertransitiontrace.proto
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package com.android.server.wm.shell;
+
+import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
+
+option java_multiple_files = true;
+
+/* Represents a file full of transition entries.
+ Encoded, it should start with 0x9 0x57 0x49 0x4e 0x54 0x52 0x41 0x43 0x45 (.TRNTRACE), such
+ that it can be easily identified. */
+message TransitionTraceProto {
+
+ /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
+ (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
+ constants into .proto files. */
+ enum MagicNumber {
+ INVALID = 0;
+ MAGIC_NUMBER_L = 0x544e5254; /* TRNT (little-endian ASCII) */
+ MAGIC_NUMBER_H = 0x45434152; /* RACE (little-endian ASCII) */
+ }
+
+ fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */
+ int64 timestamp = 2; /* The timestamp of when the trace was started. */
+ repeated Transition transition = 3;
+}
+
+message Transition {
+
+ enum State {
+ COLLECTING = 0;
+ PENDING = -1;
+ STARTED = 1;
+ PLAYING = 2;
+ ABORT = 3;
+ FINISHED = 4;
+ }
+
+ int32 id = 1;
+ int32 transition_type = 2;
+ int64 timestamp = 3;
+ State state = 5;
+ int32 flags = 6;
+ repeated ChangeInfo change = 7;
+ uint64 start_transaction_id = 8;
+ uint64 finish_transaction_id = 9;
+}
+
+message ChangeInfo {
+ com.android.server.wm.IdentifierProto window_identifier = 1;
+ int32 transit_mode = 2;
+ bool has_changed = 3;
+ int32 change_flags = 4;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7439b2f0921f..5e99d6c41029 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6498,6 +6498,13 @@
<permission android:name="android.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an app to set gesture exclusion without restrictions on the vertical extent of the
+ exclusions (see {@link android.view.View#setSystemGestureExclusionRects}).
+ @hide
+ -->
+ <permission android:name="android.permission.SET_UNRESTRICTED_GESTURE_EXCLUSION"
+ android:protectionLevel="signature|privileged|recents" />
+
<!-- @SystemApi Allows TV input apps and TV apps to use TIS extension interfaces for
domain-specific features.
<p>Protection level: signature|privileged|vendorPrivileged
diff --git a/core/res/res/anim-ldrtl/task_fragment_clear_top_close_enter.xml b/core/res/res/anim-ldrtl/task_fragment_clear_top_close_enter.xml
new file mode 100644
index 000000000000..6c44bdc04e9e
--- /dev/null
+++ b/core/res/res/anim-ldrtl/task_fragment_clear_top_close_enter.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:showBackdrop="true">
+ <alpha
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/linear"
+ android:startOffset="100"
+ android:duration="350" />
+
+ <translate
+ android:fromXDelta="5%"
+ android:toXDelta="0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="450" />
+</set> \ No newline at end of file
diff --git a/core/res/res/anim-ldrtl/task_fragment_clear_top_close_exit.xml b/core/res/res/anim-ldrtl/task_fragment_clear_top_close_exit.xml
new file mode 100644
index 000000000000..65cf2c2c74fc
--- /dev/null
+++ b/core/res/res/anim-ldrtl/task_fragment_clear_top_close_exit.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/linear"
+ android:startOffset="0"
+ android:duration="100" />
+
+ <translate
+ android:fromXDelta="0"
+ android:toXDelta="-25%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="450" />
+</set> \ No newline at end of file
diff --git a/core/res/res/anim-ldrtl/task_fragment_clear_top_open_enter.xml b/core/res/res/anim-ldrtl/task_fragment_clear_top_open_enter.xml
new file mode 100644
index 000000000000..022bc22b998b
--- /dev/null
+++ b/core/res/res/anim-ldrtl/task_fragment_clear_top_open_enter.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:showBackdrop="true">
+ <alpha
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/linear"
+ android:startOffset="100"
+ android:duration="350" />
+
+ <translate
+ android:fromXDelta="-5%"
+ android:toXDelta="0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="450" />
+</set> \ No newline at end of file
diff --git a/core/res/res/anim-ldrtl/task_fragment_clear_top_open_exit.xml b/core/res/res/anim-ldrtl/task_fragment_clear_top_open_exit.xml
new file mode 100644
index 000000000000..3644dea7da90
--- /dev/null
+++ b/core/res/res/anim-ldrtl/task_fragment_clear_top_open_exit.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/standard_accelerate"
+ android:startOffset="0"
+ android:duration="100" />
+
+ <translate
+ android:fromXDelta="0"
+ android:toXDelta="25%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="450" />
+</set> \ No newline at end of file
diff --git a/core/res/res/anim-ldrtl/task_fragment_open_enter.xml b/core/res/res/anim-ldrtl/task_fragment_open_enter.xml
index b6f1af3e7840..9e3780a764d3 100644
--- a/core/res/res/anim-ldrtl/task_fragment_open_enter.xml
+++ b/core/res/res/anim-ldrtl/task_fragment_open_enter.xml
@@ -17,7 +17,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
+ android:shareInterpolator="false">
<alpha
android:fromAlpha="0"
diff --git a/core/res/res/anim/task_fragment_clear_top_close_enter.xml b/core/res/res/anim/task_fragment_clear_top_close_enter.xml
new file mode 100644
index 000000000000..e33f718d33cc
--- /dev/null
+++ b/core/res/res/anim/task_fragment_clear_top_close_enter.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:showBackdrop="true">
+ <alpha
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/standard_decelerate"
+ android:startOffset="100"
+ android:duration="350" />
+
+ <translate
+ android:fromXDelta="-5%"
+ android:toXDelta="0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="450" />
+
+</set> \ No newline at end of file
diff --git a/core/res/res/anim/task_fragment_clear_top_close_exit.xml b/core/res/res/anim/task_fragment_clear_top_close_exit.xml
new file mode 100644
index 000000000000..3d274ba9cd82
--- /dev/null
+++ b/core/res/res/anim/task_fragment_clear_top_close_exit.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/standard_accelerate"
+ android:startOffset="0"
+ android:duration="100" />
+
+ <translate
+ android:fromXDelta="0"
+ android:toXDelta="25%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="450" />
+
+</set> \ No newline at end of file
diff --git a/core/res/res/anim/task_fragment_clear_top_open_enter.xml b/core/res/res/anim/task_fragment_clear_top_open_enter.xml
new file mode 100644
index 000000000000..b9636616c35d
--- /dev/null
+++ b/core/res/res/anim/task_fragment_clear_top_open_enter.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:showBackdrop="true">
+
+ <alpha
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/standard_decelerate"
+ android:startOffset="100"
+ android:duration="350" />
+
+ <translate
+ android:fromXDelta="5%"
+ android:toXDelta="0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:startOffset="0"
+ android:duration="450" />
+
+</set> \ No newline at end of file
diff --git a/core/res/res/anim/task_fragment_clear_top_open_exit.xml b/core/res/res/anim/task_fragment_clear_top_open_exit.xml
new file mode 100644
index 000000000000..22be7d1004eb
--- /dev/null
+++ b/core/res/res/anim/task_fragment_clear_top_open_exit.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/standard_accelerate"
+ android:startOffset="0"
+ android:duration="100" />
+
+ <translate
+ android:fromXDelta="0"
+ android:toXDelta="-25%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="450" />
+
+</set> \ No newline at end of file
diff --git a/core/res/res/anim/task_fragment_open_enter.xml b/core/res/res/anim/task_fragment_open_enter.xml
index aa61e6f17070..87ee17925ca7 100644
--- a/core/res/res/anim/task_fragment_open_enter.xml
+++ b/core/res/res/anim/task_fragment_open_enter.xml
@@ -16,7 +16,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false">
+ android:shareInterpolator="false">
<alpha
android:fromAlpha="0"
android:toAlpha="1.0"
diff --git a/core/res/res/anim/task_fragment_open_exit.xml b/core/res/res/anim/task_fragment_open_exit.xml
index b4914d21d097..55a472db750d 100644
--- a/core/res/res/anim/task_fragment_open_exit.xml
+++ b/core/res/res/anim/task_fragment_open_exit.xml
@@ -32,5 +32,5 @@
android:fillBefore="true"
android:fillAfter="true"
android:interpolator="@interpolator/fast_out_extra_slow_in"
- android:duration="400" />
-</set> \ No newline at end of file
+ android:duration="400"/>
+</set>
diff --git a/core/res/res/drawable-nodpi/default_wallpaper.png b/core/res/res/drawable-nodpi/default_wallpaper.png
index 5152972d2a80..a23f5539cca7 100644
--- a/core/res/res/drawable-nodpi/default_wallpaper.png
+++ b/core/res/res/drawable-nodpi/default_wallpaper.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png b/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png
index 26376fb87cbe..1e272e06221c 100644
--- a/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png
+++ b/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png
Binary files differ
diff --git a/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png b/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png
index 490ebeeb75c1..d10c77d2f7f3 100644
--- a/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png
+++ b/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png
Binary files differ
diff --git a/core/res/res/layout-car/car_alert_dialog.xml b/core/res/res/layout-car/car_alert_dialog.xml
index 2e7b62ceb723..3c7a5c578fc5 100644
--- a/core/res/res/layout-car/car_alert_dialog.xml
+++ b/core/res/res/layout-car/car_alert_dialog.xml
@@ -54,7 +54,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/text_view_start_margin"
android:layout_marginEnd="@dimen/text_view_end_margin"
- style="@style/CarBody4"/>
+ style="@style/CarDialogMessageText"/>
<!-- we don't need this spacer, but the id needs to be here for compatibility -->
<Space
diff --git a/core/res/res/layout-car/car_alert_dialog_button_bar.xml b/core/res/res/layout-car/car_alert_dialog_button_bar.xml
index 4f815b887088..43fd1eb4c25f 100644
--- a/core/res/res/layout-car/car_alert_dialog_button_bar.xml
+++ b/core/res/res/layout-car/car_alert_dialog_button_bar.xml
@@ -16,13 +16,13 @@
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/buttonPanel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:scrollbarAlwaysDrawVerticalTrack="true"
- android:scrollIndicators="top|bottom"
- android:fillViewport="true"
- style="?attr/buttonBarStyle">
+ android:id="@+id/buttonPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbarAlwaysDrawVerticalTrack="true"
+ android:scrollIndicators="top|bottom"
+ android:fillViewport="true"
+ style="?attr/buttonBarStyle">
<com.android.internal.widget.ButtonBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -35,7 +35,7 @@
<Button
android:id="@+id/button3"
- style="@style/CarAction1"
+ style="@style/CarDialogButtonText"
android:minWidth="@dimen/car_touch_target_size"
android:paddingStart="@dimen/car_padding_2"
android:paddingEnd="@dimen/car_padding_2"
@@ -46,7 +46,7 @@
<Button
android:id="@+id/button2"
- style="@style/CarAction1"
+ style="@style/CarDialogButtonText"
android:minWidth="@dimen/car_touch_target_size"
android:paddingStart="@dimen/car_padding_2"
android:paddingEnd="@dimen/car_padding_2"
@@ -57,7 +57,7 @@
<Button
android:id="@+id/button1"
- style="@style/CarAction1"
+ style="@style/CarDialogButtonText"
android:minWidth="@dimen/car_touch_target_size"
android:paddingStart="@dimen/car_padding_2"
android:paddingEnd="@dimen/car_padding_2"
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index d32f10a817fc..e673a8b8946d 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -623,7 +623,7 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Arazo bat izan da. Saiatu berriro."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Hatz-markaren ikonoa"</string>
- <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Aurpegi bidez desblokeatzea"</string>
+ <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Aurpegi bidez desblokeatzeko eginbidea"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Arazoak ditugu aurpegi bidez desblokeatzeko eginbidearekin"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Sakatu hau aurpegi-eredua ezabatzeko eta, gero, gehitu aurpegia berriro"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfiguratu aurpegi bidez desblokeatzeko eginbidea"</string>
@@ -993,7 +993,7 @@
<string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Zabaldu desblokeatzeko eremua."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Hatza lerratuta desblokeatzea."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Ereduaren bidez desblokeatzea."</string>
- <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"Aurpegi bidez desblokeatzea."</string>
+ <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"Aurpegi bidez desblokeatzeko eginbidea."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"PIN kodearen bidez desblokeatzea."</string>
<string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIMa desblokeatzeko PINa."</string>
<string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM txartela desblokeatzeko PUK kodea."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 33b8af65390f..9645208a15a7 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -2086,7 +2086,7 @@
<string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"A tableta non ten suficiente batería. Xa non se restrinxirán as funcións."</string>
<string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"O dispositivo non ten suficiente batería. Xa non se restrinxirán as funcións."</string>
<string name="mime_type_folder" msgid="2203536499348787650">"Cartafol"</string>
- <string name="mime_type_apk" msgid="3168784749499623902">"Aplicación para Android"</string>
+ <string name="mime_type_apk" msgid="3168784749499623902">"Aplicación Android"</string>
<string name="mime_type_generic" msgid="4606589110116560228">"Ficheiro"</string>
<string name="mime_type_generic_ext" msgid="9220220924380909486">"Ficheiro <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
<string name="mime_type_audio" msgid="4933450584432509875">"Audio"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index c78c6f69643f..37536fb9dacd 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -641,8 +641,8 @@
<string name="face_acquired_too_far" msgid="2922278214231064859">"फ़ोन को नज़दीक लाएं"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"फ़ोन को थोड़ा और ऊपर ले जाएं"</string>
<string name="face_acquired_too_low" msgid="4075391872960840081">"फ़ोन को थोड़ा नीचे ले जाएं"</string>
- <string name="face_acquired_too_right" msgid="6245286514593540859">"फ़ोन को अपने बाईं ओर ले जाएं"</string>
- <string name="face_acquired_too_left" msgid="9201762240918405486">"फ़ोन को अपने दाईं ओर ले जाएं"</string>
+ <string name="face_acquired_too_right" msgid="6245286514593540859">"फ़ोन को अपनी बाईं ओर ले जाएं"</string>
+ <string name="face_acquired_too_left" msgid="9201762240918405486">"फ़ोन को अपनी दाईं ओर ले जाएं"</string>
<string name="face_acquired_poor_gaze" msgid="4427153558773628020">"कृपया अपने डिवाइस की तरफ़ सीधे देखें."</string>
<string name="face_acquired_not_detected" msgid="1057966913397548150">"आपका चेहरा नहीं दिख रहा है. फ़ोन को अपनी आंखों की सीध में पकड़कर रखें."</string>
<string name="face_acquired_too_much_motion" msgid="8199691445085189528">"डिवाइस बहुत ज़्यादा हिल रहा है. फ़ोन को बिना हिलाएं पकड़ें."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 1b2138bbf97f..5241ddaceea6 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -641,8 +641,8 @@
<string name="face_acquired_too_far" msgid="2922278214231064859">"Avvicina il telefono"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Sposta il telefono più in alto"</string>
<string name="face_acquired_too_low" msgid="4075391872960840081">"Sposta il telefono più in basso"</string>
- <string name="face_acquired_too_right" msgid="6245286514593540859">"Sposta il telefono verso sinistra"</string>
- <string name="face_acquired_too_left" msgid="9201762240918405486">"Sposta il telefono verso destra"</string>
+ <string name="face_acquired_too_right" msgid="6245286514593540859">"Sposta il telefono alla tua sinistra"</string>
+ <string name="face_acquired_too_left" msgid="9201762240918405486">"Sposta il telefono alla tua destra"</string>
<string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Guarda più direttamente verso il dispositivo."</string>
<string name="face_acquired_not_detected" msgid="1057966913397548150">"Impossibile vedere il volto. Tieni il telefono all\'altezza degli occhi."</string>
<string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Troppo movimento. Tieni fermo il telefono."</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 90da15f2c282..8d8d792ce008 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -639,7 +639,7 @@
<string name="face_acquired_too_dark" msgid="7919016380747701228">"ಪ್ರಕಾಶಮಾನವಾದ ಲೈಟಿಂಗ್ ಬಳಸಿ"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ಫೋನ್ ಅನ್ನು ದೂರಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ಫೋನ್ ಅನ್ನು ಸಮೀಪಕ್ಕೆ ತನ್ನಿ"</string>
- <string name="face_acquired_too_high" msgid="8278815780046368576">"ಫೋನ್ ಅನ್ನು ಮೇಲಕ್ಕೆ ಎತ್ತಿ ಹಿಡಿಯಿರಿ."</string>
+ <string name="face_acquired_too_high" msgid="8278815780046368576">"ಫೋನ್ ಅನ್ನು ಎತ್ತರಕ್ಕೆ ಹಿಡಿಯಿರಿ"</string>
<string name="face_acquired_too_low" msgid="4075391872960840081">"ಫೋನ್ ಅನ್ನು ಕೆಳಗೆ ಸರಿಸಿ"</string>
<string name="face_acquired_too_right" msgid="6245286514593540859">"ಫೋನ್ ಅನ್ನು ನಿಮ್ಮ ಎಡಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="face_acquired_too_left" msgid="9201762240918405486">"ಫೋನ್ ಅನ್ನು ನಿಮ್ಮ ಬಲಕ್ಕೆ ಸರಿಸಿ"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index baa5103120be..57672a245f9d 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -626,7 +626,7 @@
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Жүзүнөн таанып ачуу"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Жүзүнөн таанып ачуу функциясында маселе келип чыкты"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Жүзүңүздүн үлгүсүн өчүрүү үчүн басып, жаңы үлгүнү кошуңуз"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Жүзүнөн таанып ачууну тууралоо"</string>
+ <string name="face_setup_notification_title" msgid="8843461561970741790">"Жүзүнөн таанып ачууну жөндөө"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Телефонуңузду карап туруп эле кулпусун ачып алыңыз"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Жүзүнөн таанып ачуу функциясын колдонуу үчүн Жөндөөлөр &gt; Купуялык бөлүмүнө өтүп, "<b>"Камераны колдонууну"</b>" күйгүзүңүз"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Кулпусун ачуунун көбүрөөк жолдорун жөндөңүз"</string>
diff --git a/core/res/res/values-mcc334-mnc020-pa/strings.xml b/core/res/res/values-mcc334-mnc020-pa/strings.xml
index 23f77d88ab72..3cf6bc88e70e 100644
--- a/core/res/res/values-mcc334-mnc020-pa/strings.xml
+++ b/core/res/res/values-mcc334-mnc020-pa/strings.xml
@@ -21,6 +21,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
<string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ਪ੍ਰਮਾਣੀਕਰਨ ਅਸਫਲ -29-"</string>
- <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ਸੇਵਾ ਨੂੰ ਸਬਸਕ੍ਰਾਈਬ ਨਹੀਂ ਕੀਤਾ -33-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ਸੇਵਾ ਦੀ ਗਾਹਕੀ ਨਹੀਂ ਲਈ -33-"</string>
<string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ਕਿਸੇ ਵੀ APN ਲਈ ਇੱਕ ਤੋਂ ਵੱਧ PDN ਕਨੈਕਸ਼ਨਾਂ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ -55-"</string>
</resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 4a028aba4707..4b59bd84802a 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -639,10 +639,10 @@
<string name="face_acquired_too_dark" msgid="7919016380747701228">"ଉଜ୍ଜ୍ୱଳ ଲାଇଟ ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ଫୋନକୁ ଟିକେ ଦୂରକୁ ନିଅନ୍ତୁ"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ଫୋନକୁ ପାଖକୁ ଆଣନ୍ତୁ"</string>
- <string name="face_acquired_too_high" msgid="8278815780046368576">"ଫୋନକୁ ଉପରକୁ ମୁଭ କରନ୍ତୁ"</string>
- <string name="face_acquired_too_low" msgid="4075391872960840081">"ଫୋନ୍‌କୁ ତଳକୁ ମୁଭ କରନ୍ତୁ"</string>
- <string name="face_acquired_too_right" msgid="6245286514593540859">"ଫୋନକୁ ଆପଣଙ୍କ ବାମ ପଟକୁ ମୁଭ କରନ୍ତୁ"</string>
- <string name="face_acquired_too_left" msgid="9201762240918405486">"ଫୋନକୁ ଆପଣଙ୍କ ଡାହାଣ ପଟକୁ ମୁଭ କରନ୍ତୁ"</string>
+ <string name="face_acquired_too_high" msgid="8278815780046368576">"ଫୋନକୁ ଉପରକୁ ନିଅନ୍ତୁ"</string>
+ <string name="face_acquired_too_low" msgid="4075391872960840081">"ଫୋନକୁ ତଳକୁ ନିଅନ୍ତୁ"</string>
+ <string name="face_acquired_too_right" msgid="6245286514593540859">"ଫୋନକୁ ଆପଣଙ୍କ ବାମ ପଟକୁ ନିଅନ୍ତୁ"</string>
+ <string name="face_acquired_too_left" msgid="9201762240918405486">"ଫୋନକୁ ଆପଣଙ୍କ ଡାହାଣ ପଟକୁ ନିଅନ୍ତୁ"</string>
<string name="face_acquired_poor_gaze" msgid="4427153558773628020">"ଦୟାକରି ଆପଣଙ୍କ ଡିଭାଇସ୍‌କୁ ସିଧାସଳଖ ଦେଖନ୍ତୁ।"</string>
<string name="face_acquired_not_detected" msgid="1057966913397548150">"ଆପଣଙ୍କ ଫେସ ଦେଖାଯାଉନାହିଁ। ଆପଣଙ୍କ ଫୋନକୁ ଆଖି ସିଧାରେ ଧରି ରଖନ୍ତୁ।"</string>
<string name="face_acquired_too_much_motion" msgid="8199691445085189528">"ଅତ୍ୟଧିକ ଅସ୍ଥିର। ଫୋନ୍‍କୁ ସ୍ଥିର ଭାବେ ଧରନ୍ତୁ।"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index b6d866dc4ba2..8be5450d62bb 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -641,10 +641,10 @@
<string name="face_acquired_too_dark" msgid="7919016380747701228">"Сделайте освещение ярче"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Переместите телефон дальше от лица."</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Переместите телефон ближе к лицу"</string>
- <string name="face_acquired_too_high" msgid="8278815780046368576">"Переместите телефон выше"</string>
- <string name="face_acquired_too_low" msgid="4075391872960840081">"Переместите телефон ниже"</string>
- <string name="face_acquired_too_right" msgid="6245286514593540859">"Переместите телефон левее"</string>
- <string name="face_acquired_too_left" msgid="9201762240918405486">"Переместите телефон правее"</string>
+ <string name="face_acquired_too_high" msgid="8278815780046368576">"Переместите телефон выше."</string>
+ <string name="face_acquired_too_low" msgid="4075391872960840081">"Переместите телефон ниже."</string>
+ <string name="face_acquired_too_right" msgid="6245286514593540859">"Переместите телефон левее."</string>
+ <string name="face_acquired_too_left" msgid="9201762240918405486">"Переместите телефон правее."</string>
<string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Смотрите прямо на устройство."</string>
<string name="face_acquired_not_detected" msgid="1057966913397548150">"Вашего лица не видно. Держите телефон на уровне глаз"</string>
<string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Не перемещайте устройство. Держите его неподвижно."</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 306c0a5b8629..721f76bc187c 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -967,7 +967,7 @@
<string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"సైన్ ఇన్ చేయండి"</string>
<string name="lockscreen_glogin_invalid_input" msgid="4369219936865697679">"వినియోగదారు పేరు లేదా పాస్‌వర్డ్ చెల్లదు."</string>
<string name="lockscreen_glogin_account_recovery_hint" msgid="1683405808525090649">"మీ వినియోగదారు పేరు లేదా పాస్‌వర్డ్‌ను మర్చిపోయారా?\n"<b>"google.com/accounts/recovery"</b>"ని సందర్శించండి."</string>
- <string name="lockscreen_glogin_checking_password" msgid="2607271802803381645">"చెక్ చేస్తోంది..."</string>
+ <string name="lockscreen_glogin_checking_password" msgid="2607271802803381645">"తనిఖీ చేస్తోంది..."</string>
<string name="lockscreen_unlock_label" msgid="4648257878373307582">"అన్‌లాక్ చేయండి"</string>
<string name="lockscreen_sound_on_label" msgid="1660281470535492430">"ధ్వని ఆన్‌లో ఉంది"</string>
<string name="lockscreen_sound_off_label" msgid="2331496559245450053">"ధ్వని ఆఫ్‌లో ఉంది"</string>
@@ -1369,7 +1369,7 @@
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"పరీక్ష నియంత్రణ మోడ్ ప్రారంభించబడింది"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"పరీక్ష నియంత్రణ మోడ్‌ను నిలిపివేయడానికి ఫ్యాక్టరీ రీసెట్‍‌ను అమలు చేయండి."</string>
<string name="console_running_notification_title" msgid="6087888939261635904">"సీరియల్ కన్సోల్ ప్రారంభించబడింది"</string>
- <string name="console_running_notification_message" msgid="7892751888125174039">"పని తీరు ప్రభావితమైంది. నిలిపివేయడానికి, బూట్‌లోడర్‌ను చెక్ చేయండి."</string>
+ <string name="console_running_notification_message" msgid="7892751888125174039">"పని తీరు ప్రభావితమైంది. నిలిపివేయడానికి, బూట్‌లోడర్‌ను తనిఖీ చేయండి."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"ప్రయోగాత్మక MTE ఎనేబుల్ చేయబడింది"</string>
<string name="mte_override_notification_message" msgid="2441170442725738942">"పనితీరు, స్థిరత్వం ప్రభావితం కావచ్చు. డిజేబుల్ చేయడానికి రీబూట్ చేయండి. arm64.memtag.bootctlని ఉపయోగించి ఎనేబుల్ చేసినట్లయితే, దాన్ని ముందుగా ఏదీ లేనిదిగా సెట్ చేయండి."</string>
<string name="usb_contaminant_detected_title" msgid="4359048603069159678">"USB పోర్ట్‌లో ద్రవ లేదా వ్యర్థ పదార్థాలు ఉన్నాయి"</string>
@@ -1394,7 +1394,7 @@
<string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> ఇతర యాప్‌లలో చూపబడుతోంది"</string>
<string name="alert_windows_notification_message" msgid="6538171456970725333">"<xliff:g id="NAME">%s</xliff:g> ఈ లక్షణాన్ని ఉపయోగించకూడదు అని మీరు అనుకుంటే, సెట్టింగ్‌లను తెరవడానికి ట్యాప్ చేసి, దీన్ని ఆఫ్ చేయండి."</string>
<string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"ఆఫ్ చేయి"</string>
- <string name="ext_media_checking_notification_title" msgid="8299199995416510094">"<xliff:g id="NAME">%s</xliff:g>ని చెక్ చేస్తోంది…"</string>
+ <string name="ext_media_checking_notification_title" msgid="8299199995416510094">"<xliff:g id="NAME">%s</xliff:g>ని తనిఖీ చేస్తోంది…"</string>
<string name="ext_media_checking_notification_message" msgid="2231566971425375542">"ప్రస్తుత కంటెంట్ సమీక్షించబడుతోంది"</string>
<string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"మీడియా స్టోరేజ్‌ను విశ్లేషిస్తోంది"</string>
<string name="ext_media_new_notification_title" msgid="3517407571407687677">"కొత్త <xliff:g id="NAME">%s</xliff:g>"</string>
@@ -1434,7 +1434,7 @@
<string name="ext_media_move_failure_message" msgid="4197306718121869335">"కంటెంట్‌ని తరలించడానికి మళ్లీ ప్రయత్నించండి"</string>
<string name="ext_media_status_removed" msgid="241223931135751691">"తీసివేయబడింది"</string>
<string name="ext_media_status_unmounted" msgid="8145812017295835941">"తొలగించబడింది"</string>
- <string name="ext_media_status_checking" msgid="159013362442090347">"చెక్ చేస్తోంది..."</string>
+ <string name="ext_media_status_checking" msgid="159013362442090347">"తనిఖీ చేస్తోంది..."</string>
<string name="ext_media_status_mounted" msgid="3459448555811203459">"సిద్ధంగా ఉంది"</string>
<string name="ext_media_status_mounted_ro" msgid="1974809199760086956">"చదవడానికి మాత్రమే"</string>
<string name="ext_media_status_bad_removal" msgid="508448566481406245">"అసురక్షితంగా తీసివేయబడింది"</string>
@@ -1656,7 +1656,7 @@
<string name="kg_login_submit_button" msgid="893611277617096870">"సైన్ ఇన్ చేయండి"</string>
<string name="kg_login_invalid_input" msgid="8292367491901220210">"చెల్లని వినియోగదారు పేరు లేదా పాస్‌వర్డ్."</string>
<string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"మీ వినియోగదారు పేరు లేదా పాస్‌వర్డ్‌ను మర్చిపోయారా?\n"<b>"google.com/accounts/recovery"</b>"ని సందర్శించండి."</string>
- <string name="kg_login_checking_password" msgid="4676010303243317253">"ఖాతాను చెక్ చేస్తోంది…"</string>
+ <string name="kg_login_checking_password" msgid="4676010303243317253">"ఖాతాను తనిఖీ చేస్తోంది…"</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"మీరు మీ పిన్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"మీరు మీ పాస్‌వర్డ్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
@@ -1953,7 +1953,7 @@
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"ఈ యాప్ అదనపు సెక్యూరిటీ కోసం రిక్వెస్ట్ చేస్తోంది. బదులుగా మీ Android TV పరికరంలో ట్రై చేయండి."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"ఈ యాప్ అదనపు సెక్యూరిటీ కోసం రిక్వెస్ట్ చేస్తోంది. బదులుగా మీ టాబ్లెట్‌లో ట్రై చేయండి."</string>
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"ఈ యాప్ అదనపు సెక్యూరిటీ కోసం రిక్వెస్ట్ చేస్తోంది. బదులుగా మీ ఫోన్‌లో ట్రై చేయండి."</string>
- <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ఈ యాప్ పాత వెర్షన్ Android కోసం రూపొందించబడింది మరియు అది సరిగ్గా పని చేయకపోవచ్చు. అప్‌డేట్‌ల కోసం చెక్ చేయడానికి ప్రయత్నించండి లేదా డెవలపర్‌ని సంప్రదించండి."</string>
+ <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ఈ యాప్ పాత వెర్షన్ Android కోసం రూపొందించబడింది మరియు అది సరిగ్గా పని చేయకపోవచ్చు. అప్‌డేట్‌ల కోసం తనిఖీ చేయడానికి ప్రయత్నించండి లేదా డెవలపర్‌ని సంప్రదించండి."</string>
<string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"అప్‌డేట్ కోసం చెక్ చేయండి"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"మీకు కొత్త మెసేజ్‌లు ఉన్నాయి"</string>
<string name="new_sms_notification_content" msgid="3197949934153460639">"వీక్షించడానికి SMS యాప్‌ను తెరవండి"</string>
@@ -2056,7 +2056,7 @@
<string name="zen_upgrade_notification_visd_title" msgid="2001148984371968620">"కొత్తది: అంతరాయం కలిగించవద్దు నోటిఫికేషన్‌లను దాస్తోంది"</string>
<string name="zen_upgrade_notification_visd_content" msgid="3683314609114134946">"మరింత తెలుసుకోవడానికి మరియు మార్చడానికి నొక్కండి."</string>
<string name="zen_upgrade_notification_title" msgid="8198167698095298717">"అంతరాయం కలిగించవద్దు మార్చబడింది"</string>
- <string name="zen_upgrade_notification_content" msgid="5228458567180124005">"బ్లాక్ చేయబడిన దాన్ని చెక్ చేయడానికి నొక్కండి."</string>
+ <string name="zen_upgrade_notification_content" msgid="5228458567180124005">"బ్లాక్ చేయబడిన దాన్ని తనిఖీ చేయడానికి నొక్కండి."</string>
<string name="review_notification_settings_title" msgid="5102557424459810820">"నోటిఫికేషన్ సెట్టింగ్‌లను రివ్యూ చేయండి"</string>
<string name="review_notification_settings_text" msgid="5916244866751849279">"Android 13తో మొదలుకుని, మీరు ఇన్‌స్టాల్ చేసే యాప్‌లకు నోటిఫికేషన్‌లను పంపడానికి మీ అనుమతి అవసరం. ఇప్పటికే ఉన్న యాప్‌ల కోసం ఈ అనుమతిని మార్చడానికి ట్యాప్ చేయండి."</string>
<string name="review_notification_settings_remind_me_action" msgid="1081081018678480907">"తర్వాత గుర్తు చేయి"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 66ac64a255cc..547b471babc3 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -643,8 +643,8 @@
<string name="face_acquired_too_far" msgid="2922278214231064859">"Тримайте телефон ближче до обличчя"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Підніміть телефон вище"</string>
<string name="face_acquired_too_low" msgid="4075391872960840081">"Опустіть телефон нижче"</string>
- <string name="face_acquired_too_right" msgid="6245286514593540859">"Посуньте телефон лівіше"</string>
- <string name="face_acquired_too_left" msgid="9201762240918405486">"Посуньте телефон правіше"</string>
+ <string name="face_acquired_too_right" msgid="6245286514593540859">"Тримайте телефон лівіше"</string>
+ <string name="face_acquired_too_left" msgid="9201762240918405486">"Тримайте телефон правіше"</string>
<string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Дивіться просто на пристрій."</string>
<string name="face_acquired_not_detected" msgid="1057966913397548150">"Обличчя не видно. Утримуйте телефон на рівні очей."</string>
<string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Забагато рухів. Тримайте телефон нерухомо."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 4df2c224706d..7f6b6795342a 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -657,7 +657,7 @@
<string name="face_acquired_dark_glasses_detected" msgid="7263638432128692048">"Bạn cần cho thấy toàn bộ khuôn mặt của mình"</string>
<string name="face_acquired_mouth_covering_detected" msgid="615991670821926847">"Toàn bộ khuôn mặt của bạn phải được hiển thị"</string>
<string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Không thể tạo mẫu khuôn mặt của bạn. Hãy thử lại."</string>
- <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Đã phát hiện thấy kính râm. Toàn bộ khuôn mặt của bạn phải được trông thấy rõ ràng."</string>
+ <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Đã phát hiện đeo kính đen. Toàn bộ khuôn mặt của bạn phải được hiển thị."</string>
<string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Đã phát hiện khuôn mặt bị che khuất. Toàn bộ khuôn mặt của bạn phải được hiển thị."</string>
<string-array name="face_acquired_vendor">
</string-array>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d2f31b05313d..515ea5006667 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2422,6 +2422,15 @@
<!-- When closing the current activity, this is the animation that is
run on the current activity (which is exiting the screen). -->
<attr name="activityCloseExitAnimation" format="reference" />
+ <!-- When closing a dream activity, this is the animation that is
+ run on the dream activity (which is exiting the screen). -->
+ <attr name="dreamActivityCloseExitAnimation" format="reference" />
+ <!-- When opening a dream activity, this is the animation that is
+ run on the dream activity (which is entering the screen). -->
+ <attr name="dreamActivityOpenEnterAnimation" format="reference" />
+ <!-- When opening a dream activity, this is the animation that is
+ run on the old activity (which is exiting the screen). -->
+ <attr name="dreamActivityOpenExitAnimation" format="reference" />
<!-- When opening an activity in a new task, this is the animation that is
run on the activity of the new task (which is entering the screen). -->
<attr name="taskOpenEnterAnimation" format="reference" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 31229e97024f..a8c7bf249ada 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2030,6 +2030,10 @@
STREAM_MUSIC as if it's on TV platform. -->
<bool name="config_single_volume">false</bool>
+ <!-- Flag indicating whether notification and ringtone volumes
+ are controlled together (aliasing is true) or not. -->
+ <bool name="config_alias_ring_notif_stream_types">true</bool>
+
<!-- Flag indicating whether platform level volume adjustments are enabled for remote sessions
on grouped devices. -->
<bool name="config_volumeAdjustmentForRemoteGroupSessions">true</bool>
@@ -3755,6 +3759,9 @@
"Guest" and "Reset guest". -->
<bool name="config_guestUserAutoCreated">false</bool>
+ <!-- If true, owner can change guest user ephemeral state via UI option -->
+ <bool name="config_guestUserAllowEphemeralStateChange">true</bool>
+
<!-- Enforce strong auth on boot. Setting this to false represents a security risk and should
not be ordinarily done. The only case in which this might be permissible is in a car head
unit where there are hardware mechanisms to protect the device (physical keys) and not
@@ -5133,16 +5140,24 @@
-->
<color name="config_letterboxBackgroundColor">@android:color/system_neutral2_900</color>
- <!-- Horizonal position of a center of the letterboxed app window.
+ <!-- Horizontal position of a center of the letterboxed app window.
0 corresponds to the left side of the screen and 1 to the right side. If given value < 0
- or > 1, it is ignored and central positionis used (0.5). -->
+ or > 1, it is ignored and central position is used (0.5). -->
<item name="config_letterboxHorizontalPositionMultiplier" format="float" type="dimen">0.5</item>
- <!-- Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape
- device orientation. -->
- <bool name="config_letterboxIsReachabilityEnabled">false</bool>
+ <!-- Vertical position of a center of the letterboxed app window.
+ 0 corresponds to the upper side of the screen and 1 to the lower side. If given value < 0
+ or > 1, it is ignored and central position is used (0.5). -->
+ <item name="config_letterboxVerticalPositionMultiplier" format="float" type="dimen">0.5</item>
+
+ <!-- Whether horizontal reachability repositioning is allowed for letterboxed fullscreen apps.
+ -->
+ <bool name="config_letterboxIsHorizontalReachabilityEnabled">false</bool>
- <!-- Default horizonal position of the letterboxed app window when reachability is
+ <!-- Whether vertical reachability repositioning is allowed for letterboxed fullscreen apps. -->
+ <bool name="config_letterboxIsVerticalReachabilityEnabled">false</bool>
+
+ <!-- Default horizontal position of the letterboxed app window when reachability is
enabled and an app is fullscreen in landscape device orientation. When reachability is
enabled, the position can change between left, center and right. This config defines the
default one:
@@ -5150,11 +5165,30 @@
- Option 1 - Center.
- Option 2 - Right.
If given value is outside of this range, the option 1 (center) is assummed. -->
- <integer name="config_letterboxDefaultPositionForReachability">1</integer>
+ <integer name="config_letterboxDefaultPositionForHorizontalReachability">1</integer>
+
+ <!-- Default vertical position of the letterboxed app window when reachability is
+ enabled and an app is fullscreen in portrait device orientation. When reachability is
+ enabled, the position can change between top, center and bottom. This config defines the
+ default one:
+ - Option 0 - Top.
+ - Option 1 - Center.
+ - Option 2 - Bottom.
+ If given value is outside of this range, the option 1 (center) is assummed. -->
+ <integer name="config_letterboxDefaultPositionForVerticalReachability">1</integer>
<!-- Whether displaying letterbox education is enabled for letterboxed fullscreen apps. -->
<bool name="config_letterboxIsEducationEnabled">false</bool>
+ <!-- Default min aspect ratio for unresizable apps which are eligible for size compat mode.
+ Values <= 1.0 will be ignored. Activity min/max aspect ratio restrictions will still be
+ espected so this override can control the maximum screen area that can be occupied by
+ the app in the letterbox mode. -->
+ <item name="config_letterboxDefaultMinAspectRatioForUnresizableApps" format="float" type="dimen">0.0</item>
+
+ <!-- Whether using split screen aspect ratio as a default aspect ratio for unresizable apps. -->
+ <bool name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled">false</bool>
+
<!-- Whether a camera compat controller is enabled to allow the user to apply or revert
treatment for stretched issues in camera viewfinder. -->
<bool name="config_isCameraCompatControlForStretchedIssuesEnabled">false</bool>
@@ -5816,4 +5850,7 @@
<string-array name="config_serviceStateLocationAllowedPackages">
<item>"com.android.phone"</item>
</string-array>
+
+ <!-- Whether the wake screen on notifications feature is available. -->
+ <bool name="config_pulseOnNotificationsAvailable">true</bool>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b754100a3ed6..2542268a153a 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -684,7 +684,7 @@
<!-- Lighting and shadow properties -->
<dimen name="light_y">0dp</dimen>
- <dimen name="light_z">600dp</dimen>
+ <dimen name="light_z">500dp</dimen>
<dimen name="light_radius">800dp</dimen>
<item type="dimen" format="float" name="ambient_shadow_alpha">0.039</item>
<item type="dimen" format="float" name="spot_shadow_alpha">0.19</item>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 5d17047d7430..bad05e077b7d 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -85,6 +85,9 @@ please see styles_device_defaults.xml.
<item name="activityOpenExitAnimation">@anim/activity_open_exit</item>
<item name="activityCloseEnterAnimation">@anim/activity_close_enter</item>
<item name="activityCloseExitAnimation">@anim/activity_close_exit</item>
+ <item name="dreamActivityCloseExitAnimation">@anim/dream_activity_close_exit</item>
+ <item name="dreamActivityOpenEnterAnimation">@anim/dream_activity_open_enter</item>
+ <item name="dreamActivityOpenExitAnimation">@anim/dream_activity_open_exit</item>
<item name="taskOpenEnterAnimation">@anim/task_open_enter</item>
<item name="taskOpenExitAnimation">@anim/task_open_exit</item>
<item name="launchTaskBehindTargetAnimation">@anim/launch_task_behind_target</item>
diff --git a/core/res/res/values/styles_car.xml b/core/res/res/values/styles_car.xml
index ca3ba936630e..0655fde70934 100644
--- a/core/res/res/values/styles_car.xml
+++ b/core/res/res/values/styles_car.xml
@@ -14,96 +14,15 @@
limitations under the License.
-->
<resources>
- <!-- Car text -->
- <style name="CarBody1">
- <item name="textStyle">normal</item>
- <item name="textSize">@dimen/car_body1_size</item>
- <item name="textColor">@color/car_body1</item>
- </style>
-
- <style name="CarBody1.Light">
- <item name="textColor">@color/car_body1_light</item>
- </style>
-
- <style name="CarBody1.Dark">
- <item name="textColor">@color/car_body2_dark</item>
- </style>
-
- <style name="CarBody2">
- <item name="textStyle">normal</item>
- <item name="textSize">@dimen/car_body2_size</item>
- <item name="textColor">@color/car_body2</item>
- </style>
-
- <style name="CarBody2.Dark">
- <item name="textColor">@color/car_body2_dark</item>
- </style>
- <style name="CarBody2.Light">
- <item name="textColor">@color/car_body2_light</item>
- </style>
-
- <style name="CarBody3">
- <item name="textStyle">normal</item>
- <item name="textSize">@dimen/car_body3_size</item>
- <item name="textColor">@color/car_body3</item>
- </style>
-
- <!-- The smallest styling for body text. The color of this text changes based on the day/night
- mode. -->
- <style name="CarBody4">
+ <!-- The Dialog message text style-->
+ <style name="CarDialogMessageText">
<item name="textStyle">normal</item>
<item name="textSize">@dimen/car_body4_size</item>
<item name="textColor">@color/car_body4</item>
</style>
-
- <style name="CarAction1">
- <item name="textStyle">bold</item>
- <item name="textSize">@dimen/car_action1_size</item>
- <item name="textColor">@color/control_default_material</item>
- </style>
-
- <style name="CarAction1.Dark">
- <item name="textColor">@color/car_highlight_dark</item>
- </style>
- <style name="CarAction1.Light">
- <item name="textColor">@color/car_highlight_light</item>
- </style>
-
- <!-- The styling for title text. The color of this text changes based on day/night mode. -->
- <style name="CarTitle" >
- <item name="textStyle">bold</item>
- <item name="textSize">@dimen/car_title2_size</item>
- <item name="textColor">@color/car_title</item>
- </style>
-
- <!-- Title text that is permanently a dark color. -->
- <style name="CarTitle.Dark" >
- <item name="textColor">@color/car_title_dark</item>
- </style>
-
- <!-- Title text that is permanently a light color. -->
- <style name="CarTitle.Light" >
- <item name="textColor">@color/car_title_light</item>
- </style>
-
- <!-- Action bar -->
- <style name="ActionBarTitle" parent="@style/Widget.DeviceDefault.TextView">
- <item name="android:singleLine">true</item>
- <item name="android:textAppearance">?attr/textAppearanceLarge</item>
- </style>
-
- <style name="ActionBarButton"
- parent="@style/Widget.DeviceDefault.Button.Borderless.Colored">
- <item name="android:textAppearance">@style/ActionBarButtonTextAppearance</item>
- <!-- Button's internal horizontal padding -->
- <item name="android:paddingStart">@*android:dimen/car_padding_3</item>
- <item name="android:paddingEnd">@*android:dimen/car_padding_3</item>
- <item name="android:drawablePadding">@*android:dimen/car_padding_2</item>
- <item name="android:maxWidth">@*android:dimen/action_bar_button_max_width</item>
- </style>
-
- <style name="ActionBarButtonTextAppearance"
- parent="@style/TextAppearance.DeviceDefault.Widget.Button.Borderless.Colored">
- <item name="android:textAllCaps">false</item>
+ <!-- This style makes Dialog button text use medium font weight. -->
+ <style name="CarDialogButtonText">
+ <item name="android:textAppearance">@style/TextAppearance.DeviceDefault.Widget.Button</item>
+ <item name="android:textColor">@color/control_default_material</item>
</style>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a8258118607d..1438e7fa5cf4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -273,6 +273,7 @@
<java-symbol type="attr" name="autofillSaveCustomSubtitleMaxHeight"/>
<java-symbol type="bool" name="action_bar_embed_tabs" />
<java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
+ <java-symbol type="bool" name="config_alias_ring_notif_stream_types" />
<java-symbol type="bool" name="config_avoidGfxAccel" />
<java-symbol type="bool" name="config_bluetooth_address_validation" />
<java-symbol type="integer" name="config_chooser_max_targets_per_row" />
@@ -394,6 +395,7 @@
<java-symbol type="bool" name="config_supportsInsecureLockScreen" />
<java-symbol type="bool" name="config_guestUserEphemeral" />
<java-symbol type="bool" name="config_guestUserAutoCreated" />
+ <java-symbol type="bool" name="config_guestUserAllowEphemeralStateChange" />
<java-symbol type="bool" name="config_localDisplaysMirrorContent" />
<java-symbol type="array" name="config_localPrivateDisplayPorts" />
<java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" />
@@ -1704,6 +1706,10 @@
<java-symbol type="anim" name="task_fragment_close_exit" />
<java-symbol type="anim" name="task_fragment_open_enter" />
<java-symbol type="anim" name="task_fragment_open_exit" />
+ <java-symbol type="anim" name="task_fragment_clear_top_close_enter" />
+ <java-symbol type="anim" name="task_fragment_clear_top_close_exit" />
+ <java-symbol type="anim" name="task_fragment_clear_top_open_enter" />
+ <java-symbol type="anim" name="task_fragment_clear_top_open_exit" />
<java-symbol type="array" name="config_autoRotationTiltTolerance" />
<java-symbol type="array" name="config_longPressVibePattern" />
@@ -3336,6 +3342,7 @@
<java-symbol type="string" name="config_dozeTapSensorType" />
<java-symbol type="array" name="config_dozeTapSensorPostureMapping" />
<java-symbol type="bool" name="config_dozePulsePickup" />
+ <java-symbol type="bool" name="config_pulseOnNotificationsAvailable" />
<!-- Used for MimeIconUtils. -->
<java-symbol type="drawable" name="ic_doc_apk" />
@@ -4387,9 +4394,14 @@
<java-symbol type="integer" name="config_letterboxBackgroundType" />
<java-symbol type="color" name="config_letterboxBackgroundColor" />
<java-symbol type="dimen" name="config_letterboxHorizontalPositionMultiplier" />
- <java-symbol type="bool" name="config_letterboxIsReachabilityEnabled" />
- <java-symbol type="integer" name="config_letterboxDefaultPositionForReachability" />
+ <java-symbol type="dimen" name="config_letterboxVerticalPositionMultiplier" />
+ <java-symbol type="bool" name="config_letterboxIsHorizontalReachabilityEnabled" />
+ <java-symbol type="bool" name="config_letterboxIsVerticalReachabilityEnabled" />
+ <java-symbol type="integer" name="config_letterboxDefaultPositionForHorizontalReachability" />
+ <java-symbol type="integer" name="config_letterboxDefaultPositionForVerticalReachability" />
<java-symbol type="bool" name="config_letterboxIsEducationEnabled" />
+ <java-symbol type="dimen" name="config_letterboxDefaultMinAspectRatioForUnresizableApps" />
+ <java-symbol type="bool" name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled" />
<java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index bfb2fd57975f..a2d4bafef41c 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -31,7 +31,6 @@ import static org.junit.Assert.assertTrue;
import android.annotation.Nullable;
import android.app.Activity;
-import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.IApplicationThread;
@@ -571,53 +570,6 @@ public class ActivityThreadTest {
}
@Test
- public void testHandleProcessConfigurationChanged_DependOnProcessState() {
- final ActivityThread activityThread = ActivityThread.currentActivityThread();
- final Configuration origConfig = activityThread.getConfiguration();
- final int newDpi = origConfig.densityDpi + 10;
- final Configuration newConfig = new Configuration(origConfig);
- newConfig.seq++;
- newConfig.densityDpi = newDpi;
-
- activityThread.updateProcessState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
- false /* fromIPC */);
-
- applyProcessConfiguration(activityThread, newConfig);
- try {
- // In the cached state, the configuration is only set as pending and not applied.
- assertEquals(origConfig.densityDpi, activityThread.getConfiguration().densityDpi);
- assertTrue(activityThread.isCachedProcessState());
- } finally {
- // The foreground state is the default state of instrumentation.
- activityThread.updateProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
- false /* fromIPC */);
- }
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-
- try {
- // The state becomes non-cached, the pending configuration should be applied.
- assertEquals(newConfig.densityDpi, activityThread.getConfiguration().densityDpi);
- assertFalse(activityThread.isCachedProcessState());
- } finally {
- // Restore to the original configuration.
- activityThread.getConfiguration().seq = origConfig.seq - 1;
- applyProcessConfiguration(activityThread, origConfig);
- }
- }
-
- private static void applyProcessConfiguration(ActivityThread thread, Configuration config) {
- final ClientTransaction clientTransaction = newTransaction(thread,
- null /* activityToken */);
- clientTransaction.addCallback(ConfigurationChangeItem.obtain(config));
- final IApplicationThread appThread = thread.getApplicationThread();
- try {
- appThread.scheduleTransaction(clientTransaction);
- } catch (Exception ignored) {
- }
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
- @Test
public void testResumeAfterNewIntent() {
final Activity activity = mActivityTestRule.launchActivity(new Intent());
final ActivityThread activityThread = activity.getActivityThread();
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index c504f0cf2d94..dcb1835204dd 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -214,19 +214,23 @@ public class InsetsControllerTest {
}
@Test
- public void testFrameDoesntMatchDisplay() {
- mController.onFrameChanged(new Rect(0, 0, 100, 100));
- mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200));
- InsetsSourceControl control =
- new InsetsSourceControl(
- ITYPE_STATUS_BAR, mLeash, new Point(), Insets.of(0, 10, 0, 0));
- mController.onControlsChanged(new InsetsSourceControl[] { control });
+ public void testFrameDoesntOverlapWithInsets() {
WindowInsetsAnimationControlListener controlListener =
mock(WindowInsetsAnimationControlListener.class);
- mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, new LinearInterpolator(),
- new CancellationSignal(), controlListener);
- mController.addOnControllableInsetsChangedListener(
- (controller, typeMask) -> assertEquals(0, typeMask));
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // The frame doesn't overlap with status bar.
+ mController.onFrameChanged(new Rect(0, 10, 100, 100));
+
+ InsetsSourceControl control =
+ new InsetsSourceControl(
+ ITYPE_STATUS_BAR, mLeash, new Point(), Insets.of(0, 10, 0, 0));
+ mController.onControlsChanged(new InsetsSourceControl[]{control});
+ mController.controlWindowInsetsAnimation(0, 0 /* durationMs */,
+ new LinearInterpolator(),
+ new CancellationSignal(), controlListener);
+ mController.addOnControllableInsetsChangedListener(
+ (controller, typeMask) -> assertEquals(0, typeMask));
+ });
verify(controlListener).onCancelled(null);
verify(controlListener, never()).onReady(any(), anyInt());
}
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index 47ce2d87e69f..cc4fbab1f190 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -304,6 +304,23 @@ public class TextViewTest {
assertFalse(mTextView.isCursorVisible());
}
+ @Test(expected = NullPointerException.class)
+ @UiThreadTest
+ public void setTextCharArrayNullThrows() {
+ mTextView = new TextView(mActivity);
+ mTextView.setText((char[]) null, 0, 0);
+ }
+
+ @Test
+ @UiThreadTest
+ public void setTextCharArrayValidAfterSetTextString() {
+ mTextView = new TextView(mActivity);
+ mTextView.setText(new char[] { 'h', 'i'}, 0, 2);
+ CharSequence charWrapper = mTextView.getText();
+ mTextView.setText("null out char wrapper");
+ assertEquals("hi", charWrapper.toString());
+ }
+
private String createLongText() {
int size = 600 * 1000;
final StringBuilder builder = new StringBuilder(size);
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index f448cb3091e7..b5194f637395 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -66,6 +66,7 @@ public class WindowOnBackInvokedDispatcherTest {
MockitoAnnotations.initMocks(this);
mDispatcher = new WindowOnBackInvokedDispatcher(true /* applicationCallbackEnabled */);
mDispatcher.attachToWindow(mWindowSession, mWindow);
+ mDispatcher.onWindowFocusChanged(true);
}
private void waitForIdle() {
@@ -152,4 +153,31 @@ public class WindowOnBackInvokedDispatcherTest {
waitForIdle();
verify(mCallback2).onBackStarted();
}
+
+ @Test
+ public void skipBackInvokeWhenNoFocus() throws RemoteException {
+ ArgumentCaptor<OnBackInvokedCallbackInfo> captor =
+ ArgumentCaptor.forClass(OnBackInvokedCallbackInfo.class);
+
+ mDispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback1);
+
+ verify(mWindowSession, times(1)).setOnBackInvokedCallbackInfo(
+ Mockito.eq(mWindow),
+ captor.capture());
+
+ verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), captor.capture());
+
+ // Should invoke back if it's still in focused.
+ captor.getValue().getCallback().onBackInvoked();
+ waitForIdle();
+ verify(mCallback1).onBackInvoked();
+
+ // In case the focus has lost during back gesture.
+ mDispatcher.onWindowFocusChanged(false);
+
+ captor.getValue().getCallback().onBackInvoked();
+ waitForIdle();
+ verifyZeroInteractions(mCallback1);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index b38e1c274b45..e8c7ce0b312b 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -84,17 +84,23 @@ import android.service.chooser.ChooserTarget;
import android.view.View;
import androidx.annotation.CallSuper;
+import androidx.test.espresso.matcher.BoundedDiagnosingMatcher;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
+import com.android.internal.R;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.widget.GridLayoutManager;
+import com.android.internal.widget.RecyclerView;
+import org.hamcrest.Description;
import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -260,6 +266,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity(
@@ -283,6 +290,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, "chooser test"));
@@ -303,6 +311,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -323,6 +332,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -346,6 +356,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -372,6 +383,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -397,6 +409,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -418,6 +431,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -472,6 +486,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -512,6 +527,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -546,6 +562,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(null);
Intent sendIntent = createSendTextIntent();
@@ -581,6 +598,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -643,6 +661,7 @@ public class ChooserActivityTest {
when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
when(ChooserActivityOverrideData.getInstance().resolverListController.getLastChosen())
.thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
@@ -682,6 +701,7 @@ public class ChooserActivityTest {
when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper)
@@ -714,6 +734,7 @@ public class ChooserActivityTest {
when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
final ChooserActivity activity =
@@ -741,6 +762,7 @@ public class ChooserActivityTest {
when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
MetricsLogger mockLogger = ChooserActivityOverrideData.getInstance().metricsLogger;
@@ -770,6 +792,7 @@ public class ChooserActivityTest {
when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper)
@@ -840,6 +863,7 @@ public class ChooserActivityTest {
when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
final IChooserWrapper activity = (IChooserWrapper)
@@ -916,6 +940,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -952,6 +977,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -991,6 +1017,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -1085,6 +1112,7 @@ public class ChooserActivityTest {
when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -1118,6 +1146,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -1151,6 +1180,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -1183,6 +1213,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
@@ -1212,6 +1243,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -1244,6 +1276,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -1278,6 +1311,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
when(
@@ -1317,6 +1351,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -1359,6 +1394,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -1403,6 +1439,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -1479,6 +1516,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -1560,6 +1598,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
// Create direct share target
@@ -1632,6 +1671,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
// Create direct share target
@@ -1680,6 +1720,25 @@ public class ChooserActivityTest {
wrapper.getAdapter().getItem(1).getDisplayLabel(), is("testTitle1"));
}
+ @Test
+ public void testUpdateMaxTargetsPerRow_columnCountIsUpdated() throws InterruptedException {
+ updateMaxTargetsPerRowResource(/* targetsPerRow= */ 4);
+ givenAppTargets(/* appCount= */ 16);
+ Intent sendIntent = createSendTextIntent();
+ final ChooserActivity activity =
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+
+ updateMaxTargetsPerRowResource(/* targetsPerRow= */ 6);
+ InstrumentationRegistry.getInstrumentation()
+ .runOnMainSync(() -> activity.onConfigurationChanged(
+ InstrumentationRegistry.getInstrumentation()
+ .getContext().getResources().getConfiguration()));
+
+ waitForIdle();
+ onView(withIdFromRuntimeResource("resolver_list"))
+ .check(matches(withGridColumnCount(6)));
+ }
+
// This test is too long and too slow and should not be taken as an example for future tests.
@Test @Ignore
public void testDirectTargetLoggingWithAppTargetNotRankedPortrait()
@@ -1720,6 +1779,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -2035,6 +2095,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -2117,6 +2178,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -2204,6 +2266,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -2273,6 +2336,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(resolvedComponentInfos);
@@ -2421,6 +2485,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
Intent sendIntent = createSendTextIntent();
@@ -2450,6 +2515,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
Intent sendIntent = createSendTextIntent();
@@ -2503,6 +2569,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
Intent chooserIntent = createChooserIntent(createSendTextIntent(),
@@ -2639,6 +2706,7 @@ public class ChooserActivityTest {
when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
// Create caller target which is duplicate with one of app targets
@@ -3032,6 +3100,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
when(
@@ -3041,6 +3110,7 @@ public class ChooserActivityTest {
.getResolversForIntent(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(workResolvedComponentInfos));
when(
@@ -3050,6 +3120,7 @@ public class ChooserActivityTest {
.getResolversForIntentAsUser(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class),
eq(UserHandle.SYSTEM)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
@@ -3063,6 +3134,65 @@ public class ChooserActivityTest {
return withText(getRuntimeResourceId(id, "string"));
}
+ private static GridRecyclerSpanCountMatcher withGridColumnCount(int columnCount) {
+ return new GridRecyclerSpanCountMatcher(Matchers.is(columnCount));
+ }
+
+ private static class GridRecyclerSpanCountMatcher extends
+ BoundedDiagnosingMatcher<View, RecyclerView> {
+
+ private final Matcher<Integer> mIntegerMatcher;
+
+ private GridRecyclerSpanCountMatcher(Matcher<Integer> integerMatcher) {
+ super(RecyclerView.class);
+ this.mIntegerMatcher = integerMatcher;
+ }
+
+ @Override
+ protected void describeMoreTo(Description description) {
+ description.appendText("RecyclerView grid layout span count to match: ");
+ this.mIntegerMatcher.describeTo(description);
+ }
+
+ @Override
+ protected boolean matchesSafely(RecyclerView view, Description mismatchDescription) {
+ int spanCount = ((GridLayoutManager) view.getLayoutManager()).getSpanCount();
+ if (this.mIntegerMatcher.matches(spanCount)) {
+ return true;
+ } else {
+ mismatchDescription.appendText("RecyclerView grid layout span count was ")
+ .appendValue(spanCount);
+ return false;
+ }
+ }
+ }
+
+ private void givenAppTargets(int appCount) {
+ List<ResolvedComponentInfo> resolvedComponentInfos =
+ createResolvedComponentsForTest(appCount);
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resolverListController
+ .getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class)))
+ .thenReturn(resolvedComponentInfos);
+ }
+
+ private void updateMaxTargetsPerRowResource(int targetsPerRow) {
+ ChooserActivityOverrideData.getInstance().resources = Mockito.spy(
+ InstrumentationRegistry.getInstrumentation().getContext().getResources());
+ when(
+ ChooserActivityOverrideData
+ .getInstance()
+ .resources
+ .getInteger(R.integer.config_chooser_max_targets_per_row))
+ .thenReturn(targetsPerRow);
+ }
+
// ChooserWrapperActivity inherits from the framework ChooserActivity, so if the framework
// resources have been updated since the framework was last built/pushed, the inherited behavior
// (which is the focus of our testing) will still be implemented in terms of the old resource
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 43fba529182e..92c05b0fe9fc 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -96,6 +96,7 @@ public class ResolverActivityTest {
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
@@ -127,6 +128,7 @@ public class ResolverActivityTest {
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
waitForIdle();
@@ -171,6 +173,7 @@ public class ResolverActivityTest {
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
waitForIdle();
@@ -203,6 +206,7 @@ public class ResolverActivityTest {
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
when(sOverrides.resolverListController.getLastChosen())
.thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
@@ -273,6 +277,7 @@ public class ResolverActivityTest {
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
@@ -317,6 +322,7 @@ public class ResolverActivityTest {
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
when(sOverrides.resolverListController.getLastChosen())
.thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0));
@@ -807,6 +813,7 @@ public class ResolverActivityTest {
createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
Intent sendIntent = createSendImageIntent();
@@ -831,6 +838,7 @@ public class ResolverActivityTest {
createResolvedComponentsForTest(1);
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
Intent sendIntent = createSendImageIntent();
@@ -888,6 +896,7 @@ public class ResolverActivityTest {
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
when(sOverrides.resolverListController.getLastChosen())
.thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0));
@@ -965,13 +974,16 @@ public class ResolverActivityTest {
List<ResolvedComponentInfo> workResolvedComponentInfos) {
when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
Mockito.isA(List.class),
eq(UserHandle.SYSTEM)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
index e16d44854516..42593f60094b 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
@@ -20,11 +20,14 @@ import static junit.framework.Assert.assertEquals;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.mockito.ArgumentMatchers.intThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.usage.IUsageStatsManager;
@@ -48,6 +51,7 @@ import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
@@ -78,6 +82,8 @@ public class ResolverListControllerTest {
Configuration config = new Configuration();
config.locale = Locale.getDefault();
List<ResolveInfo> services = new ArrayList<>();
+ mUsm = new UsageStatsManager(mMockContext, mMockService);
+ when(mMockContext.getSystemService(Context.USAGE_STATS_SERVICE)).thenReturn(mUsm);
when(mMockPackageManager.queryIntentServices(any(), anyInt())).thenReturn(services);
when(mMockResources.getConfiguration()).thenReturn(config);
when(mMockContext.getResources()).thenReturn(mMockResources);
@@ -112,8 +118,6 @@ public class ResolverListControllerTest {
doAnswer(answer).when(mMockService).reportChooserSelection(
anyString(), anyInt(), anyString(), any(), anyString());
when(mMockContext.getOpPackageName()).thenReturn(refererPackage);
- mUsm = new UsageStatsManager(mMockContext, mMockService);
- when(mMockContext.getSystemService(Context.USAGE_STATS_SERVICE)).thenReturn(mUsm);
mController = new ResolverListController(mMockContext, mMockPackageManager, sendIntent,
refererPackage, UserHandle.USER_CURRENT, /* userHandle */ UserHandle.SYSTEM);
mController.sort(new ArrayList<ResolvedComponentInfo>());
@@ -129,8 +133,6 @@ public class ResolverListControllerTest {
Intent sendIntent = createSendImageIntent(annotation);
String refererPackage = "test_referer_package";
List<ResolvedComponentInfo> resolvedComponents = createResolvedComponentsForTest(10);
- mUsm = new UsageStatsManager(mMockContext, mMockService);
- when(mMockContext.getSystemService(Context.USAGE_STATS_SERVICE)).thenReturn(mUsm);
mController = new ResolverListController(mMockContext, mMockPackageManager, sendIntent,
refererPackage, UserHandle.USER_CURRENT, /* userHandle */ UserHandle.SYSTEM);
List<ResolvedComponentInfo> topKList = new ArrayList<>(resolvedComponents);
@@ -151,6 +153,102 @@ public class ResolverListControllerTest {
sortList, topKList);
}
+ @Test
+ public void getResolversForIntent_usesResultsFromPackageManager() {
+ mockStats();
+ List<ResolveInfo> infos = new ArrayList<>();
+ infos.add(ResolverDataProvider.createResolveInfo(0, UserHandle.USER_CURRENT));
+ when(mMockPackageManager.queryIntentActivitiesAsUser(any(), anyInt(),
+ any(UserHandle.class))).thenReturn(infos);
+ mController = new ResolverListController(mMockContext, mMockPackageManager,
+ createSendImageIntent("test"), null, UserHandle.USER_CURRENT,
+ /* userHandle= */ UserHandle.SYSTEM);
+ List<Intent> intents = new ArrayList<>();
+ intents.add(createActionMainIntent());
+
+ List<ResolvedComponentInfo> resolvers = mController
+ .getResolversForIntent(
+ /* shouldGetResolvedFilter= */ true,
+ /* shouldGetActivityMetadata= */ true,
+ /* shouldGetOnlyDefaultActivities= */ true,
+ intents);
+
+ assertThat(resolvers, hasSize(1));
+ assertThat(resolvers.get(0).getResolveInfoAt(0), is(infos.get(0)));
+ }
+
+ @Test
+ public void getResolversForIntent_shouldGetOnlyDefaultActivitiesTrue_addsFlag() {
+ mockStats();
+ List<ResolveInfo> infos = new ArrayList<>();
+ infos.add(ResolverDataProvider.createResolveInfo(0, UserHandle.USER_CURRENT));
+ when(mMockPackageManager.queryIntentActivitiesAsUser(any(), anyInt(),
+ any(UserHandle.class))).thenReturn(infos);
+ mController = new ResolverListController(mMockContext, mMockPackageManager,
+ createSendImageIntent("test"), null, UserHandle.USER_CURRENT,
+ /* userHandle= */ UserHandle.SYSTEM);
+ List<Intent> intents = new ArrayList<>();
+ intents.add(createActionMainIntent());
+
+ mController
+ .getResolversForIntent(
+ /* shouldGetResolvedFilter= */ true,
+ /* shouldGetActivityMetadata= */ true,
+ /* shouldGetOnlyDefaultActivities= */ true,
+ intents);
+
+ verify(mMockPackageManager).queryIntentActivitiesAsUser(any(),
+ containsFlag(PackageManager.MATCH_DEFAULT_ONLY), any());
+ }
+
+ @Test
+ public void getResolversForIntent_shouldGetOnlyDefaultActivitiesFalse_doesNotAddFlag() {
+ mockStats();
+ List<ResolveInfo> infos = new ArrayList<>();
+ infos.add(ResolverDataProvider.createResolveInfo(0, UserHandle.USER_CURRENT));
+ when(mMockPackageManager.queryIntentActivitiesAsUser(any(), anyInt(),
+ any(UserHandle.class))).thenReturn(infos);
+ mController = new ResolverListController(mMockContext, mMockPackageManager,
+ createSendImageIntent("test"), null, UserHandle.USER_CURRENT,
+ /* userHandle= */ UserHandle.SYSTEM);
+ List<Intent> intents = new ArrayList<>();
+ intents.add(createActionMainIntent());
+
+ mController
+ .getResolversForIntent(
+ /* shouldGetResolvedFilter= */ true,
+ /* shouldGetActivityMetadata= */ true,
+ /* shouldGetOnlyDefaultActivities= */ false,
+ intents);
+
+ verify(mMockPackageManager).queryIntentActivitiesAsUser(any(),
+ doesNotContainFlag(PackageManager.MATCH_DEFAULT_ONLY), any());
+ }
+
+ private int containsFlag(int flag) {
+ return intThat(new FlagMatcher(flag, /* contains= */ true));
+ }
+
+ private int doesNotContainFlag(int flag) {
+ return intThat(new FlagMatcher(flag, /* contains= */ false));
+ }
+
+ public static class FlagMatcher implements ArgumentMatcher<Integer> {
+
+ private final int mFlag;
+ private final boolean mContains;
+
+ public FlagMatcher(int flag, boolean contains) {
+ mFlag = flag;
+ mContains = contains;
+ }
+
+ @Override
+ public boolean matches(Integer integer) {
+ return ((integer & mFlag) != 0) == mContains;
+ }
+ }
+
private UsageStats initStats(String packageName, String action,
String annotation, int count) {
ArrayMap<String, ArrayMap<String, Integer>> chooserCounts = new ArrayMap<>();
@@ -174,6 +272,24 @@ public class ResolverListControllerTest {
return sendIntent;
}
+ private Intent createActionMainIntent() {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_MAIN);
+ sendIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ return sendIntent;
+ }
+
+ private void mockStats() {
+ final List<UsageStats> slices = new ArrayList<>();
+ ParceledListSlice<UsageStats> stats = new ParceledListSlice<>(slices);
+ try {
+ when(mMockService.queryUsageStats(anyInt(), anyLong(), anyLong(), anyString(),
+ anyInt())).thenReturn(stats);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
private Integer getCount(
UsageStatsManager usm, String packageName, String action, String annotation) {
if (usm == null) {
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 09fc7ea6fffd..e068730e9bda 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -36,6 +36,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.only;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -83,6 +84,7 @@ public class FrameTrackerTest {
private StatsLogWrapper mStatsLog;
private ArgumentCaptor<OnJankDataListener> mListenerCapture;
private SurfaceControl mSurfaceControl;
+ private ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
@Before
public void setup() {
@@ -99,6 +101,8 @@ public class FrameTrackerTest {
mSurfaceControl = new SurfaceControl.Builder().setName("Surface").build();
mViewRootWrapper = mock(ViewRootWrapper.class);
when(mViewRootWrapper.getSurfaceControl()).thenReturn(mSurfaceControl);
+ doNothing().when(mViewRootWrapper).addSurfaceChangedCallback(any());
+ doNothing().when(mViewRootWrapper).removeSurfaceChangedCallback(any());
mSurfaceControlWrapper = mock(SurfaceControlWrapper.class);
mListenerCapture = ArgumentCaptor.forClass(OnJankDataListener.class);
@@ -109,23 +113,29 @@ public class FrameTrackerTest {
mChoreographer = mock(ChoreographerWrapper.class);
mStatsLog = mock(StatsLogWrapper.class);
+ mRunnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class);
}
private FrameTracker spyFrameTracker(int cuj, String postfix, boolean surfaceOnly) {
+ InteractionJankMonitor monitor = mock(InteractionJankMonitor.class);
Handler handler = mRule.getActivity().getMainThreadHandler();
Session session = new Session(cuj, postfix);
Configuration config = mock(Configuration.class);
when(config.isSurfaceOnly()).thenReturn(surfaceOnly);
when(config.getSurfaceControl()).thenReturn(mSurfaceControl);
when(config.shouldDeferMonitor()).thenReturn(true);
+ View view = mRule.getActivity().getWindow().getDecorView();
+ Handler spyHandler = spy(new Handler(handler.getLooper()));
+ when(config.getView()).thenReturn(surfaceOnly ? null : view);
+ when(config.getHandler()).thenReturn(spyHandler);
FrameTracker frameTracker = Mockito.spy(
- new FrameTracker(session, handler, mRenderer, mViewRootWrapper,
+ new FrameTracker(monitor, session, spyHandler, mRenderer, mViewRootWrapper,
mSurfaceControlWrapper, mChoreographer, mWrapper, mStatsLog,
/* traceThresholdMissedFrames= */ 1,
/* traceThresholdFrameTimeMillis= */ -1,
/* FrameTrackerListener= */ null, config));
doNothing().when(frameTracker).triggerPerfetto();
- doNothing().when(frameTracker).postTraceStartMarker();
+ doNothing().when(frameTracker).postTraceStartMarker(mRunnableArgumentCaptor.capture());
return frameTracker;
}
@@ -140,6 +150,7 @@ public class FrameTrackerTest {
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
+ mRunnableArgumentCaptor.getValue().run();
verify(mRenderer, only()).addObserver(any());
// send first frame with a long duration - should not be taken into account
@@ -173,6 +184,7 @@ public class FrameTrackerTest {
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
+ mRunnableArgumentCaptor.getValue().run();
verify(mRenderer, only()).addObserver(any());
// send first frame - not janky
@@ -208,6 +220,7 @@ public class FrameTrackerTest {
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
+ mRunnableArgumentCaptor.getValue().run();
verify(mRenderer, only()).addObserver(any());
// send first frame - janky
@@ -243,6 +256,7 @@ public class FrameTrackerTest {
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
+ mRunnableArgumentCaptor.getValue().run();
verify(mRenderer, only()).addObserver(any());
// send first frame - not janky
@@ -278,6 +292,7 @@ public class FrameTrackerTest {
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
+ mRunnableArgumentCaptor.getValue().run();
verify(mRenderer, only()).addObserver(any());
// send first frame - not janky
@@ -319,6 +334,7 @@ public class FrameTrackerTest {
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
+ mRunnableArgumentCaptor.getValue().run();
verify(mRenderer, only()).addObserver(any());
// send first frame - not janky
@@ -332,7 +348,7 @@ public class FrameTrackerTest {
tracker.end(FrameTracker.REASON_END_NORMAL);
// Send incomplete callback for 102L
- sendSfFrame(102L, JANK_NONE);
+ sendSfFrame(tracker, 102L, JANK_NONE);
// Send janky but complete callbck fo 103L
sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L);
@@ -356,6 +372,7 @@ public class FrameTrackerTest {
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
+ mRunnableArgumentCaptor.getValue().run();
verify(mRenderer).addObserver(any());
// First frame - not janky
@@ -380,6 +397,7 @@ public class FrameTrackerTest {
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
+ mRunnableArgumentCaptor.getValue().run();
verify(mRenderer, only()).addObserver(any());
// end the trace session
@@ -403,6 +421,7 @@ public class FrameTrackerTest {
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
+ mRunnableArgumentCaptor.getValue().run();
verify(mRenderer, only()).addObserver(any());
// end the trace session at the same vsync id, end vsync id will less than the begin one.
@@ -444,6 +463,7 @@ public class FrameTrackerTest {
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
+ mRunnableArgumentCaptor.getValue().run();
verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
// First frame - not janky
@@ -479,6 +499,7 @@ public class FrameTrackerTest {
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
+ mRunnableArgumentCaptor.getValue().run();
verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
// First frame - janky
@@ -514,6 +535,7 @@ public class FrameTrackerTest {
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
+ mRunnableArgumentCaptor.getValue().run();
verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
// First frame - not janky
@@ -548,6 +570,7 @@ public class FrameTrackerTest {
CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
when(mChoreographer.getVsyncId()).thenReturn(100L);
tracker.begin();
+ mRunnableArgumentCaptor.getValue().run();
verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 100L);
sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
@@ -594,7 +617,7 @@ public class FrameTrackerTest {
if (!tracker.mSurfaceOnly) {
sendHwuiFrame(tracker, durationMillis, vsyncId, firstWindowFrame);
}
- sendSfFrame(vsyncId, jankType);
+ sendSfFrame(tracker, vsyncId, jankType);
}
private void sendHwuiFrame(FrameTracker tracker, long durationMillis, long vsyncId,
@@ -604,12 +627,18 @@ public class FrameTrackerTest {
.getMetric(FrameMetrics.FIRST_DRAW_FRAME);
doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis))
.when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION);
+ final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+ doNothing().when(tracker).postCallback(captor.capture());
tracker.onFrameMetricsAvailable(0);
+ captor.getValue().run();
}
- private void sendSfFrame(long vsyncId, @JankType int jankType) {
+ private void sendSfFrame(FrameTracker tracker, long vsyncId, @JankType int jankType) {
+ final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+ doNothing().when(tracker).postCallback(captor.capture());
mListenerCapture.getValue().onJankDataAvailable(new JankData[] {
new JankData(vsyncId, jankType)
});
+ captor.getValue().run();
}
}
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index 5a6fd5317bbc..d96f041c13f8 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -93,7 +93,7 @@ public class InteractionJankMonitorTest {
@Test
public void testBeginEnd() {
InteractionJankMonitor monitor = createMockedInteractionJankMonitor();
- FrameTracker tracker = createMockedFrameTracker(null);
+ FrameTracker tracker = createMockedFrameTracker(monitor, null);
doReturn(tracker).when(monitor).createFrameTracker(any(), any());
doNothing().when(tracker).begin();
doReturn(true).when(tracker).end(anyInt());
@@ -134,7 +134,7 @@ public class InteractionJankMonitorTest {
public void testBeginTimeout() {
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
InteractionJankMonitor monitor = createMockedInteractionJankMonitor();
- FrameTracker tracker = createMockedFrameTracker(null);
+ FrameTracker tracker = createMockedFrameTracker(monitor, null);
doReturn(tracker).when(monitor).createFrameTracker(any(), any());
doNothing().when(tracker).begin();
doReturn(true).when(tracker).cancel(anyInt());
@@ -180,7 +180,8 @@ public class InteractionJankMonitorTest {
return monitor;
}
- private FrameTracker createMockedFrameTracker(FrameTracker.FrameTrackerListener listener) {
+ private FrameTracker createMockedFrameTracker(InteractionJankMonitor monitor,
+ FrameTracker.FrameTrackerListener listener) {
Session session = spy(new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX));
doReturn(false).when(session).logToStatsd();
@@ -190,6 +191,7 @@ public class InteractionJankMonitorTest {
ViewRootWrapper viewRoot = spy(new ViewRootWrapper(mView.getViewRootImpl()));
doNothing().when(viewRoot).addSurfaceChangedCallback(any());
+ doNothing().when(viewRoot).removeSurfaceChangedCallback(any());
SurfaceControlWrapper surfaceControl = mock(SurfaceControlWrapper.class);
doNothing().when(surfaceControl).addJankStatsListener(any(), any());
@@ -200,15 +202,18 @@ public class InteractionJankMonitorTest {
Configuration configuration = mock(Configuration.class);
when(configuration.isSurfaceOnly()).thenReturn(false);
+ when(configuration.getView()).thenReturn(mView);
+ when(configuration.getHandler()).thenReturn(mView.getHandler());
- FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
+ FrameTracker tracker = spy(new FrameTracker(monitor, session, mWorker.getThreadHandler(),
threadedRenderer, viewRoot, surfaceControl, choreographer,
new FrameMetricsWrapper(), new StatsLogWrapper(),
/* traceThresholdMissedFrames= */ 1,
/* traceThresholdFrameTimeMillis= */ -1, listener, configuration));
- doNothing().when(tracker).postTraceStartMarker();
+ doNothing().when(tracker).postTraceStartMarker(any());
doNothing().when(tracker).triggerPerfetto();
+ doReturn(configuration.getHandler()).when(tracker).getHandler();
return tracker;
}
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
index 0f05be06bff6..c53fb23ba948 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
@@ -20,6 +20,7 @@ import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_B
import static com.google.common.truth.Truth.assertThat;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.Parcel;
import android.os.UserHandle;
@@ -48,7 +49,11 @@ public class RegisterStatusBarResultTest {
final ArrayMap<String, StatusBarIcon> iconMap = new ArrayMap<>();
iconMap.put(dumyIconKey, new StatusBarIcon("com.android.internal.statusbar.test",
UserHandle.of(100), 123, 1, 2, "dummyIconDescription"));
-
+ final LetterboxDetails letterboxDetails = new LetterboxDetails(
+ /* letterboxInnerBounds= */ new Rect(1, 2, 3, 4),
+ /* letterboxFullBounds= */ new Rect(5, 6, 7, 8),
+ /* appAppearance= */ 321
+ );
final RegisterStatusBarResult original = new RegisterStatusBarResult(iconMap,
0x2 /* disabledFlags1 */,
0x4 /* appearance */,
@@ -62,7 +67,8 @@ public class RegisterStatusBarResultTest {
BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE,
new InsetsVisibilities() /* requestedVisibilities */,
"test" /* packageName */,
- new int[0] /* transientBarTypes */);
+ new int[0] /* transientBarTypes */,
+ new LetterboxDetails[] {letterboxDetails});
final RegisterStatusBarResult copy = clone(original);
@@ -84,6 +90,7 @@ public class RegisterStatusBarResultTest {
assertThat(copy.mRequestedVisibilities).isEqualTo(original.mRequestedVisibilities);
assertThat(copy.mPackageName).isEqualTo(original.mPackageName);
assertThat(copy.mTransientBarTypes).isEqualTo(original.mTransientBarTypes);
+ assertThat(copy.mLetterboxDetails).isEqualTo(original.mLetterboxDetails);
}
private RegisterStatusBarResult clone(RegisterStatusBarResult original) {
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 8d3751e6ad6c..47f70ddf2d42 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -207,8 +207,8 @@ public class ActivityThreadClientTest {
final Configuration currentConfig = new Configuration();
assertFalse("Must not report change if no public diff",
- shouldReportChange(0 /* publicDiff */, currentConfig, newConfig,
- null /* sizeBuckets */, 0 /* handledConfigChanges */));
+ shouldReportChange(currentConfig, newConfig, null /* sizeBuckets */,
+ 0 /* handledConfigChanges */));
final int[] verticalThresholds = {100, 400};
final SizeConfigurationBuckets buckets = new SizeConfigurationBuckets(
@@ -221,25 +221,25 @@ public class ActivityThreadClientTest {
newConfig.screenHeightDp = 300;
assertFalse("Must not report changes if the diff is small and not handled",
- shouldReportChange(CONFIG_SCREEN_SIZE /* publicDiff */, currentConfig,
- newConfig, buckets, CONFIG_FONT_SCALE /* handledConfigChanges */));
+ shouldReportChange(currentConfig, newConfig, buckets,
+ CONFIG_FONT_SCALE /* handledConfigChanges */));
assertTrue("Must report changes if the small diff is handled",
- shouldReportChange(CONFIG_SCREEN_SIZE /* publicDiff */, currentConfig, newConfig,
- buckets, CONFIG_SCREEN_SIZE /* handledConfigChanges */));
+ shouldReportChange(currentConfig, newConfig, buckets,
+ CONFIG_SCREEN_SIZE /* handledConfigChanges */));
currentConfig.fontScale = 0.8f;
newConfig.fontScale = 1.2f;
assertTrue("Must report handled changes regardless of small unhandled change",
- shouldReportChange(CONFIG_SCREEN_SIZE | CONFIG_FONT_SCALE /* publicDiff */,
- currentConfig, newConfig, buckets, CONFIG_FONT_SCALE /* handledConfigChanges */));
+ shouldReportChange(currentConfig, newConfig, buckets,
+ CONFIG_FONT_SCALE /* handledConfigChanges */));
newConfig.screenHeightDp = 500;
assertFalse("Must not report changes if there's unhandled big changes",
- shouldReportChange(CONFIG_SCREEN_SIZE | CONFIG_FONT_SCALE /* publicDiff */,
- currentConfig, newConfig, buckets, CONFIG_FONT_SCALE /* handledConfigChanges */));
+ shouldReportChange(currentConfig, newConfig, buckets,
+ CONFIG_FONT_SCALE /* handledConfigChanges */));
}
private void recreateAndVerifyNoRelaunch(ActivityThread activityThread, TestActivity activity) {
diff --git a/core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java b/core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java
index fa4aa803c75e..ed857e8fc960 100644
--- a/core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java
+++ b/core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java
@@ -88,26 +88,15 @@ public class SizeConfigurationBucketsTest {
}
/**
- * Tests that null size configuration buckets unflips the correct configuration flags.
+ * Tests that {@code null} size configuration buckets do not unflip the configuration flags.
*/
@Test
public void testNullSizeConfigurationBuckets() {
- // Check that all 3 size configurations are filtered out of the diff if the buckets are null
- // and non-size attributes of screen layout are unchanged. Add a non-size related config
- // change (i.e. CONFIG_LOCALE) to test that the diff is not set to zero.
final int diff = CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_SCREEN_LAYOUT
| CONFIG_LOCALE;
final int filteredDiffNonSizeLayoutUnchanged = SizeConfigurationBuckets.filterDiff(diff,
Configuration.EMPTY, Configuration.EMPTY, null);
- assertEquals(CONFIG_LOCALE, filteredDiffNonSizeLayoutUnchanged);
-
- // Check that only screen size and smallest screen size are filtered out of the diff if the
- // buckets are null and non-size attributes of screen layout are changed.
- final Configuration newConfig = new Configuration();
- newConfig.screenLayout |= SCREENLAYOUT_ROUND_YES;
- final int filteredDiffNonSizeLayoutChanged = SizeConfigurationBuckets.filterDiff(diff,
- Configuration.EMPTY, newConfig, null);
- assertEquals(CONFIG_SCREEN_LAYOUT | CONFIG_LOCALE, filteredDiffNonSizeLayoutChanged);
+ assertEquals(diff, filteredDiffNonSizeLayoutUnchanged);
}
/**
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index f7c2b732a77b..f01e2e809c21 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -163,6 +163,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1941440781": {
+ "message": "Creating Pending Move-to-back: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Task.java"
+ },
"-1939861963": {
"message": "Create root task displayId=%d winMode=%d",
"level": "VERBOSE",
@@ -571,12 +577,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1521427940": {
- "message": "commitVisibility: %s: visible=%b mVisibleRequested=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-1517908912": {
"message": "requestScrollCapture: caught exception dispatching to window.token=%s",
"level": "WARN",
@@ -757,6 +757,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1383884640": {
+ "message": " allReady query: used=%b override=%b defer=%d states=[%s]",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"-1376035390": {
"message": "No task found",
"level": "DEBUG",
@@ -1501,6 +1507,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
+ "-636553602": {
+ "message": "commitVisibility: %s: visible=%b visibleRequested=%b, isInTransition=%b, runningAnimation=%b, caller=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"-635082269": {
"message": "******** booted=%b msg=%b haveBoot=%b haveApp=%b haveWall=%b wallEnabled=%b haveKeyguard=%b",
"level": "INFO",
@@ -1693,6 +1705,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "-417730399": {
+ "message": "Preparing to sync a window that was already in the sync, so try dropping buffer. win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-415865166": {
"message": "findFocusedWindow: Found new focus @ %s",
"level": "VERBOSE",
@@ -2071,6 +2089,12 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
+ "-57572004": {
+ "message": "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b canCustomizeAppTransition=%b Callers=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
"-55185509": {
"message": "setFocusedTask: taskId=%d touchedActivity=%s",
"level": "DEBUG",
@@ -2113,6 +2137,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
+ "-4263657": {
+ "message": "Got a buffer for request id=%d but latest request is id=%d. Since the buffer is out-of-date, drop it. win=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"3593205": {
"message": "commitVisibility: %s: visible=%b mVisibleRequested=%b",
"level": "VERBOSE",
@@ -2143,6 +2173,12 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimationController.java"
},
+ "25888308": {
+ "message": "Resize reasons for w=%s: %s configChanged=%b dragResizingChanged=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_RESIZE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"35398067": {
"message": "goodToGo(): onAnimationStart, transit=%s, apps=%d, wallpapers=%d, nonApps=%d",
"level": "DEBUG",
@@ -2527,12 +2563,6 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "352982444": {
- "message": " allReady query: used=%b override=%b states=[%s]",
- "level": "VERBOSE",
- "group": "WM_DEBUG_WINDOW_TRANSITIONS",
- "at": "com\/android\/server\/wm\/Transition.java"
- },
"355720268": {
"message": "stopFreezingDisplayLocked: Unfreezing now",
"level": "DEBUG",
@@ -2575,6 +2605,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
+ "385237117": {
+ "message": "moveFocusableActivityToTop: already on top and focused, activity=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_FOCUS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"385595355": {
"message": "Starting animation on %s: type=%d, anim=%s",
"level": "VERBOSE",
@@ -2797,12 +2833,6 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
- "625447638": {
- "message": "Resize reasons for w=%s: %s configChanged=%b dragResizingChanged=%b reportOrientationChanged=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_RESIZE",
- "at": "com\/android\/server\/wm\/WindowState.java"
- },
"628276090": {
"message": "Delaying app transition for screen rotation animation to finish",
"level": "VERBOSE",
@@ -3049,6 +3079,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "898260097": {
+ "message": "Creating Pending Pip-Enter: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+ },
"898863925": {
"message": "Attempted to add QS dialog window with unknown token %s. Aborting.",
"level": "WARN",
@@ -3379,11 +3415,11 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1246035185": {
- "message": "stopFreezingDisplayLocked: Returning waitingForConfig=%b, waitingForRemoteRotation=%b, mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, mClientFreezingScreen=%b, mOpeningApps.size()=%d",
+ "1239439010": {
+ "message": "moveFocusableActivityToTop: set focused, activity=%s",
"level": "DEBUG",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ "group": "WM_DEBUG_FOCUS",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
},
"1252594551": {
"message": "Window types in WindowContext and LayoutParams.type should match! Type from LayoutParams is %d, but type from WindowContext is %d",
@@ -3481,6 +3517,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/ScreenRotationAnimation.java"
},
+ "1360176455": {
+ "message": "stopFreezingDisplayLocked: Returning waitingForConfig=%b, waitingForRemoteDisplayChange=%b, mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, mClientFreezingScreen=%b, mOpeningApps.size()=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"1364126018": {
"message": "Resumed activity; dropping state of: %s",
"level": "INFO",
@@ -3505,6 +3547,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/TaskDisplayArea.java"
},
+ "1393721079": {
+ "message": "Starting remote display change: from [rot = %d], to [%dx%d, rot = %d]",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONFIGURATION",
+ "at": "com\/android\/server\/wm\/RemoteDisplayChangeController.java"
+ },
"1396893178": {
"message": "createRootTask unknown displayId=%d",
"level": "ERROR",
@@ -3955,12 +4003,6 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "1856211951": {
- "message": "moveFocusableActivityToTop: already on top, activity=%s",
- "level": "DEBUG",
- "group": "WM_DEBUG_FOCUS",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"1856783490": {
"message": "resumeTopActivity: Restarting %s",
"level": "DEBUG",
@@ -4171,6 +4213,12 @@
"group": "WM_DEBUG_ANIM",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "2079410261": {
+ "message": "applyAnimation: override requested, but it is prohibited by policy.",
+ "level": "ERROR",
+ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
+ "at": "com\/android\/server\/wm\/AppTransition.java"
+ },
"2083556954": {
"message": "Set mOrientationChanging of %s",
"level": "VERBOSE",
@@ -4224,12 +4272,6 @@
"level": "VERBOSE",
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayRotation.java"
- },
- "2137411379": {
- "message": "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b Callers=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
- "at": "com\/android\/server\/wm\/AppTransition.java"
}
},
"groups": {
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 84e949a18c52..f8c015f28a50 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -932,7 +932,7 @@
<font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font>
</family>
<family lang="und-Laoo" variant="compact">
- <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf
+ <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf
</font>
<font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
</family>
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 1629b6ace35d..239621eeed1e 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -40,6 +40,7 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.net.Uri;
import android.os.Build;
+import android.os.Trace;
import android.system.ErrnoException;
import android.system.Os;
import android.util.DisplayMetrics;
@@ -223,13 +224,21 @@ public final class ImageDecoder implements AutoCloseable {
public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
return nCreate(mData, mOffset, mLength, preferAnimation, this);
}
+
+ @Override
+ public String toString() {
+ return "ByteArraySource{len=" + mLength + "}";
+ }
}
private static class ByteBufferSource extends Source {
ByteBufferSource(@NonNull ByteBuffer buffer) {
mBuffer = buffer;
+ mLength = mBuffer.limit() - mBuffer.position();
}
+
private final ByteBuffer mBuffer;
+ private final int mLength;
@Override
public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
@@ -241,6 +250,11 @@ public final class ImageDecoder implements AutoCloseable {
ByteBuffer buffer = mBuffer.slice();
return nCreate(buffer, buffer.position(), buffer.limit(), preferAnimation, this);
}
+
+ @Override
+ public String toString() {
+ return "ByteBufferSource{len=" + mLength + "}";
+ }
}
private static class ContentResolverSource extends Source {
@@ -285,6 +299,16 @@ public final class ImageDecoder implements AutoCloseable {
return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
}
+
+ @Override
+ public String toString() {
+ String uri = mUri.toString();
+ if (uri.length() > 90) {
+ // We want to keep the Uri usable - usually the authority and the end is important.
+ uri = uri.substring(0, 80) + ".." + uri.substring(uri.length() - 10);
+ }
+ return "ContentResolverSource{uri=" + uri + "}";
+ }
}
@NonNull
@@ -399,6 +423,11 @@ public final class ImageDecoder implements AutoCloseable {
return createFromStream(is, false, preferAnimation, this);
}
}
+
+ @Override
+ public String toString() {
+ return "InputStream{s=" + mInputStream + "}";
+ }
}
/**
@@ -444,6 +473,11 @@ public final class ImageDecoder implements AutoCloseable {
return createFromAsset(ais, preferAnimation, this);
}
}
+
+ @Override
+ public String toString() {
+ return "AssetInputStream{s=" + mAssetInputStream + "}";
+ }
}
private static class ResourceSource extends Source {
@@ -485,6 +519,17 @@ public final class ImageDecoder implements AutoCloseable {
return createFromAsset((AssetInputStream) is, preferAnimation, this);
}
+
+ @Override
+ public String toString() {
+ // Try to return a human-readable name for debugging purposes.
+ try {
+ return "Resource{name=" + mResources.getResourceName(mResId) + "}";
+ } catch (Resources.NotFoundException e) {
+ // It's ok if we don't find it, fall back to ID.
+ }
+ return "Resource{id=" + mResId + "}";
+ }
}
/**
@@ -521,6 +566,11 @@ public final class ImageDecoder implements AutoCloseable {
InputStream is = mAssets.open(mFileName);
return createFromAsset((AssetInputStream) is, preferAnimation, this);
}
+
+ @Override
+ public String toString() {
+ return "AssetSource{file=" + mFileName + "}";
+ }
}
private static class FileSource extends Source {
@@ -534,6 +584,11 @@ public final class ImageDecoder implements AutoCloseable {
public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
return createFromFile(mFile, preferAnimation, this);
}
+
+ @Override
+ public String toString() {
+ return "FileSource{file=" + mFile + "}";
+ }
}
private static class CallableSource extends Source {
@@ -557,6 +612,11 @@ public final class ImageDecoder implements AutoCloseable {
}
return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
}
+
+ @Override
+ public String toString() {
+ return "CallableSource{obj=" + mCallable.toString() + "}";
+ }
}
/**
@@ -1763,61 +1823,65 @@ public final class ImageDecoder implements AutoCloseable {
@NonNull
private static Drawable decodeDrawableImpl(@NonNull Source src,
@Nullable OnHeaderDecodedListener listener) throws IOException {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ImageDecoder#decodeDrawable");
try (ImageDecoder decoder = src.createImageDecoder(true /*preferAnimation*/)) {
decoder.mSource = src;
decoder.callHeaderDecoded(listener, src);
- if (decoder.mUnpremultipliedRequired) {
- // Though this could be supported (ignored) for opaque images,
- // it seems better to always report this error.
- throw new IllegalStateException("Cannot decode a Drawable " +
- "with unpremultiplied pixels!");
- }
+ try (ImageDecoderSourceTrace unused = new ImageDecoderSourceTrace(decoder)) {
+ if (decoder.mUnpremultipliedRequired) {
+ // Though this could be supported (ignored) for opaque images,
+ // it seems better to always report this error.
+ throw new IllegalStateException(
+ "Cannot decode a Drawable with unpremultiplied pixels!");
+ }
- if (decoder.mMutable) {
- throw new IllegalStateException("Cannot decode a mutable " +
- "Drawable!");
- }
+ if (decoder.mMutable) {
+ throw new IllegalStateException("Cannot decode a mutable Drawable!");
+ }
- // this call potentially manipulates the decoder so it must be performed prior to
- // decoding the bitmap and after decode set the density on the resulting bitmap
- final int srcDensity = decoder.computeDensity(src);
- if (decoder.mAnimated) {
- // AnimatedImageDrawable calls postProcessAndRelease only if
- // mPostProcessor exists.
- ImageDecoder postProcessPtr = decoder.mPostProcessor == null ?
- null : decoder;
- decoder.checkState(true);
- Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
- postProcessPtr, decoder.mDesiredWidth,
- decoder.mDesiredHeight, decoder.getColorSpacePtr(),
- decoder.checkForExtended(), srcDensity,
- src.computeDstDensity(), decoder.mCropRect,
- decoder.mInputStream, decoder.mAssetFd);
- // d has taken ownership of these objects.
- decoder.mInputStream = null;
- decoder.mAssetFd = null;
- return d;
- }
+ // this call potentially manipulates the decoder so it must be performed prior to
+ // decoding the bitmap and after decode set the density on the resulting bitmap
+ final int srcDensity = decoder.computeDensity(src);
+ if (decoder.mAnimated) {
+ // AnimatedImageDrawable calls postProcessAndRelease only if
+ // mPostProcessor exists.
+ ImageDecoder postProcessPtr = decoder.mPostProcessor == null ? null : decoder;
+ decoder.checkState(true);
+ Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
+ postProcessPtr, decoder.mDesiredWidth,
+ decoder.mDesiredHeight, decoder.getColorSpacePtr(),
+ decoder.checkForExtended(), srcDensity,
+ src.computeDstDensity(), decoder.mCropRect,
+ decoder.mInputStream, decoder.mAssetFd);
+ // d has taken ownership of these objects.
+ decoder.mInputStream = null;
+ decoder.mAssetFd = null;
+ return d;
+ }
- Bitmap bm = decoder.decodeBitmapInternal();
- bm.setDensity(srcDensity);
+ Bitmap bm = decoder.decodeBitmapInternal();
+ bm.setDensity(srcDensity);
- Resources res = src.getResources();
- byte[] np = bm.getNinePatchChunk();
- if (np != null && NinePatch.isNinePatchChunk(np)) {
- Rect opticalInsets = new Rect();
- bm.getOpticalInsets(opticalInsets);
- Rect padding = decoder.mOutPaddingRect;
- if (padding == null) {
- padding = new Rect();
+ Resources res = src.getResources();
+ byte[] np = bm.getNinePatchChunk();
+ if (np != null && NinePatch.isNinePatchChunk(np)) {
+ Rect opticalInsets = new Rect();
+ bm.getOpticalInsets(opticalInsets);
+ Rect padding = decoder.mOutPaddingRect;
+ if (padding == null) {
+ padding = new Rect();
+ }
+ nGetPadding(decoder.mNativePtr, padding);
+ return new NinePatchDrawable(res, bm, np, padding,
+ opticalInsets, null);
}
- nGetPadding(decoder.mNativePtr, padding);
- return new NinePatchDrawable(res, bm, np, padding,
- opticalInsets, null);
- }
- return new BitmapDrawable(res, bm);
+ return new BitmapDrawable(res, bm);
+ }
+ } finally {
+ // Close the ImageDecoder#decode trace.
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
@@ -1867,26 +1931,51 @@ public final class ImageDecoder implements AutoCloseable {
@NonNull
private static Bitmap decodeBitmapImpl(@NonNull Source src,
@Nullable OnHeaderDecodedListener listener) throws IOException {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ImageDecoder#decodeBitmap");
try (ImageDecoder decoder = src.createImageDecoder(false /*preferAnimation*/)) {
decoder.mSource = src;
decoder.callHeaderDecoded(listener, src);
+ try (ImageDecoderSourceTrace unused = new ImageDecoderSourceTrace(decoder)) {
+ // this call potentially manipulates the decoder so it must be performed prior to
+ // decoding the bitmap
+ final int srcDensity = decoder.computeDensity(src);
+ Bitmap bm = decoder.decodeBitmapInternal();
+ bm.setDensity(srcDensity);
- // this call potentially manipulates the decoder so it must be performed prior to
- // decoding the bitmap
- final int srcDensity = decoder.computeDensity(src);
- Bitmap bm = decoder.decodeBitmapInternal();
- bm.setDensity(srcDensity);
-
- Rect padding = decoder.mOutPaddingRect;
- if (padding != null) {
- byte[] np = bm.getNinePatchChunk();
- if (np != null && NinePatch.isNinePatchChunk(np)) {
- nGetPadding(decoder.mNativePtr, padding);
+ Rect padding = decoder.mOutPaddingRect;
+ if (padding != null) {
+ byte[] np = bm.getNinePatchChunk();
+ if (np != null && NinePatch.isNinePatchChunk(np)) {
+ nGetPadding(decoder.mNativePtr, padding);
+ }
}
+ return bm;
}
+ } finally {
+ // Close the ImageDecoder#decode trace.
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ }
+ }
- return bm;
+ /**
+ * This describes the decoder in traces to ease debugging. It has to be called after
+ * header has been decoded and width/height have been populated. It should be used
+ * inside a try-with-resources call to automatically complete the trace.
+ */
+ private static AutoCloseable traceDecoderSource(ImageDecoder decoder) {
+ final boolean resourceTracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_RESOURCES);
+ if (resourceTracingEnabled) {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, describeDecoderForTrace(decoder));
}
+
+ return new AutoCloseable() {
+ @Override
+ public void close() throws Exception {
+ if (resourceTracingEnabled) {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ }
+ }
+ };
}
// This method may modify the decoder so it must be called prior to performing the decode
@@ -1994,6 +2083,66 @@ public final class ImageDecoder implements AutoCloseable {
}
}
+ /**
+ * Returns a short string describing what passed ImageDecoder is loading -
+ * it reports image dimensions, desired dimensions (if any) and source resource.
+ *
+ * The string appears in perf traces to simplify search for slow or memory intensive
+ * image loads.
+ *
+ * Example: ID#w=300;h=250;dw=150;dh=150;src=Resource{name=@resource}
+ *
+ * @hide
+ */
+ private static String describeDecoderForTrace(@NonNull ImageDecoder decoder) {
+ StringBuilder builder = new StringBuilder();
+ // Source dimensions
+ builder.append("ID#w=");
+ builder.append(decoder.mWidth);
+ builder.append(";h=");
+ builder.append(decoder.mHeight);
+ // Desired dimensions (if present)
+ if (decoder.mDesiredWidth != decoder.mWidth
+ || decoder.mDesiredHeight != decoder.mHeight) {
+ builder.append(";dw=");
+ builder.append(decoder.mDesiredWidth);
+ builder.append(";dh=");
+ builder.append(decoder.mDesiredHeight);
+ }
+ // Source description
+ builder.append(";src=");
+ builder.append(decoder.mSource);
+ return builder.toString();
+ }
+
+ /**
+ * Records a trace with information about the source being decoded - dimensions,
+ * desired dimensions and source information.
+ *
+ * It significantly eases debugging of slow resource loads on main thread and
+ * possible large memory consumers.
+ *
+ * @hide
+ */
+ private static final class ImageDecoderSourceTrace implements AutoCloseable {
+
+ private final boolean mResourceTracingEnabled;
+
+ ImageDecoderSourceTrace(ImageDecoder decoder) {
+ mResourceTracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_RESOURCES);
+ if (mResourceTracingEnabled) {
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, describeDecoderForTrace(decoder));
+ }
+ }
+
+ @Override
+ public void close() {
+ if (mResourceTracingEnabled) {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ }
+ }
+ }
+
private static native ImageDecoder nCreate(long asset,
boolean preferAnimation, Source src) throws IOException;
private static native ImageDecoder nCreate(ByteBuffer buffer, int position, int limit,
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 74cad1aaa057..417a27d0f506 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -321,6 +321,7 @@ public class RippleDrawable extends LayerDrawable {
boolean pressed = false;
boolean focused = false;
boolean hovered = false;
+ boolean windowFocused = false;
for (int state : stateSet) {
if (state == R.attr.state_enabled) {
@@ -331,10 +332,12 @@ public class RippleDrawable extends LayerDrawable {
pressed = true;
} else if (state == R.attr.state_hovered) {
hovered = true;
+ } else if (state == R.attr.state_window_focused) {
+ windowFocused = true;
}
}
setRippleActive(enabled && pressed);
- setBackgroundActive(hovered, focused, pressed);
+ setBackgroundActive(hovered, focused, pressed, windowFocused);
return changed;
}
@@ -358,7 +361,8 @@ public class RippleDrawable extends LayerDrawable {
}
}
- private void setBackgroundActive(boolean hovered, boolean focused, boolean pressed) {
+ private void setBackgroundActive(boolean hovered, boolean focused, boolean pressed,
+ boolean windowFocused) {
if (mState.mRippleStyle == STYLE_SOLID) {
if (mBackground == null && (hovered || focused)) {
mBackground = new RippleBackground(this, mHotspotBounds, isBounded());
@@ -370,7 +374,7 @@ public class RippleDrawable extends LayerDrawable {
} else {
if (focused || hovered) {
if (!pressed) {
- enterPatternedBackgroundAnimation(focused, hovered);
+ enterPatternedBackgroundAnimation(focused, hovered, windowFocused);
}
} else {
exitPatternedBackgroundAnimation();
@@ -840,9 +844,14 @@ public class RippleDrawable extends LayerDrawable {
invalidateSelf(false);
}
- private void enterPatternedBackgroundAnimation(boolean focused, boolean hovered) {
+ private void enterPatternedBackgroundAnimation(boolean focused, boolean hovered,
+ boolean windowFocused) {
mBackgroundOpacity = 0;
- mTargetBackgroundOpacity = focused ? .6f : hovered ? .2f : 0f;
+ if (focused) {
+ mTargetBackgroundOpacity = windowFocused ? .6f : .2f;
+ } else {
+ mTargetBackgroundOpacity = hovered ? .2f : 0f;
+ }
if (mBackgroundAnimation != null) mBackgroundAnimation.cancel();
// after cancel
mRunBackgroundAnimation = true;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
index 921552b6cfbb..68ff806c6765 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
@@ -199,7 +199,7 @@ public final class CommonFoldingFeature {
throw new IllegalArgumentException(
"Display feature rectangle cannot have zero width and height simultaneously.");
}
- this.mRect = rect;
+ this.mRect = new Rect(rect);
}
/** Returns the type of the feature. */
@@ -217,7 +217,7 @@ public final class CommonFoldingFeature {
/** Returns the bounds of the feature. */
@NonNull
public Rect getRect() {
- return mRect;
+ return new Rect(mRect);
}
@Override
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
index fdcb7be597d5..cc2bb63ca8e1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
@@ -22,7 +22,6 @@ import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN;
import static androidx.window.common.CommonFoldingFeature.parseListFromString;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
@@ -30,22 +29,25 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.SparseIntArray;
+import androidx.window.util.AcceptOnceConsumer;
import androidx.window.util.BaseDataProducer;
-import androidx.window.util.DataProducer;
import com.android.internal.R;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.function.Consumer;
/**
- * An implementation of {@link androidx.window.util.DataProducer} that returns the device's posture
- * by mapping the state returned from {@link DeviceStateManager} to values provided in the resources
- * config at {@link R.array#config_device_state_postures}.
+ * An implementation of {@link androidx.window.util.BaseDataProducer} that returns
+ * the device's posture by mapping the state returned from {@link DeviceStateManager} to
+ * values provided in the resources' config at {@link R.array#config_device_state_postures}.
*/
-public final class DeviceStateManagerFoldingFeatureProducer extends
- BaseDataProducer<List<CommonFoldingFeature>> {
+public final class DeviceStateManagerFoldingFeatureProducer
+ extends BaseDataProducer<List<CommonFoldingFeature>> {
private static final String TAG =
DeviceStateManagerFoldingFeatureProducer.class.getSimpleName();
private static final boolean DEBUG = false;
@@ -54,15 +56,11 @@ public final class DeviceStateManagerFoldingFeatureProducer extends
private int mCurrentDeviceState = INVALID_DEVICE_STATE;
- private final DeviceStateCallback mDeviceStateCallback = (state) -> {
- mCurrentDeviceState = state;
- notifyDataChanged();
- };
@NonNull
- private final DataProducer<String> mRawFoldSupplier;
+ private final BaseDataProducer<String> mRawFoldSupplier;
public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context,
- @NonNull DataProducer<String> rawFoldSupplier) {
+ @NonNull BaseDataProducer<String> rawFoldSupplier) {
mRawFoldSupplier = rawFoldSupplier;
String[] deviceStatePosturePairs = context.getResources()
.getStringArray(R.array.config_device_state_postures);
@@ -70,7 +68,8 @@ public final class DeviceStateManagerFoldingFeatureProducer extends
String[] deviceStatePostureMapping = deviceStatePosturePair.split(":");
if (deviceStatePostureMapping.length != 2) {
if (DEBUG) {
- Log.e(TAG, "Malformed device state posture pair: " + deviceStatePosturePair);
+ Log.e(TAG, "Malformed device state posture pair: "
+ + deviceStatePosturePair);
}
continue;
}
@@ -82,7 +81,8 @@ public final class DeviceStateManagerFoldingFeatureProducer extends
posture = Integer.parseInt(deviceStatePostureMapping[1]);
} catch (NumberFormatException e) {
if (DEBUG) {
- Log.e(TAG, "Failed to parse device state or posture: " + deviceStatePosturePair,
+ Log.e(TAG, "Failed to parse device state or posture: "
+ + deviceStatePosturePair,
e);
}
continue;
@@ -92,32 +92,95 @@ public final class DeviceStateManagerFoldingFeatureProducer extends
}
if (mDeviceStateToPostureMap.size() > 0) {
- context.getSystemService(DeviceStateManager.class)
- .registerCallback(context.getMainExecutor(), mDeviceStateCallback);
+ DeviceStateCallback deviceStateCallback = (state) -> {
+ mCurrentDeviceState = state;
+ mRawFoldSupplier.getData(this::notifyFoldingFeatureChange);
+ };
+ Objects.requireNonNull(context.getSystemService(DeviceStateManager.class))
+ .registerCallback(context.getMainExecutor(), deviceStateCallback);
}
}
- @Override
- @Nullable
- public Optional<List<CommonFoldingFeature>> getData() {
- final int globalHingeState = globalHingeState();
- Optional<String> displayFeaturesString = mRawFoldSupplier.getData();
- if (displayFeaturesString.isEmpty() || TextUtils.isEmpty(displayFeaturesString.get())) {
- return Optional.empty();
+ /**
+ * Add a callback to mCallbacks if there is no device state. This callback will be run
+ * once a device state is set. Otherwise,run the callback immediately.
+ */
+ private void runCallbackWhenValidState(@NonNull Consumer<List<CommonFoldingFeature>> callback,
+ String displayFeaturesString) {
+ if (isCurrentStateValid()) {
+ callback.accept(calculateFoldingFeature(displayFeaturesString));
+ } else {
+ // This callback will be added to mCallbacks and removed once it runs once.
+ AcceptOnceConsumer<List<CommonFoldingFeature>> singleRunCallback =
+ new AcceptOnceConsumer<>(this, callback);
+ addDataChangedCallback(singleRunCallback);
}
- return Optional.of(parseListFromString(displayFeaturesString.get(), globalHingeState));
+ }
+
+ /**
+ * Checks to find {@link DeviceStateManagerFoldingFeatureProducer#mCurrentDeviceState} in the
+ * {@link DeviceStateManagerFoldingFeatureProducer#mDeviceStateToPostureMap} which was
+ * initialized in the constructor of {@link DeviceStateManagerFoldingFeatureProducer}.
+ * Returns a boolean value of whether the device state is valid.
+ */
+ private boolean isCurrentStateValid() {
+ // If the device state is not found in the map, indexOfKey returns a negative number.
+ return mDeviceStateToPostureMap.indexOfKey(mCurrentDeviceState) >= 0;
}
@Override
- protected void onListenersChanged(Set<Runnable> callbacks) {
+ protected void onListenersChanged(
+ @NonNull Set<Consumer<List<CommonFoldingFeature>>> callbacks) {
super.onListenersChanged(callbacks);
if (callbacks.isEmpty()) {
- mRawFoldSupplier.removeDataChangedCallback(this::notifyDataChanged);
+ mCurrentDeviceState = INVALID_DEVICE_STATE;
+ mRawFoldSupplier.removeDataChangedCallback(this::notifyFoldingFeatureChange);
+ } else {
+ mRawFoldSupplier.addDataChangedCallback(this::notifyFoldingFeatureChange);
+ }
+ }
+
+ @NonNull
+ @Override
+ public Optional<List<CommonFoldingFeature>> getCurrentData() {
+ Optional<String> displayFeaturesString = mRawFoldSupplier.getCurrentData();
+ if (!isCurrentStateValid()) {
+ return Optional.empty();
+ } else {
+ return displayFeaturesString.map(this::calculateFoldingFeature);
+ }
+ }
+
+ /**
+ * Adds the data to the storeFeaturesConsumer when the data is ready.
+ * @param storeFeaturesConsumer a consumer to collect the data when it is first available.
+ */
+ public void getData(Consumer<List<CommonFoldingFeature>> storeFeaturesConsumer) {
+ mRawFoldSupplier.getData((String displayFeaturesString) -> {
+ if (TextUtils.isEmpty(displayFeaturesString)) {
+ storeFeaturesConsumer.accept(new ArrayList<>());
+ } else {
+ runCallbackWhenValidState(storeFeaturesConsumer, displayFeaturesString);
+ }
+ });
+ }
+
+ private void notifyFoldingFeatureChange(String displayFeaturesString) {
+ if (!isCurrentStateValid()) {
+ return;
+ }
+ if (TextUtils.isEmpty(displayFeaturesString)) {
+ notifyDataChanged(new ArrayList<>());
} else {
- mRawFoldSupplier.addDataChangedCallback(this::notifyDataChanged);
+ notifyDataChanged(calculateFoldingFeature(displayFeaturesString));
}
}
+ private List<CommonFoldingFeature> calculateFoldingFeature(String displayFeaturesString) {
+ final int globalHingeState = globalHingeState();
+ return parseListFromString(displayFeaturesString, globalHingeState);
+ }
+
private int globalHingeState() {
return mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java
index 69ad1badce60..7906342d445d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java
@@ -32,6 +32,7 @@ import com.android.internal.R;
import java.util.Optional;
import java.util.Set;
+import java.util.function.Consumer;
/**
* Implementation of {@link androidx.window.util.DataProducer} that produces a
@@ -40,7 +41,7 @@ import java.util.Set;
* settings where the {@link String} property is saved with the key
* {@link RawFoldingFeatureProducer#DISPLAY_FEATURES}. If this value is null or empty then the
* value in {@link android.content.res.Resources} is used. If both are empty then
- * {@link RawFoldingFeatureProducer#getData()} returns an empty object.
+ * {@link RawFoldingFeatureProducer#getData} returns an empty object.
* {@link RawFoldingFeatureProducer} listens to changes in the setting so that it can override
* the system {@link CommonFoldingFeature} data.
*/
@@ -63,12 +64,13 @@ public final class RawFoldingFeatureProducer extends BaseDataProducer<String> {
@Override
@NonNull
- public Optional<String> getData() {
+ public void getData(Consumer<String> dataConsumer) {
String displayFeaturesString = getFeatureString();
if (displayFeaturesString == null) {
- return Optional.empty();
+ dataConsumer.accept("");
+ } else {
+ dataConsumer.accept(displayFeaturesString);
}
- return Optional.of(displayFeaturesString);
}
/**
@@ -84,7 +86,7 @@ public final class RawFoldingFeatureProducer extends BaseDataProducer<String> {
}
@Override
- protected void onListenersChanged(Set<Runnable> callbacks) {
+ protected void onListenersChanged(Set<Consumer<String>> callbacks) {
if (callbacks.isEmpty()) {
unregisterObserversIfNeeded();
} else {
@@ -92,6 +94,12 @@ public final class RawFoldingFeatureProducer extends BaseDataProducer<String> {
}
}
+ @NonNull
+ @Override
+ public Optional<String> getCurrentData() {
+ return Optional.of(getFeatureString());
+ }
+
/**
* Registers settings observers, if needed. When settings observers are registered for this
* producer callbacks for changes in data will be triggered.
@@ -125,8 +133,8 @@ public final class RawFoldingFeatureProducer extends BaseDataProducer<String> {
@Override
public void onChange(boolean selfChange, Uri uri) {
if (mDisplayFeaturesUri.equals(uri)) {
- notifyDataChanged();
+ notifyDataChanged(getFeatureString());
}
}
}
-}
+} \ No newline at end of file
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index c9a0d7d99cc6..da9fd0c2d96f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -16,6 +16,7 @@
package androidx.window.extensions.embedding;
+import static android.app.ActivityManager.START_SUCCESS;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -43,6 +44,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.SystemProperties;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
@@ -70,6 +72,8 @@ import java.util.function.Consumer;
public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback,
ActivityEmbeddingComponent {
static final String TAG = "SplitController";
+ static final boolean ENABLE_SHELL_TRANSITIONS =
+ SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
@VisibleForTesting
@GuardedBy("mLock")
@@ -94,6 +98,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
private final Handler mHandler;
private final Object mLock = new Object();
+ private final ActivityStartMonitor mActivityStartMonitor;
public SplitController() {
final MainThreadExecutor executor = new MainThreadExecutor();
@@ -105,7 +110,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
new LifecycleCallbacks());
// Intercept activity starts to route activities to new containers if necessary.
Instrumentation instrumentation = activityThread.getInstrumentation();
- instrumentation.addMonitor(new ActivityStartMonitor());
+ mActivityStartMonitor = new ActivityStartMonitor();
+ instrumentation.addMonitor(mActivityStartMonitor);
}
/** Updates the embedding rules applied to future activity launches. */
@@ -332,6 +338,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* bounds is large enough for at least one split rule.
*/
private void updateAnimationOverride(@NonNull TaskContainer taskContainer) {
+ if (ENABLE_SHELL_TRANSITIONS) {
+ // TODO(b/207070762): cleanup with legacy app transition
+ // Animation will be handled by WM Shell with Shell transition enabled.
+ return;
+ }
if (!taskContainer.isTaskBoundsInitialized()
|| !taskContainer.isWindowingModeInitialized()) {
// We don't know about the Task bounds/windowingMode yet.
@@ -381,6 +392,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* in a state that the caller shouldn't handle.
*/
@VisibleForTesting
+ @GuardedBy("mLock")
boolean resolveActivityToContainer(@NonNull Activity activity, boolean isOnReparent) {
if (isInPictureInPicture(activity) || activity.isFinishing()) {
// We don't embed activity when it is in PIP, or finishing. Return true since we don't
@@ -607,6 +619,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* Checks if there is a rule to split the two activities. If there is one, puts them into split
* and returns {@code true}. Otherwise, returns {@code false}.
*/
+ @GuardedBy("mLock")
private boolean putActivitiesIntoSplitIfNecessary(@NonNull Activity primaryActivity,
@NonNull Activity secondaryActivity) {
final SplitPairRule splitRule = getSplitRule(primaryActivity, secondaryActivity);
@@ -793,6 +806,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* Returns a container for the new activity intent to launch into as splitting with the primary
* activity.
*/
+ @GuardedBy("mLock")
@Nullable
private TaskFragmentContainer getSecondaryContainerForSplitIfAny(
@NonNull WindowContainerTransaction wct, @NonNull Activity primaryActivity,
@@ -865,6 +879,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* if needed.
* @param taskId parent Task of the new TaskFragment.
*/
+ @GuardedBy("mLock")
TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId) {
if (activityInTask == null) {
@@ -1373,6 +1388,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return ActivityThread.currentActivityThread().getActivity(activityToken);
}
+ @VisibleForTesting
+ ActivityStartMonitor getActivityStartMonitor() {
+ return mActivityStartMonitor;
+ }
+
/**
* Gets the token of the initial TaskFragment that embedded this activity. Do not rely on it
* after creation because the activity could be reparented.
@@ -1524,7 +1544,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* A monitor that intercepts all activity start requests originating in the client process and
* can amend them to target a specific task fragment to form a split.
*/
- private class ActivityStartMonitor extends Instrumentation.ActivityMonitor {
+ @VisibleForTesting
+ class ActivityStartMonitor extends Instrumentation.ActivityMonitor {
+ @VisibleForTesting
+ Intent mCurrentIntent;
@Override
public Instrumentation.ActivityResult onStartActivity(@NonNull Context who,
@@ -1552,11 +1575,29 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// the dedicated container.
options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
launchedInTaskFragment.getTaskFragmentToken());
+ mCurrentIntent = intent;
}
}
return super.onStartActivity(who, intent, options);
}
+
+ @Override
+ public void onStartActivityResult(int result, @NonNull Bundle bOptions) {
+ super.onStartActivityResult(result, bOptions);
+ if (mCurrentIntent != null && result != START_SUCCESS) {
+ // Clear the pending appeared intent if the activity was not started successfully.
+ final IBinder token = bOptions.getBinder(
+ ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN);
+ if (token != null) {
+ final TaskFragmentContainer container = getContainer(token);
+ if (container != null) {
+ container.clearPendingAppearedIntentIfNeeded(mCurrentIntent);
+ }
+ }
+ }
+ mCurrentIntent = null;
+ }
}
/**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
index 586ac1f212a1..5cc496a225c2 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
@@ -180,9 +180,18 @@ class TaskFragmentAnimationSpec {
Animation loadOpenAnimation(@NonNull RemoteAnimationTarget target,
@NonNull Rect wholeAnimationBounds) {
final boolean isEnter = target.mode != MODE_CLOSING;
- final Animation animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
- ? com.android.internal.R.anim.task_fragment_open_enter
- : com.android.internal.R.anim.task_fragment_open_exit);
+ final Animation animation;
+ // Background color on TaskDisplayArea has already been set earlier in
+ // WindowContainer#getAnimationAdapter.
+ if (target.showBackdrop) {
+ animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+ ? com.android.internal.R.anim.task_fragment_clear_top_open_enter
+ : com.android.internal.R.anim.task_fragment_clear_top_open_exit);
+ } else {
+ animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+ ? com.android.internal.R.anim.task_fragment_open_enter
+ : com.android.internal.R.anim.task_fragment_open_exit);
+ }
animation.initialize(target.localBounds.width(), target.localBounds.height(),
wholeAnimationBounds.width(), wholeAnimationBounds.height());
animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
@@ -192,9 +201,16 @@ class TaskFragmentAnimationSpec {
Animation loadCloseAnimation(@NonNull RemoteAnimationTarget target,
@NonNull Rect wholeAnimationBounds) {
final boolean isEnter = target.mode != MODE_CLOSING;
- final Animation animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
- ? com.android.internal.R.anim.task_fragment_close_enter
- : com.android.internal.R.anim.task_fragment_close_exit);
+ final Animation animation;
+ if (target.showBackdrop) {
+ animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+ ? com.android.internal.R.anim.task_fragment_clear_top_close_enter
+ : com.android.internal.R.anim.task_fragment_clear_top_close_exit);
+ } else {
+ animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+ ? com.android.internal.R.anim.task_fragment_close_enter
+ : com.android.internal.R.anim.task_fragment_close_exit);
+ }
animation.initialize(target.localBounds.width(), target.localBounds.height(),
wholeAnimationBounds.width(), wholeAnimationBounds.height());
animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index abf32a26efa2..a188e2bf4985 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -198,6 +198,22 @@ class TaskFragmentContainer {
return mPendingAppearedIntent;
}
+ void setPendingAppearedIntent(@Nullable Intent intent) {
+ mPendingAppearedIntent = intent;
+ }
+
+ /**
+ * Clears the pending appeared Intent if it is the same as given Intent. Otherwise, the
+ * pending appeared Intent is cleared when TaskFragmentInfo is set and is not empty (has
+ * running activities).
+ */
+ void clearPendingAppearedIntentIfNeeded(@NonNull Intent intent) {
+ if (mPendingAppearedIntent == null || mPendingAppearedIntent != intent) {
+ return;
+ }
+ mPendingAppearedIntent = null;
+ }
+
boolean hasActivity(@NonNull IBinder token) {
if (mInfo != null && mInfo.getActivities().contains(token)) {
return true;
@@ -230,13 +246,18 @@ class TaskFragmentContainer {
void setInfo(@NonNull TaskFragmentInfo info) {
if (!mIsFinished && mInfo == null && info.isEmpty()) {
- // onTaskFragmentAppeared with empty info. We will remove the TaskFragment if it is
- // still empty after timeout.
+ // onTaskFragmentAppeared with empty info. We will remove the TaskFragment if no
+ // pending appeared intent/activities. Otherwise, wait and removing the TaskFragment if
+ // it is still empty after timeout.
mAppearEmptyTimeout = () -> {
mAppearEmptyTimeout = null;
mController.onTaskFragmentAppearEmptyTimeout(this);
};
- mController.getHandler().postDelayed(mAppearEmptyTimeout, APPEAR_EMPTY_TIMEOUT_MS);
+ if (mPendingAppearedIntent != null || !mPendingAppearedActivities.isEmpty()) {
+ mController.getHandler().postDelayed(mAppearEmptyTimeout, APPEAR_EMPTY_TIMEOUT_MS);
+ } else {
+ mAppearEmptyTimeout.run();
+ }
} else if (mAppearEmptyTimeout != null && !info.isEmpty()) {
mController.getHandler().removeCallbacks(mAppearEmptyTimeout);
mAppearEmptyTimeout = null;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index c1d1c8e8d4e0..6bfb16a3c22d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -25,8 +25,7 @@ import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
import android.annotation.Nullable;
import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityManager.AppTask;
+import android.app.ActivityClient;
import android.app.Application;
import android.app.WindowConfiguration;
import android.content.Context;
@@ -34,7 +33,6 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.util.ArrayMap;
-import android.util.Log;
import androidx.annotation.NonNull;
import androidx.window.common.CommonFoldingFeature;
@@ -46,7 +44,6 @@ import androidx.window.util.DataProducer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
@@ -66,7 +63,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer;
- public WindowLayoutComponentImpl(Context context) {
+ public WindowLayoutComponentImpl(@NonNull Context context) {
((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
RawFoldingFeatureProducer foldingFeatureProducer = new RawFoldingFeatureProducer(context);
@@ -83,8 +80,12 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
*/
public void addWindowLayoutInfoListener(@NonNull Activity activity,
@NonNull Consumer<WindowLayoutInfo> consumer) {
+ mFoldingFeatureProducer.getData((features) -> {
+ // Get the WindowLayoutInfo from the activity and pass the value to the layoutConsumer.
+ WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(activity, features);
+ consumer.accept(newWindowLayout);
+ });
mWindowLayoutChangeListeners.put(activity, consumer);
- onDisplayFeaturesChanged();
}
/**
@@ -92,18 +93,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
*
* @param consumer no longer interested in receiving updates to {@link WindowLayoutInfo}
*/
- public void removeWindowLayoutInfoListener(
- @NonNull Consumer<WindowLayoutInfo> consumer) {
+ public void removeWindowLayoutInfoListener(@NonNull Consumer<WindowLayoutInfo> consumer) {
mWindowLayoutChangeListeners.values().remove(consumer);
- onDisplayFeaturesChanged();
- }
-
- void updateWindowLayout(@NonNull Activity activity,
- @NonNull WindowLayoutInfo newLayout) {
- Consumer<WindowLayoutInfo> consumer = mWindowLayoutChangeListeners.get(activity);
- if (consumer != null) {
- consumer.accept(newLayout);
- }
}
@NonNull
@@ -111,7 +102,6 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
return mWindowLayoutChangeListeners.keySet();
}
- @NonNull
private boolean isListeningForLayoutChanges(IBinder token) {
for (Activity activity: getActivitiesListeningForLayoutChanges()) {
if (token.equals(activity.getWindow().getAttributes().token)) {
@@ -128,12 +118,12 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
/**
* A convenience method to translate from the common feature state to the extensions feature
* state. More specifically, translates from {@link CommonFoldingFeature.State} to
- * {@link FoldingFeature.STATE_FLAT} or {@link FoldingFeature.STATE_HALF_OPENED}. If it is not
+ * {@link FoldingFeature#STATE_FLAT} or {@link FoldingFeature#STATE_HALF_OPENED}. If it is not
* possible to translate, then we will return a {@code null} value.
*
* @param state if it matches a value in {@link CommonFoldingFeature.State}, {@code null}
- * otherwise. @return a {@link FoldingFeature.STATE_FLAT} or
- * {@link FoldingFeature.STATE_HALF_OPENED} if the given state matches a value in
+ * otherwise. @return a {@link FoldingFeature#STATE_FLAT} or
+ * {@link FoldingFeature#STATE_HALF_OPENED} if the given state matches a value in
* {@link CommonFoldingFeature.State} and {@code null} otherwise.
*/
@Nullable
@@ -147,17 +137,24 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
}
- private void onDisplayFeaturesChanged() {
+ private void onDisplayFeaturesChanged(List<CommonFoldingFeature> storedFeatures) {
for (Activity activity : getActivitiesListeningForLayoutChanges()) {
- WindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
- updateWindowLayout(activity, newLayout);
+ // Get the WindowLayoutInfo from the activity and pass the value to the layoutConsumer.
+ Consumer<WindowLayoutInfo> layoutConsumer = mWindowLayoutChangeListeners.get(activity);
+ WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(activity, storedFeatures);
+ layoutConsumer.accept(newWindowLayout);
}
}
- @NonNull
- private WindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
- List<DisplayFeature> displayFeatures = getDisplayFeatures(activity);
- return new WindowLayoutInfo(displayFeatures);
+ /**
+ * Translates the {@link DisplayFeature} into a {@link WindowLayoutInfo} when a
+ * valid state is found.
+ * @param activity a proxy for the {@link android.view.Window} that contains the
+ */
+ private WindowLayoutInfo getWindowLayoutInfo(
+ @NonNull Activity activity, List<CommonFoldingFeature> storedFeatures) {
+ List<DisplayFeature> displayFeatureList = getDisplayFeatures(activity, storedFeatures);
+ return new WindowLayoutInfo(displayFeatureList);
}
/**
@@ -175,67 +172,54 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
*
* @param activity a proxy for the {@link android.view.Window} that contains the
* {@link DisplayFeature}.
- * @return a {@link List} of valid {@link DisplayFeature} that
* are within the {@link android.view.Window} of the {@link Activity}
*/
- private List<DisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
+ private List<DisplayFeature> getDisplayFeatures(
+ @NonNull Activity activity, List<CommonFoldingFeature> storedFeatures) {
List<DisplayFeature> features = new ArrayList<>();
- int displayId = activity.getDisplay().getDisplayId();
- if (displayId != DEFAULT_DISPLAY) {
- Log.w(TAG, "This sample doesn't support display features on secondary displays");
+ if (!shouldReportDisplayFeatures(activity)) {
return features;
}
- if (isTaskInMultiWindowMode(activity)) {
- // It is recommended not to report any display features in multi-window mode, since it
- // won't be possible to synchronize the display feature positions with window movement.
- return features;
- }
-
- Optional<List<CommonFoldingFeature>> storedFeatures = mFoldingFeatureProducer.getData();
- if (storedFeatures.isPresent()) {
- for (CommonFoldingFeature baseFeature : storedFeatures.get()) {
- Integer state = convertToExtensionState(baseFeature.getState());
- if (state == null) {
- continue;
- }
- Rect featureRect = baseFeature.getRect();
- rotateRectToDisplayRotation(displayId, featureRect);
- transformToWindowSpaceRect(activity, featureRect);
+ int displayId = activity.getDisplay().getDisplayId();
+ for (CommonFoldingFeature baseFeature : storedFeatures) {
+ Integer state = convertToExtensionState(baseFeature.getState());
+ if (state == null) {
+ continue;
+ }
+ Rect featureRect = baseFeature.getRect();
+ rotateRectToDisplayRotation(displayId, featureRect);
+ transformToWindowSpaceRect(activity, featureRect);
- if (!isRectZero(featureRect)) {
- // TODO(b/228641877) Remove guarding if when fixed.
- features.add(new FoldingFeature(featureRect, baseFeature.getType(), state));
- }
+ if (!isRectZero(featureRect)) {
+ // TODO(b/228641877): Remove guarding when fixed.
+ features.add(new FoldingFeature(featureRect, baseFeature.getType(), state));
}
}
return features;
}
/**
- * Checks whether the task associated with the activity is in multi-window. If task info is not
- * available it defaults to {@code true}.
+ * Checks whether display features should be reported for the activity.
+ * TODO(b/238948678): Support reporting display features in all windowing modes.
*/
- private boolean isTaskInMultiWindowMode(@NonNull Activity activity) {
- final ActivityManager am = activity.getSystemService(ActivityManager.class);
- if (am == null) {
- return true;
- }
-
- final List<AppTask> appTasks = am.getAppTasks();
- final int taskId = activity.getTaskId();
- AppTask task = null;
- for (AppTask t : appTasks) {
- if (t.getTaskInfo().taskId == taskId) {
- task = t;
- break;
- }
+ private boolean shouldReportDisplayFeatures(@NonNull Activity activity) {
+ int displayId = activity.getDisplay().getDisplayId();
+ if (displayId != DEFAULT_DISPLAY) {
+ // Display features are not supported on secondary displays.
+ return false;
}
- if (task == null) {
- // The task might be removed on the server already.
- return true;
+ final int taskWindowingMode = ActivityClient.getInstance().getTaskWindowingMode(
+ activity.getActivityToken());
+ if (taskWindowingMode == -1) {
+ // If we cannot determine the task windowing mode for any reason, it is likely that we
+ // won't be able to determine its position correctly as well. DisplayFeatures' bounds
+ // in this case can't be computed correctly, so we should skip.
+ return false;
}
- return WindowConfiguration.inMultiWindowMode(task.getTaskInfo().getWindowingMode());
+ // It is recommended not to report any display features in multi-window mode, since it
+ // won't be possible to synchronize the display feature positions with window movement.
+ return !WindowConfiguration.inMultiWindowMode(taskWindowingMode);
}
/**
@@ -262,7 +246,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private void onDisplayFeaturesChangedIfListening(Activity activity) {
IBinder token = activity.getWindow().getAttributes().token;
if (token == null || isListeningForLayoutChanges(token)) {
- onDisplayFeaturesChanged();
+ mFoldingFeatureProducer.getData(
+ WindowLayoutComponentImpl.this::onDisplayFeaturesChanged);
}
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
index 970f0a2af632..5bfb0ebdcaa8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -28,41 +28,42 @@ import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
-import android.util.Log;
import androidx.annotation.NonNull;
import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.RawFoldingFeatureProducer;
-import androidx.window.util.DataProducer;
+import androidx.window.util.BaseDataProducer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Optional;
/**
* Reference implementation of androidx.window.sidecar OEM interface for use with
* WindowManager Jetpack.
*/
class SampleSidecarImpl extends StubSidecar {
- private static final String TAG = "SampleSidecar";
-
- private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer;
-
+ private List<CommonFoldingFeature> mStoredFeatures = new ArrayList<>();
SampleSidecarImpl(Context context) {
((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
- DataProducer<String> settingsFeatureProducer = new RawFoldingFeatureProducer(context);
- mFoldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context,
- settingsFeatureProducer);
+ BaseDataProducer<String> settingsFeatureProducer = new RawFoldingFeatureProducer(context);
+ BaseDataProducer<List<CommonFoldingFeature>> foldingFeatureProducer =
+ new DeviceStateManagerFoldingFeatureProducer(context,
+ settingsFeatureProducer);
- mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
+ foldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
}
- private void onDisplayFeaturesChanged() {
+ private void setStoredFeatures(List<CommonFoldingFeature> storedFeatures) {
+ mStoredFeatures = storedFeatures;
+ }
+
+ private void onDisplayFeaturesChanged(List<CommonFoldingFeature> storedFeatures) {
+ setStoredFeatures(storedFeatures);
updateDeviceState(getDeviceState());
for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
@@ -79,16 +80,16 @@ class SampleSidecarImpl extends StubSidecar {
}
private int deviceStateFromFeature() {
- List<CommonFoldingFeature> storedFeatures = mFoldingFeatureProducer.getData()
- .orElse(Collections.emptyList());
- for (int i = 0; i < storedFeatures.size(); i++) {
- CommonFoldingFeature feature = storedFeatures.get(i);
+ for (int i = 0; i < mStoredFeatures.size(); i++) {
+ CommonFoldingFeature feature = mStoredFeatures.get(i);
final int state = feature.getState();
switch (state) {
case CommonFoldingFeature.COMMON_STATE_FLAT:
return SidecarDeviceState.POSTURE_OPENED;
case CommonFoldingFeature.COMMON_STATE_HALF_OPENED:
return SidecarDeviceState.POSTURE_HALF_OPENED;
+ case CommonFoldingFeature.COMMON_STATE_UNKNOWN:
+ return SidecarDeviceState.POSTURE_UNKNOWN;
}
}
return SidecarDeviceState.POSTURE_UNKNOWN;
@@ -109,7 +110,6 @@ class SampleSidecarImpl extends StubSidecar {
private List<SidecarDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
int displayId = activity.getDisplay().getDisplayId();
if (displayId != DEFAULT_DISPLAY) {
- Log.w(TAG, "This sample doesn't support display features on secondary displays");
return Collections.emptyList();
}
@@ -119,18 +119,15 @@ class SampleSidecarImpl extends StubSidecar {
return Collections.emptyList();
}
- Optional<List<CommonFoldingFeature>> storedFeatures = mFoldingFeatureProducer.getData();
List<SidecarDisplayFeature> features = new ArrayList<>();
- if (storedFeatures.isPresent()) {
- for (CommonFoldingFeature baseFeature : storedFeatures.get()) {
- SidecarDisplayFeature feature = new SidecarDisplayFeature();
- Rect featureRect = baseFeature.getRect();
- rotateRectToDisplayRotation(displayId, featureRect);
- transformToWindowSpaceRect(activity, featureRect);
- feature.setRect(featureRect);
- feature.setType(baseFeature.getType());
- features.add(feature);
- }
+ for (CommonFoldingFeature baseFeature : mStoredFeatures) {
+ SidecarDisplayFeature feature = new SidecarDisplayFeature();
+ Rect featureRect = baseFeature.getRect();
+ rotateRectToDisplayRotation(displayId, featureRect);
+ transformToWindowSpaceRect(activity, featureRect);
+ feature.setRect(featureRect);
+ feature.setType(baseFeature.getType());
+ features.add(feature);
}
return Collections.unmodifiableList(features);
}
@@ -138,7 +135,7 @@ class SampleSidecarImpl extends StubSidecar {
@Override
protected void onListenersChanged() {
if (hasListeners()) {
- onDisplayFeaturesChanged();
+ onDisplayFeaturesChanged(mStoredFeatures);
}
}
@@ -158,7 +155,7 @@ class SampleSidecarImpl extends StubSidecar {
private void onDisplayFeaturesChangedForActivity(@NonNull Activity activity) {
IBinder token = activity.getWindow().getAttributes().token;
if (token == null || mWindowLayoutChangeListenerTokens.contains(token)) {
- onDisplayFeaturesChanged();
+ onDisplayFeaturesChanged(mStoredFeatures);
}
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java
new file mode 100644
index 000000000000..7624b693ac43
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.util;
+
+import android.annotation.NonNull;
+
+import java.util.function.Consumer;
+
+/**
+ * A base class that works with {@link BaseDataProducer} to add/remove a consumer that should
+ * only be used once when {@link BaseDataProducer#notifyDataChanged} is called.
+ * @param <T> The type of data this producer returns through {@link DataProducer#getData}.
+ */
+public class AcceptOnceConsumer<T> implements Consumer<T> {
+ private final Consumer<T> mCallback;
+ private final DataProducer<T> mProducer;
+
+ public AcceptOnceConsumer(@NonNull DataProducer<T> producer, @NonNull Consumer<T> callback) {
+ mProducer = producer;
+ mCallback = callback;
+ }
+
+ @Override
+ public void accept(@NonNull T t) {
+ mCallback.accept(t);
+ mProducer.removeDataChangedCallback(this);
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
index 930db3b701b7..0da44ac36a6e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
@@ -19,38 +19,48 @@ package androidx.window.util;
import androidx.annotation.NonNull;
import java.util.LinkedHashSet;
+import java.util.Optional;
import java.util.Set;
+import java.util.function.Consumer;
/**
* Base class that provides the implementation for the callback mechanism of the
* {@link DataProducer} API.
*
- * @param <T> The type of data this producer returns through {@link #getData()}.
+ * @param <T> The type of data this producer returns through {@link DataProducer#getData}.
*/
public abstract class BaseDataProducer<T> implements DataProducer<T> {
- private final Set<Runnable> mCallbacks = new LinkedHashSet<>();
+ private final Set<Consumer<T>> mCallbacks = new LinkedHashSet<>();
@Override
- public final void addDataChangedCallback(@NonNull Runnable callback) {
+ public final void addDataChangedCallback(@NonNull Consumer<T> callback) {
mCallbacks.add(callback);
+ Optional<T> currentData = getCurrentData();
+ currentData.ifPresent(callback);
onListenersChanged(mCallbacks);
}
@Override
- public final void removeDataChangedCallback(@NonNull Runnable callback) {
+ public final void removeDataChangedCallback(@NonNull Consumer<T> callback) {
mCallbacks.remove(callback);
onListenersChanged(mCallbacks);
}
- protected void onListenersChanged(Set<Runnable> callbacks) {}
+ protected void onListenersChanged(Set<Consumer<T>> callbacks) {}
/**
- * Called to notify all registered callbacks that the data provided by {@link #getData()} has
- * changed.
+ * @return the current data if available and {@code Optional.empty()} otherwise.
*/
- protected void notifyDataChanged() {
- for (Runnable callback : mCallbacks) {
- callback.run();
+ @NonNull
+ public abstract Optional<T> getCurrentData();
+
+ /**
+ * Called to notify all registered consumers that the data provided
+ * by {@link DataProducer#getData} has changed.
+ */
+ protected void notifyDataChanged(T value) {
+ for (Consumer<T> callback : mCallbacks) {
+ callback.accept(value);
}
}
-}
+} \ No newline at end of file
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java
index d4d1a23b756b..ec301dc34aaa 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java
@@ -18,26 +18,27 @@ package androidx.window.util;
import android.annotation.NonNull;
-import java.util.Optional;
+import java.util.function.Consumer;
/**
- * Produces data through {@link #getData()} and provides a mechanism for receiving a callback when
- * the data managed by the produces has changed.
+ * Produces data through {@link DataProducer#getData} and provides a mechanism for receiving
+ * a callback when the data managed by the produces has changed.
*
- * @param <T> The type of data this producer returns through {@link #getData()}.
+ * @param <T> The type of data this producer returns through {@link DataProducer#getData}.
*/
public interface DataProducer<T> {
/**
- * Returns the data currently stored in the provider, or {@link Optional#empty()} if the
- * provider has no data.
+ * Emits the first available data at that point in time.
+ * @param dataConsumer a {@link Consumer} that will receive one value.
*/
- Optional<T> getData();
+ void getData(@NonNull Consumer<T> dataConsumer);
/**
- * Adds a callback to be notified when the data returned from {@link #getData()} has changed.
+ * Adds a callback to be notified when the data returned
+ * from {@link DataProducer#getData} has changed.
*/
- void addDataChangedCallback(@NonNull Runnable callback);
+ void addDataChangedCallback(@NonNull Consumer<T> callback);
- /** Removes a callback previously added with {@link #addDataChangedCallback(Runnable)}. */
- void removeDataChangedCallback(@NonNull Runnable callback);
+ /** Removes a callback previously added with {@link #addDataChangedCallback(Consumer)}. */
+ void removeDataChangedCallback(@NonNull Consumer<T> callback);
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index ad496a906a33..4bc503369d0e 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -16,6 +16,7 @@
package androidx.window.extensions.embedding;
+import static android.app.ActivityManager.START_CANCELED;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -293,6 +294,26 @@ public class SplitControllerTest {
}
@Test
+ public void testOnStartActivityResultError() {
+ final Intent intent = new Intent();
+ final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
+ intent, taskContainer, mSplitController);
+ final SplitController.ActivityStartMonitor monitor =
+ mSplitController.getActivityStartMonitor();
+
+ container.setPendingAppearedIntent(intent);
+ final Bundle bundle = new Bundle();
+ bundle.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
+ container.getTaskFragmentToken());
+ monitor.mCurrentIntent = intent;
+ doReturn(container).when(mSplitController).getContainer(any());
+
+ monitor.onStartActivityResult(START_CANCELED, bundle);
+ assertNull(container.getPendingAppearedIntent());
+ }
+
+ @Test
public void testOnActivityCreated() {
mSplitController.onActivityCreated(mActivity);
@@ -865,6 +886,8 @@ public class SplitControllerTest {
assertSplitPair(activityBelow, mActivity, true /* matchParentBounds */);
}
+ // Suppress GuardedBy warning on unit tests
+ @SuppressWarnings("GuardedBy")
@Test
public void testResolveActivityToContainer_minDimensions_shouldExpandSplitContainer() {
final Activity primaryActivity = createMockActivity();
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
index 28c2773e25cb..44c7e6c611de 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
@@ -209,21 +209,21 @@ public class TaskFragmentContainerTest {
assertNull(container.mAppearEmptyTimeout);
- // Not set if it is not appeared empty.
- final TaskFragmentInfo info = mock(TaskFragmentInfo.class);
- doReturn(new ArrayList<>()).when(info).getActivities();
- doReturn(false).when(info).isEmpty();
- container.setInfo(info);
-
- assertNull(container.mAppearEmptyTimeout);
-
// Set timeout if the first info set is empty.
+ final TaskFragmentInfo info = mock(TaskFragmentInfo.class);
container.mInfo = null;
doReturn(true).when(info).isEmpty();
container.setInfo(info);
assertNotNull(container.mAppearEmptyTimeout);
+ // Not set if it is not appeared empty.
+ doReturn(new ArrayList<>()).when(info).getActivities();
+ doReturn(false).when(info).isEmpty();
+ container.setInfo(info);
+
+ assertNull(container.mAppearEmptyTimeout);
+
// Remove timeout after the container becomes non-empty.
doReturn(false).when(info).isEmpty();
container.setInfo(info);
@@ -232,6 +232,7 @@ public class TaskFragmentContainerTest {
// Running the timeout will call into SplitController.onTaskFragmentAppearEmptyTimeout.
container.mInfo = null;
+ container.setPendingAppearedIntent(mIntent);
doReturn(true).when(info).isEmpty();
container.setInfo(info);
container.mAppearEmptyTimeout.run();
diff --git a/libs/WindowManager/Shell/res/color/decor_button_dark_color.xml b/libs/WindowManager/Shell/res/color/decor_button_dark_color.xml
new file mode 100644
index 000000000000..bf325bd84c00
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/decor_button_dark_color.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item app:state_task_focused="true" android:color="#FF000000" />
+ <item android:color="#33000000" />
+</selector>
diff --git a/libs/WindowManager/Shell/res/color/decor_button_light_color.xml b/libs/WindowManager/Shell/res/color/decor_button_light_color.xml
new file mode 100644
index 000000000000..2e48bca7786a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/decor_button_light_color.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item app:state_task_focused="true" android:color="#FFFFFFFF" />
+ <item android:color="#33FFFFFF" />
+</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/decor_caption_title_color.xml b/libs/WindowManager/Shell/res/color/decor_caption_title_color.xml
new file mode 100644
index 000000000000..1ecc13e4da38
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/decor_caption_title_color.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <!-- Fading the to 85% blackness -->
+ <item app:state_task_focused="true" android:color="#D8D8D8" />
+ <!-- Fading the to 95% blackness -->
+ <item android:color="#F2F2F2" />
+</selector>
diff --git a/libs/WindowManager/Shell/res/color/taskbar_background.xml b/libs/WindowManager/Shell/res/color/taskbar_background.xml
index 329e5b9b31a0..b3d260299106 100644
--- a/libs/WindowManager/Shell/res/color/taskbar_background.xml
+++ b/libs/WindowManager/Shell/res/color/taskbar_background.xml
@@ -14,6 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
+<!-- Should be the same as in packages/apps/Launcher3/res/color-v31/taskbar_background.xml -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@android:color/system_neutral1_500" android:lStar="35" />
+ <item android:color="@android:color/system_neutral1_500" android:lStar="15" />
</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml b/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml
new file mode 100644
index 000000000000..8207365a737d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape android:shape="rectangle"
+ android:tintMode="multiply"
+ android:tint="@color/decor_caption_title_color"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="?android:attr/colorPrimary" />
+</shape>
diff --git a/libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml
new file mode 100644
index 000000000000..f2f1a1d55dee
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32.0dp"
+ android:height="32.0dp"
+ android:viewportWidth="32.0"
+ android:viewportHeight="32.0"
+ android:tint="@color/decor_button_dark_color"
+ >
+ <group android:scaleX="0.5"
+ android:scaleY="0.5"
+ android:translateX="8.0"
+ android:translateY="8.0" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/>
+ </group>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml
new file mode 100644
index 000000000000..ab4e29ac97e5
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32.0dp"
+ android:height="32.0dp"
+ android:viewportWidth="32.0"
+ android:viewportHeight="32.0"
+ android:tint="@color/decor_button_dark_color">
+ <group android:scaleX="0.5"
+ android:scaleY="0.5"
+ android:translateX="8.0"
+ android:translateY="8.0" >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
+ </group>
+</vector>
+
+
diff --git a/libs/WindowManager/Shell/res/layout/bubble_overflow_container.xml b/libs/WindowManager/Shell/res/layout/bubble_overflow_container.xml
index cb516cdbe49b..df5985c605d1 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_overflow_container.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_overflow_container.xml
@@ -30,7 +30,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
- android:gravity="center"/>
+ android:gravity="center"
+ android:clipChildren="false"/>
<LinearLayout
android:id="@+id/bubble_overflow_empty_state"
diff --git a/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml b/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml
new file mode 100644
index 000000000000..a112f1933dd1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/caption"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="end"
+ android:background="@drawable/decor_caption_title">
+ <Button
+ android:id="@+id/maximize_window"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_margin="5dp"
+ android:padding="4dp"
+ android:layout_gravity="center_vertical|end"
+ android:contentDescription="@string/maximize_button_text"
+ android:background="@drawable/decor_maximize_button_dark"
+ android:duplicateParentState="true"/>
+ <Button
+ android:id="@+id/close_window"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_margin="5dp"
+ android:padding="4dp"
+ android:layout_gravity="center_vertical|end"
+ android:contentDescription="@string/close_button_text"
+ android:background="@drawable/decor_close_button_dark"
+ android:duplicateParentState="true"/>
+</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
+
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 2476f65c7e5b..6959a591338c 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Tik om hierdie program te herbegin en maak volskerm oop."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tik om hierdie program te herbegin vir ’n beter aansig."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerakwessies?\nTik om aan te pas"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nie opgelos nie?\nTik om terug te stel"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen kamerakwessies nie? Tik om toe te maak."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Draai jou toestel om dit volskerm te maak"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dubbeltik langs ’n program om dit te herposisioneer"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Het dit"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Vou uit vir meer inligting."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimeer"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Maak toe"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index f0c391cd6b99..fe22b2ce3278 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"አረፋ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ያቀናብሩ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"አረፋ ተሰናብቷል።"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"ይህን መተግበሪያ ዳግም ለማስነሳት መታ ያድርጉ እና ወደ ሙሉ ማያ ገጽ ይሂዱ።"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"ለተሻለ ዕይታ ይህን መተግበሪያ ዳግም ለማስነሳት መታ ያድርጉ።"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"የካሜራ ችግሮች አሉ?\nዳግም ለማበጀት መታ ያድርጉ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"አልተስተካከለም?\nለማህደር መታ ያድርጉ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ምንም የካሜራ ችግሮች የሉም? ለማሰናበት መታ ያድርጉ።"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ወደ የሙሉ ገጽ ዕይታ ለመሄድ መሣሪያዎን ያሽከርክሩት"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ቦታውን ለመቀየር ከመተግበሪያው ቀጥሎ ላይ ሁለቴ መታ ያድርጉ"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ገባኝ"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ለተጨማሪ መረጃ ይዘርጉ።"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"አስፋ"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"ዝጋ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index aa4b3b704110..2be6f39f9475 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"فقاعة"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"إدارة"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"تم إغلاق الفقاعة."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"انقر لإعادة تشغيل هذا التطبيق والانتقال إلى وضع ملء الشاشة."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"انقر لإعادة تشغيل هذا التطبيق للحصول على عرض أفضل."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"هل هناك مشاكل في الكاميرا؟\nانقر لإعادة الضبط."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ألم يتم حل المشكلة؟\nانقر للعودة"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"أليس هناك مشاكل في الكاميرا؟ انقر للإغلاق."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"قم بتدوير الشاشة للانتقال إلى وضع ملء الشاشة."</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"انقر مرتين بجانب التطبيق لتغيير موضعه."</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"حسنًا"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"التوسيع للحصول على مزيد من المعلومات"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"تكبير"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"إغلاق"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 985d3b9b96fd..098ee84de018 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"পৰিচালনা কৰক"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল অগ্ৰাহ্য কৰা হৈছে"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"এপ্‌টো ৰিষ্টাৰ্ট কৰিবলৈ আৰু পূৰ্ণ স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ টিপক।"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"উন্নত ভিউৰ বাবে এপ্‌টো ৰিষ্টাৰ্ট কৰিবলৈ টিপক।"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"কেমেৰাৰ কোনো সমস্যা হৈছে নেকি?\nপুনৰ খাপ খোৱাবলৈ টিপক"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এইটো সমাধান কৰা নাই নেকি?\nপূৰ্বাৱস্থালৈ নিবলৈ টিপক"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"কেমেৰাৰ কোনো সমস্যা নাই নেকি? অগ্ৰাহ্য কৰিবলৈ টিপক।"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"পূৰ্ণ স্ক্ৰীনলৈ যাবলৈ আপোনাৰ ডিভাইচটো ঘূৰাওক"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"এপ্‌টোৰ স্থান সলনি কৰিবলৈ ইয়াৰ কাষত দুবাৰ টিপক"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"বুজি পালোঁ"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"অধিক তথ্যৰ বাবে বিস্তাৰ কৰক।"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"সৰ্বাধিক মাত্ৰালৈ বঢ়াওক"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"বন্ধ কৰক"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 8cd9b7a635ab..2f49ae6fdafc 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Qabarcıq"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"İdarə edin"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Bu tətbiqi sıfırlayaraq tam ekrana keçmək üçün toxunun."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Toxunaraq bu tətbiqi yenidən başladın ki, daha görüntü əldə edəsiniz."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera problemi var?\nBərpa etmək üçün toxunun"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Düzəltməmisiniz?\nGeri qaytarmaq üçün toxunun"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera problemi yoxdur? Qapatmaq üçün toxunun."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Tam ekrana keçmək üçün cihazınızı fırladın"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tətbiqin yerini dəyişmək üçün yanına iki dəfə toxunun"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Anladım"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Ətraflı məlumat üçün genişləndirin."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Böyüdün"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Bağlayın"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 49524c608543..0656fe185013 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste restartovali aplikaciju i prešli u režim celog ekrana."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da biste restartovali ovu aplikaciju radi boljeg prikaza."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Imate problema sa kamerom?\nDodirnite da biste ponovo uklopili"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije rešen?\nDodirnite da biste vratili"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema sa kamerom? Dodirnite da biste odbacili."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotirajte uređaj za prikaz preko celog ekrana"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvaput dodirnite pored aplikacije da biste promenili njenu poziciju"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Važi"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite za još informacija."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Uvećajte"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Zatvorite"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 1767e0d66241..702f0abc07ae 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Усплывальнае апавяшчэнне"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Усплывальнае апавяшчэнне адхілена."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Націсніце, каб перазапусціць гэту праграму і перайсці ў поўнаэкранны рэжым."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Націсніце, каб перазапусціць гэту праграму для лепшага прагляду."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Праблемы з камерай?\nНацісніце, каб пераабсталяваць"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не ўдалося выправіць?\nНацісніце, каб аднавіць"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ніякіх праблем з камерай? Націсніце, каб адхіліць."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Каб перайсці ў поўнаэкранны рэжым, павярніце прыладу"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Двойчы націсніце побач з праграмай, каб перамясціць яе"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Зразумела"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Разгарнуць для дадатковай інфармацыі"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Разгарнуць"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Закрыць"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index c22fb86a4d4d..0de16d37cd69 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управление"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отхвърлено."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Докоснете, за да рестартирате това приложение в режим на цял екран."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Докоснете, за да рестартирате това приложение с цел по-добър изглед."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблеми с камерата?\nДокоснете за ремонтиране"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблемът не се отстрани?\nДокоснете за връщане в предишното състояние"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нямате проблеми с камерата? Докоснете, за да отхвърлите."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Завъртете екрана си, за да преминете в режим на цял екран"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Докоснете два пъти дадено приложение, за да промените позицията му"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Разбрах"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Разгъване за още информация."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Увеличаване"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Затваряне"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index c0944e0584e6..9a48d186b231 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ম্যানেজ করুন"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল করা হয়েছে।"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন ও \'ফুল-স্ক্রিন\' মোড ব্যবহার করুন।"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"আরও ভাল ভিউয়ের জন্য এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন।"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ক্যামেরা সংক্রান্ত সমস্যা?\nরিফিট করতে ট্যাপ করুন"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এখনও সমাধান হয়নি?\nরিভার্ট করার জন্য ট্যাপ করুন"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ক্যামেরা সংক্রান্ত সমস্যা নেই? বাতিল করতে ট্যাপ করুন।"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"\'ফুল স্ক্রিন\' মোডে যেতে ডিভাইস ঘোরান"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"কোনও অ্যাপের পাশে ডবল ট্যাপ করে সেটির জায়গা পরিবর্তন করুন"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"বুঝেছি"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"আরও তথ্যের জন্য বড় করুন।"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"বড় করুন"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"বন্ধ করুন"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index ae01c641cc43..31e906de7f51 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da ponovo pokrenete ovu aplikaciju i aktivirate prikaz preko cijelog ekrana."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da ponovo pokrenete ovu aplikaciju radi boljeg prikaza."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s kamerom?\nDodirnite da ponovo namjestite"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nije popravljeno?\nDodirnite da vratite"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nema problema s kamerom? Dodirnite da odbacite."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zarotirajte uređaj da aktivirate prikaz preko cijelog ekrana"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvaput dodirnite pored aplikacije da promijenite njen položaj"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Razumijem"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite za više informacija."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimiziranje"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Zatvaranje"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 8a522b3e6397..ca0a4211c435 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bombolla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Toca per reiniciar aquesta aplicació i passar a pantalla completa."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Toca per reiniciar aquesta aplicació i obtenir una millor visualització."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tens problemes amb la càmera?\nToca per resoldre\'ls"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"El problema no s\'ha resolt?\nToca per desfer els canvis"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No tens cap problema amb la càmera? Toca per ignorar."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gira el dispositiu per passar a pantalla completa"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Fes doble toc al costat d\'una aplicació per canviar-ne la posició"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entesos"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Desplega per obtenir més informació."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximitza"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Tanca"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index d0cf80aef38c..e8772fe269b6 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Klepnutím aplikaci restartujete a přejdete na režim celé obrazovky"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Klepnutím tuto aplikaci kvůli lepšímu zobrazení restartujete."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s fotoaparátem?\nKlepnutím vyřešíte"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepomohlo to?\nKlepnutím se vrátíte"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Žádné problémy s fotoaparátem? Klepnutím zavřete."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Otočením zařízení přejděte do režimu celé obrazovky"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvojitým klepnutím vedle aplikace změňte její umístění"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Rozbalením zobrazíte další informace."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximalizovat"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Zavřít"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index bb81c10c6e1b..2b55d4d688ac 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Tryk for at genstarte denne app, og gå til fuld skærm."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tryk for at genstarte denne app, så visningen forbedres."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du problemer med dit kamera?\nTryk for at gendanne det oprindelige format"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Løste det ikke problemet?\nTryk for at fortryde"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen problemer med dit kamera? Tryk for at afvise."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Drej din enhed for at gå til fuld skærm"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tryk to gange ud for en app for at ændre dens placering"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Udvid for at få flere oplysninger."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimér"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Luk"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index c5d945a982ef..03eee024f3be 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Tippe, um die App im Vollbildmodus neu zu starten."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tippe, um diese App neu zu starten und die Ansicht zu verbessern."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Probleme mit der Kamera?\nZum Anpassen tippen."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Das Problem ist nicht behoben?\nZum Rückgängigmachen tippen."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Keine Probleme mit der Kamera? Zum Schließen tippen."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gerät drehen, um zum Vollbildmodus zu wechseln"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Neben einer App doppeltippen, um die Position zu ändern"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ok"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Für weitere Informationen maximieren."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximieren"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Schließen"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 70f55058925c..49bfdf18b1e6 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Συννεφάκι"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Διαχείριση"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το συννεφάκι παραβλέφθηκε."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Πατήστε για επανεκκίνηση αυτής της εφαρμογής και ενεργοποίηση πλήρους οθόνης."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Πατήστε για να επανεκκινήσετε αυτή την εφαρμογή για καλύτερη προβολή."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Προβλήματα με την κάμερα;\nΠατήστε για επιδιόρθωση."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Δεν διορθώθηκε;\nΠατήστε για επαναφορά."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Δεν αντιμετωπίζετε προβλήματα με την κάμερα; Πατήστε για παράβλεψη."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Περιστρέψτε τη συσκευή σας για μετάβαση σε πλήρη οθόνη."</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Πατήστε δύο φορές δίπλα σε μια εφαρμογή για να αλλάξετε τη θέση της."</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Το κατάλαβα"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Ανάπτυξη για περισσότερες πληροφορίες."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Μεγιστοποίηση"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Κλείσιμο"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 0b5aefa5c72e..081a01a49249 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximise"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 0b5aefa5c72e..081a01a49249 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximise"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 0b5aefa5c72e..081a01a49249 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximise"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 0b5aefa5c72e..081a01a49249 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotate your device to go full screen"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Double-tap next to an app to reposition it"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Got it"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expand for more information."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximise"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Close"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index 5c3d0f65374a..afc14b821d62 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‎‎Bubble‎‏‎‎‏‎"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎Manage‎‏‎‎‏‎"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎Bubble dismissed.‎‏‎‎‏‎"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎Tap to restart this app and go full screen.‎‏‎‎‏‎"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‏‏‎‎Tap to restart this app for a better view.‎‏‎‎‏‎"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‏‎‏‏‏‎‎‏‎‏‏‎‎‎‏‏‎‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‏‏‏‏‎‏‏‏‏‏‎Camera issues?‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Tap to refit‎‏‎‎‏‎"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‎‏‎Didn’t fix it?‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Tap to revert‎‏‎‎‏‎"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‎‏‏‏‏‎‎‏‎‎‏‏‏‎No camera issues? Tap to dismiss.‎‏‎‎‏‎"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎‏‏‎Rotate your device to go full screen‎‏‎‎‏‎"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎Double-tap next to an app to reposition it‎‏‎‎‏‎"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‎‎‏‎Got it‎‏‎‎‏‎"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‏‎‎‎‏‏‎‏‎‏‎‎Expand for more information.‎‏‎‎‏‎"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‎‏‎‎‎‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎Maximize‎‏‎‎‏‎"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‎‎‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎Close‎‏‎‎‏‎"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index e523ae53b0cc..b376b7881333 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Cuadro"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Presiona para reiniciar esta app y acceder al modo de pantalla completa."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Presiona para reiniciar esta app y tener una mejor vista."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Tienes problemas con la cámara?\nPresiona para reajustarla"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se resolvió?\nPresiona para revertir los cambios"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No tienes problemas con la cámara? Presionar para descartar."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rota el dispositivo para ver la pantalla completa"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Presiona dos veces junto a una app para cambiar su posición"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expande para obtener más información."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Cerrar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 39990dc8cb0c..79c1f90a4b8d 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbuja"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Toca para reiniciar esta aplicación e ir a la pantalla completa."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Toca para reiniciar esta aplicación y obtener una mejor vista."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Problemas con la cámara?\nToca para reajustar"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se ha solucionado?\nToca para revertir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No hay problemas con la cámara? Toca para cerrar."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gira el dispositivo para ir al modo de pantalla completa"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toca dos veces junto a una aplicación para cambiar su posición"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Mostrar más información"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Cerrar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index a5f82a6452c4..a7fead6af9aa 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Mull"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Puudutage rakenduse taaskäivitamiseks ja täisekraanrežiimi aktiveerimiseks."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Puudutage, et see rakendus parema vaate jaoks taaskäivitada."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kas teil on kaameraprobleeme?\nPuudutage ümberpaigutamiseks."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Kas probleemi ei lahendatud?\nPuudutage ennistamiseks."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kas kaameraprobleeme pole? Puudutage loobumiseks."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pöörake seadet, et aktiveerida täisekraanirežiim"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Topeltpuudutage rakenduse kõrval, et selle asendit muuta"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Selge"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Laiendage lisateabe saamiseks."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimeeri"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Sule"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 67b9a433dc03..e7530c9690a7 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbuila"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Saka ezazu aplikazioa berrabiarazteko, eta ezarri pantaila osoko modua."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Hobeto ikusteko, sakatu hau aplikazioa berrabiarazteko."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Arazoak dauzkazu kamerarekin?\nBerriro doitzeko, sakatu hau."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ez al da konpondu?\nLeheneratzeko, sakatu hau."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ez daukazu arazorik kamerarekin? Baztertzeko, sakatu hau."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pantaila osoko modua erabiltzeko, biratu gailua"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Aplikazioaren posizioa aldatzeko, sakatu birritan haren ondoko edozein toki"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ados"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Informazio gehiago lortzeko, zabaldu hau."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizatu"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Itxi"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 761fb9ddeb2f..66a657e36c13 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"برای بازراه‌اندازی این برنامه و تغییر به حالت تمام‌صفحه، ضربه بزنید."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"برای داشتن نمایی بهتر، ضربه بزنید تا این برنامه بازراه‌اندازی شود."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"دوربین مشکل دارد؟\nبرای تنظیم مجدد اندازه ضربه بزنید"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"مشکل برطرف نشد؟\nبرای برگرداندن ضربه بزنید"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن ضربه بزنید."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"برای رفتن به حالت تمام صفحه، دستگاهتان را بچرخانید"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"در کنار برنامه دوضربه بزنید تا جابه‌جا شود"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"متوجه‌ام"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"برای اطلاعات بیشتر، گسترده کنید."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"بزرگ کردن"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"بستن"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index c809b4879e71..eaf369ad0486 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Kupla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Ylläpidä"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Napauta, niin sovellus käynnistyy uudelleen ja siirtyy koko näytön tilaan."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Napauta, niin sovellus käynnistyy uudelleen paremmin näytölle sopivana."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Onko kameran kanssa ongelmia?\nKorjaa napauttamalla"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Eikö ongelma ratkennut?\nKumoa napauttamalla"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ei ongelmia kameran kanssa? Hylkää napauttamalla."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Käännä laitetta, niin se siirtyy koko näytön tilaan"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Kaksoisnapauta sovellusta, jos haluat siirtää sitä"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Katso lisätietoja laajentamalla."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Suurenna"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Sulje"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 62b2bb65a603..8f614c56db14 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Touchez pour redémarrer cette application et passer en plein écran."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Touchez pour redémarrer cette application afin d\'obtenir un meilleur affichage."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo?\nTouchez pour réajuster"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu?\nTouchez pour rétablir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo? Touchez pour ignorer."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Faites pivoter votre appareil pour passer en plein écran"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Touchez deux fois à côté d\'une application pour la repositionner"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Développer pour en savoir plus."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Agrandir"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Fermer"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 07475055f03e..ec3e1b33a4fa 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermée."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Appuyez pour redémarrer cette application et activer le mode plein écran."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Appuyez pour redémarrer cette appli et avoir une meilleure vue."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo ?\nAppuyez pour réajuster"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu ?\nAppuyez pour rétablir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo ? Appuyez pour ignorer."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Faites pivoter l\'appareil pour passer en plein écran"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Appuyez deux fois à côté d\'une appli pour la repositionner"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Développez pour obtenir plus d\'informations"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Agrandir"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Fermer"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index b8e039602243..651353d89319 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Toca o botón para reiniciar esta aplicación e abrila en pantalla completa."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Toca o botón para reiniciar esta aplicación e gozar dunha mellor visualización."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tes problemas coa cámara?\nToca para reaxustala"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Non se solucionaron os problemas?\nToca para reverter o seu tratamento"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Non hai problemas coa cámara? Tocar para ignorar."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Xira o dispositivo para ver o contido en pantalla completa"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toca dúas veces a carón dunha aplicación para cambiala de posición"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Despregar para obter máis información."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Pechar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index deda2d755e20..3543be0bda07 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"બબલ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"મેનેજ કરો"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ છોડી દેવાયો."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"આ ઍપ ફરીથી ચાલુ કરવા માટે ટૅપ કરીને પૂર્ણ સ્ક્રીન કરો."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"વધુ સારા વ્યૂ માટે, આ ઍપને ફરી શરૂ કરવા ટૅપ કરો."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"કૅમેરામાં સમસ્યાઓ છે?\nફરીથી ફિટ કરવા માટે ટૅપ કરો"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"સુધારો નથી થયો?\nપહેલાંના પર પાછું ફેરવવા માટે ટૅપ કરો"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"કૅમેરામાં કોઈ સમસ્યા નથી? છોડી દેવા માટે ટૅપ કરો."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"પૂર્ણ સ્ક્રીન મોડ લાગુ કરવા માટે, તમારા ડિવાઇસને ફેરવો"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"કોઈ ઍપની જગ્યા બદલવા માટે, તેની બાજુમાં બે વાર ટૅપ કરો"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"સમજાઈ ગયું"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"વધુ માહિતી માટે મોટું કરો."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"મોટું કરો"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"બંધ કરો"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index a5fcb97d1418..87ac5d64ff85 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"मैनेज करें"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"इस ऐप्लिकेशन को रीस्टार्ट करने और फ़ुल स्क्रीन पर देखने के लिए टैप करें."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"टैप करके ऐप्लिकेशन को रीस्टार्ट करें और बेहतर व्यू पाएं."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्या कैमरे से जुड़ी कोई समस्या है?\nफिर से फ़िट करने के लिए टैप करें"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"क्या समस्या ठीक नहीं हुई?\nपहले जैसा करने के लिए टैप करें"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्या कैमरे से जुड़ी कोई समस्या नहीं है? खारिज करने के लिए टैप करें."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"फ़ुल स्क्रीन मोड में जाने के लिए, डिवाइस को घुमाएं"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"किसी ऐप्लिकेशन की जगह बदलने के लिए, उसके बगल में दो बार टैप करें"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ठीक है"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ज़्यादा जानकारी के लिए बड़ा करें."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"बड़ा करें"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"बंद करें"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 5ecc5585a6e9..cb4f424cf317 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić odbačen."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste ponovo pokrenuli tu aplikaciju i prikazali je na cijelom zaslonu."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da biste ponovo pokrenuli tu aplikaciju kako biste bolje vidjeli."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s fotoaparatom?\nDodirnite za popravak"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije riješen?\nDodirnite za vraćanje"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema s fotoaparatom? Dodirnite za odbacivanje."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zakrenite uređaj radi prikaza na cijelom zaslonu"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvaput dodirnite pored aplikacije da biste joj promijenili položaj"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Shvaćam"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite da biste saznali više."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimiziraj"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Zatvori"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 2295250e2853..635f4dad8f89 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Buborék"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kezelés"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Buborék elvetve."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Koppintson az alkalmazás újraindításához és a teljes képernyős mód elindításához."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"A jobb nézet érdekében koppintson az alkalmazás újraindításához."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerával kapcsolatos problémába ütközött?\nKoppintson a megoldáshoz."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nem sikerült a hiba kijavítása?\nKoppintson a visszaállításhoz."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nincsenek problémái kamerával? Koppintson az elvetéshez."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"A teljes képernyős mód elindításához forgassa el az eszközt"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Koppintson duplán az alkalmazás mellett az áthelyezéséhez"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Értem"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Kibontással további információkhoz juthat."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Teljes méret"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Bezárás"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 208936539094..da382c113797 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Պղպջակ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Կառավարել"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ամպիկը փակվեց։"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Հպեք՝ հավելվածը վերագործարկելու և լիաէկրան ռեժիմին անցնելու համար։"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Հպեք՝ հավելվածը վերագործարկելու և ավելի հարմար տեսք ընտրելու համար։"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Տեսախցիկի հետ կապված խնդիրնե՞ր կան։\nՀպեք՝ վերակարգավորելու համար։"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Չհաջողվե՞ց շտկել։\nՀպեք՝ փոփոխությունները չեղարկելու համար։"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Տեսախցիկի հետ կապված խնդիրներ չկա՞ն։ Փակելու համար հպեք։"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Պտտեք սարքը՝ լիաէկրան ռեժիմին անցնելու համար"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Կրկնակի հպեք հավելվածի կողքին՝ այն տեղափոխելու համար"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Եղավ"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Ծավալեք՝ ավելին իմանալու համար։"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Ծավալել"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Փակել"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 1b46b2fe2570..cd795390128b 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Ketuk untuk memulai ulang aplikasi ini dan membuka layar penuh."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Ketuk untuk memulai ulang aplikasi ini agar mendapatkan tampilan yang lebih baik."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Masalah kamera?\nKetuk untuk memperbaiki"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tidak dapat diperbaiki?\nKetuk untuk mengembalikan"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tidak ada masalah kamera? Ketuk untuk menutup."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Putar perangkat untuk tampilan layar penuh"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Ketuk dua kali di samping aplikasi untuk mengubah posisinya"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Oke"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Luaskan untuk melihat informasi selengkapnya."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimalkan"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Tutup"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index a201c95137f3..37141b74005c 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Blaðra"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blöðru lokað."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Ýttu til að endurræsa forritið og sýna það á öllum skjánum."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Ýta til að endurræsa forritið og fá betri sýn."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Myndavélavesen?\nÝttu til að breyta stærð"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ennþá vesen?\nÝttu til að afturkalla"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ekkert myndavélavesen? Ýttu til að hunsa."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Snúðu tækinu til að nota allan skjáinn"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Ýttu tvisvar við hlið forritsins til að færa það"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ég skil"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Stækka til að sjá frekari upplýsingar."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Stækka"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Loka"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 7157ed088d30..b63e76466b6e 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -72,7 +72,8 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Fumetto"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Tocca per riavviare l\'app e passare alla modalità a schermo intero."</string>
+ <!-- no translation found for restart_button_description (6712141648865547958) -->
+ <skip />
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi con la fotocamera?\nTocca per risolverli"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Il problema non si è risolto?\nTocca per ripristinare"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nessun problema con la fotocamera? Tocca per ignorare."</string>
@@ -81,4 +82,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Ruota il dispositivo per passare alla modalità a schermo intero"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tocca due volte accanto a un\'app per riposizionarla"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Espandi per avere ulteriori informazioni."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Ingrandisci"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Chiudi"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 52a6b0676222..0e500eafd805 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"בועה"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ניהול"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"הבועה נסגרה."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"צריך להקיש כדי להפעיל מחדש את האפליקציה הזו ולעבור למסך מלא."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"כדי לראות טוב יותר יש להקיש ולהפעיל את האפליקציה הזו מחדש."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"בעיות במצלמה?\nאפשר להקיש כדי לבצע התאמה מחדש"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"הבעיה לא נפתרה?\nאפשר להקיש כדי לחזור לגרסה הקודמת"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"אין בעיות במצלמה? אפשר להקיש כדי לסגור."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"מסובבים את המכשיר כדי לעבור לתצוגה במסך מלא"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"מקישים הקשה כפולה ליד אפליקציה כדי למקם אותה מחדש"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"הבנתי"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"מרחיבים כדי לקבל מידע נוסף."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"הגדלה"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"סגירה"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 5a25c24ba034..34ed9c72da0e 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"バブル"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ふきだしが非表示になっています。"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"タップしてこのアプリを再起動すると、全画面表示になります。"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"タップしてこのアプリを再起動すると、表示が適切になります。"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"カメラに関する問題の場合は、\nタップすると修正できます"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"修正されなかった場合は、\nタップすると元に戻ります"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"カメラに関する問題でない場合は、タップすると閉じます。"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"全画面表示にするにはデバイスを回転させてください"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"位置を変えるにはアプリの横をダブルタップしてください"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"開くと詳細が表示されます。"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"閉じる"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index bff86fa6ffe2..14b26c1932cc 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ბუშტი"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"მართვა"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ბუშტი დაიხურა."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"შეეხეთ ამ აპის გადასატვირთად და გადადით სრულ ეკრანზე."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"შეეხეთ, რომ გადატვირთოთ ეს აპი უკეთესი ხედისთვის."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"კამერად პრობლემები აქვს?\nშეეხეთ გამოსასწორებლად"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"არ გამოსწორდა?\nშეეხეთ წინა ვერსიის დასაბრუნებლად"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"კამერას პრობლემები არ აქვს? შეეხეთ უარყოფისთვის."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"მოატრიალეთ თქვენი მოწყობილობა სრული ეკრანის გასაშლელად"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ორმაგად შეეხეთ აპის გვერდითა სივრცეს, რათა ის სხვაგან გადაიტანოთ"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"გასაგებია"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"დამატებითი ინფორმაციისთვის გააფართოეთ."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"მაქსიმალურად გაშლა"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"დახურვა"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index f57f3f581c85..c42efdc4cf25 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Көпіршік"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Басқару"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқыма хабар жабылды."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Бұл қолданбаны қайта қосып, толық экранға өту үшін түртіңіз."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Ыңғайлы көріністі реттеу үшін қолданбаны түртіп, өшіріп қосыңыз."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада қателер шықты ма?\nЖөндеу үшін түртіңіз."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Жөнделмеді ме?\nҚайтару үшін түртіңіз."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада қателер шықпады ма? Жабу үшін түртіңіз."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Толық экранға ауысу үшін құрылғыңызды бұрыңыз."</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Қолданбаның орнын ауыстыру үшін жанынан екі рет түртіңіз."</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Түсінікті"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Толығырақ ақпарат алу үшін терезені жайыңыз."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Жаю"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Жабу"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 5c04f881fe0e..302b25e5bad2 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ពពុះ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"គ្រប់គ្រង"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"បានច្រានចោល​សារលេចឡើង។"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"ចុចដើម្បី​ចាប់ផ្ដើម​កម្មវិធី​នេះឡើងវិញ រួចចូលប្រើ​ពេញអេក្រង់។"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"ចុចដើម្បី​ចាប់ផ្ដើម​កម្មវិធី​នេះឡើងវិញសម្រាប់ទិដ្ឋភាពកាន់តែប្រសើរ។"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"មានបញ្ហា​ពាក់ព័ន្ធនឹង​កាមេរ៉ាឬ?\nចុចដើម្បី​ដោះស្រាយ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"មិនបាន​ដោះស្រាយ​បញ្ហានេះទេឬ?\nចុចដើម្បី​ត្រឡប់"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"មិនមាន​បញ្ហាពាក់ព័ន្ធនឹង​កាមេរ៉ាទេឬ? ចុចដើម្បី​ច្រានចោល។"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"បង្វិលឧបករណ៍របស់អ្នក ដើម្បីចូលប្រើអេក្រង់ពេញ"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ចុចពីរដងនៅជាប់កម្មវិធីណាមួយ ដើម្បីប្ដូរទីតាំងកម្មវិធីនោះ"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"យល់ហើយ"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ពង្រីកដើម្បីទទួលបានព័ត៌មានបន្ថែម។"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"ពង្រីក"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"បិទ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index e91383caa009..2b3aa0791336 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ಬಬಲ್"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ನಿರ್ವಹಿಸಿ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ಬಬಲ್ ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಮತ್ತು ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ನೋಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"ಉತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿವೆಯೇ?\nಮರುಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ಅದನ್ನು ಸರಿಪಡಿಸಲಿಲ್ಲವೇ?\nಹಿಂತಿರುಗಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿಲ್ಲವೇ? ವಜಾಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ಗೆ ಹೋಗಲು ನಿಮ್ಮ ಸಾಧನವನ್ನು ತಿರುಗಿಸಿ"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ಆ್ಯಪ್ ಒಂದರ ಸ್ಥಾನವನ್ನು ಬದಲಾಯಿಸಲು ಅದರ ಪಕ್ಕದಲ್ಲಿ ಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ಸರಿ"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ಇನ್ನಷ್ಟು ಮಾಹಿತಿಗಾಗಿ ವಿಸ್ತೃತಗೊಳಿಸಿ."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"ಹಿಗ್ಗಿಸಿ"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"ಮುಚ್ಚಿರಿ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 104ba3f22c96..5505955db71a 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"버블"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"관리"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"대화창을 닫았습니다."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"탭하여 이 앱을 다시 시작하고 전체 화면으로 이동합니다."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"보기를 개선하려면 탭하여 앱을 다시 시작합니다."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"카메라 문제가 있나요?\n해결하려면 탭하세요."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"해결되지 않았나요?\n되돌리려면 탭하세요."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"카메라에 문제가 없나요? 닫으려면 탭하세요."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"전체 화면 모드로 전환하려면 기기를 회전하세요."</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"앱 위치를 조정하려면 앱 옆을 두 번 탭하세요."</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"확인"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"추가 정보는 펼쳐서 확인하세요."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"최대화"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"닫기"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 8203622a33fc..d45a9848abc4 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Көбүк"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Башкаруу"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Калкып чыкма билдирме жабылды."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Бул колдонмону өчүрүп күйгүзүп, толук экранга өтүү үчүн таптап коюңуз."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Жакшыраак көрүү үчүн бул колдонмону өчүрүп күйгүзүңүз. Ал үчүн таптап коюңуз."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада маселелер келип чыктыбы?\nОңдоо үчүн таптаңыз"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Оңдолгон жокпу?\nАртка кайтаруу үчүн таптаңыз"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада маселе жокпу? Этибарга албоо үчүн таптаңыз."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Толук экран режимине өтүү үчүн түзмөктү буруңуз"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Колдонмонун ракурсун өзгөртүү үчүн анын тушуна эки жолу басыңыз"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Түшүндүм"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Толук маалымат алуу үчүн жайып көрүңүз."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Чоңойтуу"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Жабуу"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 24396786f9d8..0eeee906070b 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ຟອງ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ຈັດການ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ປິດ Bubble ໄສ້ແລ້ວ."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ ແລະ ໃຊ້ແບບເຕັມຈໍ."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ເພື່ອມຸມມອງທີ່ດີຂຶ້ນ."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ?\nແຕະເພື່ອປັບໃໝ່"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ບໍ່ໄດ້ແກ້ໄຂມັນບໍ?\nແຕະເພື່ອແປງກັບຄືນ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ບໍ່ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ? ແຕະເພື່ອ​ປິດ​ໄວ້."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ໝຸນອຸປະກອນຂອງທ່ານເພື່ອໃຊ້ແບບເຕັມຈໍ"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ແຕະສອງເທື່ອໃສ່ຖັດຈາກແອັບໃດໜຶ່ງເພື່ອຈັດຕຳແໜ່ງຂອງມັນຄືນໃໝ່"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ເຂົ້າໃຈແລ້ວ"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ຂະຫຍາຍເພື່ອເບິ່ງຂໍ້ມູນເພີ່ມເຕີມ."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"ຂະຫຍາຍໃຫຍ່ສຸດ"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"ປິດ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index e2ae643ad308..fc118e217481 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Debesėlis"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Debesėlio atsisakyta."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Palieskite, kad paleistumėte iš naujo šią programą ir įjungtumėte viso ekrano režimą."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Palieskite, kad iš naujo paleistumėte šią programą ir matytumėte aiškiau."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Iškilo problemų dėl kameros?\nPalieskite, kad pritaikytumėte iš naujo"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepavyko pataisyti?\nPalieskite, kad grąžintumėte"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nėra jokių problemų dėl kameros? Palieskite, kad atsisakytumėte."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pasukite įrenginį, kad įjungtumėte viso ekrano režimą"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dukart palieskite šalia programos, kad pakeistumėte jos poziciją"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Supratau"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Išskleiskite, jei reikia daugiau informacijos."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Padidinti"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Uždaryti"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index a77160bc262a..cd2af07beb7e 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbulis"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pārvaldīt"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidīts."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Pieskarieties, lai restartētu šo lietotni un pārietu pilnekrāna režīmā."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Pieskarieties, lai restartētu šo lietotni un uzlabotu attēlojumu."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Vai ir problēmas ar kameru?\nPieskarieties, lai tās novērstu."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Vai problēma netika novērsta?\nPieskarieties, lai atjaunotu."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Vai nav problēmu ar kameru? Pieskarieties, lai nerādītu."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Pagrieziet ierīci, lai aktivizētu pilnekrāna režīmu"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Veiciet dubultskārienu blakus lietotnei, lai manītu tās pozīciju"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Labi"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Izvērsiet, lai iegūtu plašāku informāciju."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimizēt"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Aizvērt"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index bac0c9eee4c2..c0dff00e6a7c 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управувајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отфрлено."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Допрете за да ја рестартирате апликацијава и да ја отворите на цел екран."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Допрете за да ја рестартирате апликацијава за подобар приказ."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми со камерата?\nДопрете за да се совпадне повторно"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не се поправи?\nДопрете за враќање"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нема проблеми со камерата? Допрете за отфрлање."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Ротирајте го уредот за да отворите на цел екран"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Допрете двапати до некоја апликација за да ја преместите"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Сфатив"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Проширете за повеќе информации."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Зголеми"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Затвори"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index de0f837fcd3f..52ea1c7c3150 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ബബിൾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബിൾ ഡിസ്മിസ് ചെയ്തു."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"ഈ ആപ്പ് റീസ്‌റ്റാർട്ട് ചെയ്‌ത് പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറാൻ ടാപ്പ് ചെയ്യുക."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"മികച്ച കാഴ്‌ചയ്‌ക്കായി ഈ ആപ്പ് റീസ്‌റ്റാർട്ട് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ക്യാമറ പ്രശ്നങ്ങളുണ്ടോ?\nശരിയാക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"അത് പരിഹരിച്ചില്ലേ?\nപുനഃസ്ഥാപിക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ക്യാമറാ പ്രശ്നങ്ങളൊന്നുമില്ലേ? നിരസിക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറാൻ ഈ ഉപകരണം റൊട്ടേറ്റ് ചെയ്യുക"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ഒരു ആപ്പിന്റെ സ്ഥാനം മാറ്റാൻ, അതിന് തൊട്ടടുത്ത് ഡബിൾ ടാപ്പ് ചെയ്യുക"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"മനസ്സിലായി"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"കൂടുതൽ വിവരങ്ങൾക്ക് വികസിപ്പിക്കുക."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"വലുതാക്കുക"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"അടയ്ക്കുക"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 1205306e0833..fd4c4aab1832 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Бөмбөлөг"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Удирдах"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Бөмбөлгийг үл хэрэгссэн."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Энэ аппыг дахин эхлүүлж, бүтэн дэлгэцэд орохын тулд товшино уу."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Харагдах байдлыг сайжруулахын тулд энэ аппыг товшиж, дахин эхлүүлнэ үү."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерын асуудал гарсан уу?\nДахин тааруулахын тулд товшино уу"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Үүнийг засаагүй юу?\nБуцаахын тулд товшино уу"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерын асуудал байхгүй юу? Хаахын тулд товшино уу."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Төхөөрөмжөө бүтэн дэлгэцээр үзэхийн тулд эргүүлнэ"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Аппыг дахин байрлуулахын тулд хажууд нь хоёр товшино"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ойлголоо"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Нэмэлт мэдээлэл авах бол дэлгэнэ үү."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Томруулах"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Хаах"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index c91d06fdf3d5..b9a165eb6b11 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापित करा"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल डिसमिस केला."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"हे अ‍ॅप रीस्टार्ट करण्यासाठी आणि फुल स्क्रीन करण्यासाठी टॅप करा."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"अधिक चांगल्या व्ह्यूसाठी हे अ‍ॅप रीस्टार्ट करण्याकरिता टॅप करा."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"कॅमेराशी संबंधित काही समस्या आहेत का?\nपुन्हा फिट करण्यासाठी टॅप करा"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"निराकरण झाले नाही?\nरिव्हर्ट करण्यासाठी कृपया टॅप करा"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"कॅमेराशी संबंधित कोणत्याही समस्या नाहीत का? डिसमिस करण्‍यासाठी टॅप करा."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"फुल स्क्रीन करण्यासाठी, तुमचे डिव्हाइस फिरवा"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ॲपची स्थिती पुन्हा बदलण्यासाठी, त्याच्या शेजारी दोनदा टॅप करा"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"समजले"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"अधिक माहितीसाठी विस्तार करा."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"मोठे करा"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"बंद करा"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 652a9919d163..3d81c9a551fa 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Gelembung"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Ketik untuk memulakan semula apl ini dan menggunakan skrin penuh."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Ketik untuk memulakan semula apl ini untuk mendapatkan paparan yang lebih baik."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Isu kamera?\nKetik untuk memuatkan semula"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Isu tidak dibetulkan?\nKetik untuk kembali"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tiada isu kamera? Ketik untuk mengetepikan."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Putar peranti anda untuk beralih ke skrin penuh"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Ketik dua kali bersebelahan apl untuk menempatkan semula apl"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Kembangkan untuk mendapatkan maklumat lanjut."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimumkan"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Tutup"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 15d182c6451e..50adfe98d8f9 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ပူဖောင်းဖောက်သံ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"စီမံရန်"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ပူဖောင်းကွက် ဖယ်လိုက်သည်။"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"ဤအက်ပ်ကို ပြန်စပြီး ဖန်သားပြင်အပြည့်လုပ်ရန် တို့ပါ။"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"ပိုကောင်းသောမြင်ကွင်းအတွက် ဤအက်ပ်ပြန်စရန် တို့နိုင်သည်။"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ကင်မရာပြဿနာလား။\nပြင်ဆင်ရန် တို့ပါ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ကောင်းမသွားဘူးလား။\nပြန်ပြောင်းရန် တို့ပါ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ကင်မရာပြဿနာ မရှိဘူးလား။ ပယ်ရန် တို့ပါ။"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ဖန်သားပြင်အပြည့်လုပ်ရန် သင့်စက်ကို လှည့်နိုင်သည်"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"အက်ပ်နေရာပြန်ချရန် ၎င်းဘေးတွင် နှစ်ချက်တို့နိုင်သည်"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ရပြီ"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"နောက်ထပ်အချက်အလက်များအတွက် ချဲ့နိုင်သည်။"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"ချဲ့ရန်"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"ပိတ်ရန်"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 2f2fea6eb833..74e066ec11fd 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Trykk for å starte denne appen på nytt og vise den i fullskjerm."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Trykk for å starte denne appen på nytt for bedre visning."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for å tilpasse"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen kameraproblemer? Trykk for å lukke."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Roter enheten for å starte fullskjerm"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dobbelttrykk ved siden av en app for å flytte den"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Greit"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Vis for å få mer informasjon."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimer"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Lukk"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 8dfec88cc29d..b257f9e6d0d3 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापन गर्नुहोस्"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल हटाइयो।"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"यो एप रिस्टार्ट गर्न ट्याप गर्नुहोस् र फुल स्क्रिन मोडमा जानुहोस्।"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"यो एप अझ राम्रो हेर्न मिल्ने बनाउनका लागि यसलाई रिस्टार्ट गर्न ट्याप गर्नुहोस्।"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्यामेरासम्बन्धी समस्या देखियो?\nसमस्या हल गर्न ट्याप गर्नुहोस्"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"समस्या हल भएन?\nपहिलेको जस्तै बनाउन ट्याप गर्नुहोस्"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्यामेरासम्बन्धी कुनै पनि समस्या छैन? खारेज गर्न ट्याप गर्नुहोस्।"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"तपाईं फुल स्क्रिन मोड हेर्न चाहनुहुन्छ भने आफ्नो डिभाइस रोटेट गर्नुहोस्"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"तपाईं जुन एपको स्थिति मिलाउन चाहनुहुन्छ सोही एपको छेउमा डबल ट्याप गर्नुहोस्"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"बुझेँ"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"थप जानकारी प्राप्त गर्न चाहनुहुन्छ भने एक्स्पान्ड गर्नुहोस्।"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"ठुलो बनाउनुहोस्"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"बन्द गर्नुहोस्"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 8468b04c66da..6ea24a8b3808 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Tik om deze app opnieuw te starten en te openen op het volledige scherm."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tik om deze app opnieuw op te starten voor een betere weergave."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Cameraproblemen?\nTik om opnieuw passend te maken."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Is dit geen oplossing?\nTik om terug te zetten."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen cameraproblemen? Tik om te sluiten."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Draai je apparaat om naar volledig scherm te schakelen"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dubbeltik naast een app om deze opnieuw te positioneren"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Uitvouwen voor meer informatie."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximaliseren"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Sluiten"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index a8d8448edf99..f8c924828d50 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ବବଲ୍"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ବବଲ୍ ଖାରଜ କରାଯାଇଛି।"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"ଏହି ଆପକୁ ରିଷ୍ଟାର୍ଟ କରି ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"ଏକ ଆହୁରି ଭଲ ଭ୍ୟୁ ପାଇଁ ଏହି ଆପ ରିଷ୍ଟାର୍ଟ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"କ୍ୟାମେରାରେ ସମସ୍ୟା ଅଛି?\nପୁଣି ଫିଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ଏହାର ସମାଧାନ ହୋଇନାହିଁ?\nଫେରିଯିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"କ୍ୟାମେରାରେ କିଛି ସମସ୍ୟା ନାହିଁ? ଖାରଜ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ପୂର୍ଣ୍ଣ-ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବାକୁ ଆପଣଙ୍କ ଡିଭାଇସକୁ ରୋଟେଟ କରନ୍ତୁ"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ଏକ ଆପକୁ ରିପୋଜିସନ କରିବା ପାଇଁ ଏହା ପାଖରେ ଦୁଇଥର-ଟାପ କରନ୍ତୁ"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ବୁଝିଗଲି"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ଅଧିକ ସୂଚନା ପାଇଁ ବିସ୍ତାର କରନ୍ତୁ।"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"ବଡ଼ କରନ୍ତୁ"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"ବନ୍ଦ କରନ୍ତୁ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index f99176cb682d..b80da0ba4e57 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"ਬੁਲਬੁਲਾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕੀਤਾ ਗਿਆ।"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਪੂਰੀ ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਓ।"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਲਈ ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਹਨ?\nਮੁੜ-ਫਿੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ਕੀ ਇਹ ਠੀਕ ਨਹੀਂ ਹੋਈ?\nਵਾਪਸ ਉਹੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਕੋਈ ਸਮੱਸਿਆ ਨਹੀਂ ਹੈ? ਖਾਰਜ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ਪੂਰੀ-ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਣ ਲਈ ਆਪਣੇ ਡੀਵਾਈਸ ਨੂੰ ਘੁਮਾਓ"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ਕਿਸੇ ਐਪ ਦੀ ਜਗ੍ਹਾ ਬਦਲਣ ਲਈ ਉਸ ਦੇ ਅੱਗੇ ਡਬਲ ਟੈਪ ਕਰੋ"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ਸਮਝ ਲਿਆ"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ ਵਿਸਤਾਰ ਕਰੋ।"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"ਵੱਡਾ ਕਰੋ"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"ਬੰਦ ਕਰੋ"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index f2147c04d335..bdd44dd8a743 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Dymek"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Zarządzaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Zamknięto dymek"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Kliknij, by uruchomić tę aplikację ponownie i przejść w tryb pełnoekranowy."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Kliknij, aby zrestartować aplikację i zyskać lepszą widoczność."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemy z aparatem?\nKliknij, aby dopasować"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Naprawa się nie udała?\nKliknij, aby cofnąć"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Brak problemów z aparatem? Kliknij, aby zamknąć"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Obróć urządzenie, aby przejść do pełnego ekranu"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Kliknij dwukrotnie obok aplikacji, aby ją przenieść"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Rozwiń, aby wyświetlić więcej informacji."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksymalizuj"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Zamknij"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 2efc5543dd87..b9e41eae5de9 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar o app e atualizar a visualização."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gire o dispositivo para entrar no modo de tela cheia"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toque duas vezes ao lado de um app para reposicionar"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendi"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Abra para ver mais informações."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index c68a6934dead..c1e57d8c9c97 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balão"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar esta app e ficar em ecrã inteiro."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar esta app e ficar com uma melhor visão."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmara?\nToque aqui para reajustar"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nenhum problema com a câmara? Toque para ignorar."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rode o dispositivo para ficar em ecrã inteiro"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toque duas vezes junto a uma app para a reposicionar"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expandir para obter mais informações"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 2efc5543dd87..b9e41eae5de9 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar o app e atualizar a visualização."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Gire o dispositivo para entrar no modo de tela cheia"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Toque duas vezes ao lado de um app para reposicionar"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendi"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Abra para ver mais informações."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Fechar"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 804d34f980ff..c49bf9dc231c 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Atingeți ca să reporniți aplicația și să treceți în modul ecran complet."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Atingeți ca să reporniți aplicația pentru o vizualizare mai bună."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Aveți probleme cu camera foto?\nAtingeți pentru a reîncadra"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nu ați remediat problema?\nAtingeți pentru a reveni"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nu aveți probleme cu camera foto? Atingeți pentru a închide."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotiți dispozitivul pentru a trece în modul ecran complet"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Atingeți de două ori lângă o aplicație pentru a o repoziționa"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Extindeți pentru mai multe informații"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximizați"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Închideți"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 95bf1cf11435..ffe031d86725 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Всплывающая подсказка"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Настроить"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Всплывающий чат закрыт."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Нажмите, чтобы перезапустить приложение и перейти в полноэкранный режим."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Нажмите, чтобы перезапустить приложение и настроить удобный для просмотра вид"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблемы с камерой?\nНажмите, чтобы исправить."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не помогло?\nНажмите, чтобы отменить изменения."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нет проблем с камерой? Нажмите, чтобы закрыть."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Чтобы перейти в полноэкранный режим, поверните устройство."</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Чтобы переместить приложение, нажмите на него дважды."</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ОК"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Развернуть, чтобы узнать больше."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Развернуть"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Закрыть"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 23dd65ad7b31..b27e1b9bc94a 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"බුබුළු"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"කළමනා කරන්න"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"බුබුල ඉවත දමා ඇත."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"මෙම යෙදුම යළි ඇරඹීමට සහ පූර්ණ තිරයට යාමට තට්ටු කරන්න."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"වඩා හොඳ දසුනක් ලබා ගැනීම සඳහා මෙම යෙදුම යළි ඇරඹීමට තට්ටු කරන්න."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"කැමරා ගැටලුද?\nයළි සවි කිරීමට තට්ටු කරන්න"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"එය විසඳුවේ නැතිද?\nප්‍රතිවර්තනය කිරීමට තට්ටු කරන්න"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"කැමරා ගැටලු නොමැතිද? ඉවත දැමීමට තට්ටු කරන්න"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"සම්පූර්ණ තිරයට යාමට ඔබගේ උපාංගය කරකවන්න"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"එය නැවත ස්ථානගත කිරීමට යෙදුමකට යාබදව දෙවරක් තට්ටු කරන්න"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"තේරුණා"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"වැඩිදුර තොරතුරු සඳහා දිග හරින්න"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"විහිදන්න"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"වසන්න"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index a231cacefb20..b5bedf79f3ba 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovať"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretá."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Klepnutím reštartujete túto aplikáciu a prejdete do režimu celej obrazovky."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Ak chcete zlepšiť zobrazenie, klepnutím túto aplikáciu reštartujte."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s kamerou?\nKlepnutím znova upravte."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nevyriešilo sa to?\nKlepnutím sa vráťte."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemáte problémy s kamerou? Klepnutím zatvoríte."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Otočením zariadenia prejdete do režimu celej obrazovky"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvojitým klepnutím vedľa aplikácie zmeníte jej pozíciu"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Dobre"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Po rozbalení sa dozviete viac."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maximalizovať"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Zavrieť"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index adeaae978eaa..ac926b9ee8db 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Mehurček"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblaček je bil opuščen."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Dotaknite se za vnovični zagon te aplikacije in preklop v celozaslonski način."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Če želite boljši prikaz, se dotaknite za vnovični zagon te aplikacije."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Težave s fotoaparatom?\nDotaknite se za vnovično prilagoditev"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"To ni odpravilo težave?\nDotaknite se za povrnitev"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nimate težav s fotoaparatom? Dotaknite se za opustitev."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Če želite preklopiti v celozaslonski način, zasukajte napravo."</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Dvakrat se dotaknite ob aplikaciji, če jo želite prestaviti."</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"V redu"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Razširitev za več informacij"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimiraj"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Zapri"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 2839b4bae7e4..07c52fe4251a 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Flluskë"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Trokit për ta rinisur këtë aplikacion dhe për të kaluar në ekranin e plotë."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Trokit për të rifilluar këtë aplikacion për një pamje më të mirë."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ka probleme me kamerën?\nTrokit për ta ripërshtatur"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nuk u rregullua?\nTrokit për ta rikthyer"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nuk ka probleme me kamerën? Trokit për ta shpërfillur."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rrotullo ekranin për të kaluar në ekran të plotë"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Trokit dy herë pranë një aplikacioni për ta ripozicionuar"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"E kuptova"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Zgjeroje për më shumë informacion."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Maksimizo"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Mbyll"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 9db6b7c63610..0289dd105ee5 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Облачић"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Додирните да бисте рестартовали апликацију и прешли у режим целог екрана."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Додирните да бисте рестартовали ову апликацију ради бољег приказа."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблема са камером?\nДодирните да бисте поново уклопили"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблем није решен?\nДодирните да бисте вратили"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немате проблема са камером? Додирните да бисте одбацили."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Ротирајте уређај за приказ преко целог екрана"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Двапут додирните поред апликације да бисте променили њену позицију"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Важи"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Проширите за још информација."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Увећајте"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Затворите"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index f6bd55423cdc..cfdb1ddcf377 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Tryck för att starta om appen i helskärmsläge."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Tryck för att starta om appen och få en bättre vy."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problem med kameran?\nTryck för att anpassa på nytt"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Löstes inte problemet?\nTryck för att återställa"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Inga problem med kameran? Tryck för att ignorera."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Rotera skärmen för att gå över till helskärmsläge"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Tryck snabbt två gånger bredvid en app för att flytta den"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Utöka för mer information."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Utöka"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Stäng"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index f6e558527ee5..383e9bb6bbfa 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Kiputo"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Gusa ili uzime na uwashe programu hii, kisha nenda kwenye skrini nzima."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Gusa ili uzime kisha uwashe programu hii, ili upate mwonekano bora."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Je, kuna hitilafu za kamera?\nGusa ili urekebishe"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Umeshindwa kurekebisha?\nGusa ili urejeshe nakala ya awali"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Je, hakuna hitilafu za kamera? Gusa ili uondoe."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zungusha kifaa chako ili uende kwenye hali ya skrini nzima"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Gusa mara mbili karibu na programu ili uihamishe"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Nimeelewa"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Panua ili upate maelezo zaidi."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Panua"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Funga"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index d8334adfe5ef..cc512f3c48aa 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"பபிள்"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"நிர்வகி"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"குமிழ் நிராகரிக்கப்பட்டது."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கலாம், முழுத்திரையில் பார்க்கலாம்."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"இங்கு தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கி, ஆப்ஸ் காட்டப்படும் விதத்தை இன்னும் சிறப்பாக்கலாம்."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"கேமரா தொடர்பான சிக்கல்களா?\nமீண்டும் பொருத்த தட்டவும்"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"சிக்கல்கள் சரிசெய்யப்படவில்லையா?\nமாற்றியமைக்க தட்டவும்"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"கேமரா தொடர்பான சிக்கல்கள் எதுவும் இல்லையா? நிராகரிக்க தட்டவும்."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"முழுத்திரைக்குச் செல்ல உங்கள் சாதனத்தைச் சுழற்றவும்"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"ஆப்ஸை இடம் மாற்ற, ஆப்ஸுக்கு அடுத்து இருமுறை தட்டவும்"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"சரி"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"கூடுதல் தகவல்களுக்கு விரிவாக்கலாம்."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"பெரிதாக்கும்"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"மூடும்"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 733075550007..cdbe021add47 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"బబుల్"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"మేనేజ్ చేయండి"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"బబుల్ విస్మరించబడింది."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"ఈ యాప్‌ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేసి, ఆపై పూర్తి స్క్రీన్‌లోకి వెళ్లండి."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"మెరుగైన వీక్షణ కోసం ఈ యాప్‌ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేయండి."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"కెమెరా సమస్యలు ఉన్నాయా?\nరీఫిట్ చేయడానికి ట్యాప్ చేయండి"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"దాని సమస్యను పరిష్కరించలేదా?\nపూర్వస్థితికి మార్చడానికి ట్యాప్ చేయండి"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"కెమెరా సమస్యలు లేవా? తీసివేయడానికి ట్యాప్ చేయండి."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"ఫుల్ స్క్రీన్‌కు వెళ్లడానికి మీ పరికరాన్ని తిప్పండి"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"యాప్ స్థానాన్ని మార్చడానికి దాని పక్కన డబుల్-ట్యాప్ చేయండి"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"అర్థమైంది"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"మరింత సమాచారం కోసం విస్తరించండి."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"గరిష్టీకరించండి"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"మూసివేయండి"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index cfee8ea3242e..136a81c06c4f 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"บับเบิล"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"จัดการ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ปิดบับเบิลแล้ว"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"แตะเพื่อรีสตาร์ทแอปนี้และแสดงแบบเต็มหน้าจอ"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"แตะเพื่อรีสตาร์ทแอปนี้และรับมุมมองที่ดียิ่งขึ้น"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"หากพบปัญหากับกล้อง\nแตะเพื่อแก้ไข"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"หากไม่ได้แก้ไข\nแตะเพื่อเปลี่ยนกลับ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"หากไม่พบปัญหากับกล้อง แตะเพื่อปิด"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"หมุนอุปกรณ์ให้แสดงเต็มหน้าจอ"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"แตะสองครั้งข้างแอปเพื่อเปลี่ยนตำแหน่ง"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"รับทราบ"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ขยายเพื่อดูข้อมูลเพิ่มเติม"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"ขยายใหญ่สุด"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"ปิด"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index eed624dd5069..4d32af36cabe 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"I-tap para i-restart ang app na ito at mag-full screen."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"I-tap para i-restart ang app na ito para sa mas magandang view."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"May mga isyu sa camera?\nI-tap para i-refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Hindi ito naayos?\nI-tap para i-revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Walang isyu sa camera? I-tap para i-dismiss."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"I-rotate ang iyong device para mag-full screen"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Mag-double tap sa tabi ng isang app para iposisyon ito ulit"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"I-expand para sa higit pang impormasyon."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"I-maximize"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Isara"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 2b4a2d0550f0..f3ab37065270 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Baloncuk"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Yönet"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Bu uygulamayı yeniden başlatmak ve tam ekrana geçmek için dokunun."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Bu uygulamayı yeniden başlatarak daha iyi bir görünüm elde etmek için dokunun."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kameranızda sorun mu var?\nDüzeltmek için dokunun"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bu işlem sorunu düzeltmedi mi?\nİşlemi geri almak için dokunun"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kameranızda sorun yok mu? Kapatmak için dokunun."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Tam ekrana geçmek için cihazınızı döndürün"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Yeniden konumlandırmak için uygulamanın yanına iki kez dokunun"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Anladım"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Daha fazla bilgi için genişletin."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Ekranı Kapla"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Kapat"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index c3411a837c78..d7d82cb56ac5 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Спливаюче сповіщення"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Налаштувати"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Спливаюче сповіщення закрито."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Натисніть, щоб перезапустити додаток і перейти в повноекранний режим."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Натисніть, щоб перезапустити цей додаток для зручнішого перегляду."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми з камерою?\nНатисніть, щоб пристосувати"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблему не вирішено?\nНатисніть, щоб скасувати зміни"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немає проблем із камерою? Торкніться, щоб закрити."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Щоб перейти в повноекранний режим, поверніть пристрій"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Щоб перемістити додаток, двічі торкніться області поруч із ним"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ОK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Розгорніть, щоб дізнатися більше."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Збільшити"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Закрити"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index a31c2be25643..4a8476aebe18 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"نظم کریں"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"بلبلہ برخاست کر دیا گیا۔"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"یہ ایپ دوبارہ شروع کرنے کے لیے تھپتھپائیں اور پوری اسکرین پر جائیں۔"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"بہتر منظر کے لیے اس ایپ کو ری اسٹارٹ کرنے کی خاطر تھپتھپائیں۔"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"کیمرے کے مسائل؟\nدوبارہ فٹ کرنے کیلئے تھپتھپائیں"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"یہ حل نہیں ہوا؟\nلوٹانے کیلئے تھپتھپائیں"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"کوئی کیمرے کا مسئلہ نہیں ہے؟ برخاست کرنے کیلئے تھپتھپائیں۔"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"پوری اسکرین پر جانے کیلئے اپنا آلہ گھمائیں"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"کسی ایپ کی پوزیشن تبدیل کرنے کے لیے اس کے آگے دو بار تھپتھپائیں"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"سمجھ آ گئی"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"مزید معلومات کے لیے پھیلائیں۔"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"بڑا کریں"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"بند کریں"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 2e3222560dde..8a4eac3bb657 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Pufaklar"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Bu ilovani qaytadan ishga tushirish va butun ekranda ochish uchun bosing."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Yaxshiroq koʻrish maqsadida bu ilovani qayta ishga tushirish uchun bosing."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera nosozmi?\nQayta moslash uchun bosing"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tuzatilmadimi?\nQaytarish uchun bosing"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera muammosizmi? Yopish uchun bosing."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Butun ekranda ochish uchun qurilmani buring"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Qayta joylash uchun keyingi ilova ustiga ikki marta bosing"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Batafsil axborot olish uchun kengaytiring."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Yoyish"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Yopish"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 8f3cffecc952..2f8fe6076c68 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bong bóng"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Quản lý"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Đã đóng bong bóng."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Nhấn để khởi động lại ứng dụng này và xem ở chế độ toàn màn hình."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Nhấn để khởi động lại ứng dụng này để xem tốt hơn."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Có vấn đề với máy ảnh?\nHãy nhấn để sửa lỗi"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bạn chưa khắc phục vấn đề?\nHãy nhấn để hủy bỏ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Không có vấn đề với máy ảnh? Hãy nhấn để đóng."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Xoay thiết bị để chuyển sang chế độ toàn màn hình"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Nhấn đúp vào bên cạnh ứng dụng để đặt lại vị trí"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Mở rộng để xem thêm thông tin."</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Phóng to"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Đóng"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 19a9d371e435..ab44fb1d2488 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"气泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭对话泡。"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"点按即可重启此应用并进入全屏模式。"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"点按即可重启此应用,获得更好的视图体验。"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相机有问题?\n点按即可整修"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"没有解决此问题?\n点按即可恢复"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相机没有问题?点按即可忽略。"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"旋转设备即可进入全屏模式"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"在某个应用旁边连续点按两次,即可调整它的位置"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"知道了"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"展开即可了解详情。"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"关闭"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 0c40e963f2e4..8fb7adebe930 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"氣泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"輕按即可重新開啟此應用程式並放大至全螢幕。"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"輕按並重新啟動此應用程式,以取得更佳的觀看體驗。"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題?\n輕按即可修正"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未能修正問題?\n輕按即可還原"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機冇問題?㩒一下就可以即可閂咗佢。"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"旋轉裝置方向即可進入全螢幕模式"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"在應用程式旁輕按兩下即可調整位置"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"知道了"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"展開即可查看詳情。"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"關閉"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 8691352cf94a..45de4156084b 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"泡泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已關閉泡泡。"</string>
- <string name="restart_button_description" msgid="5887656107651190519">"輕觸即可重新啟動這個應用程式並進入全螢幕模式。"</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"請輕觸並重新啟動此應用程式,取得更良好的觀看體驗。"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題嗎?\n輕觸即可修正"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未修正問題嗎?\n輕觸即可還原"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機沒問題嗎?輕觸即可關閉。"</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"旋轉裝置方向即可進入全螢幕模式"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"在應用程式旁輕觸兩下即可調整位置"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"我知道了"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"展開即可查看詳細資訊。"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"關閉"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 44ffbc6afa45..7c31a166e825 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -72,7 +72,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Ibhamuza"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string>
- <string name="restart_button_description" msgid="5887656107651190519">"Thepha ukuze uqale kabusha lolu hlelo lokusebenza uphinde uye kusikrini esigcwele."</string>
+ <string name="restart_button_description" msgid="6712141648865547958">"Thepha ukuze uqale kabusha le app ukuze ibonakale kangcono."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Izinkinga zekhamera?\nThepha ukuze uyilinganise kabusha"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Akuyilungisanga?\nThepha ukuze ubuyele"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Azikho izinkinga zekhamera? Thepha ukuze ucashise."</string>
@@ -81,4 +81,7 @@
<string name="letterbox_education_screen_rotation_text" msgid="5085786687366339027">"Zungezisa idivayisi yakho ukuze uye esikrinini esigcwele"</string>
<string name="letterbox_education_reposition_text" msgid="1068293354123934727">"Thepha kabili eduze kwe-app ukuze uyimise kabusha"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ngiyezwa"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Nweba ukuze uthole ulwazi olwengeziwe"</string>
+ <string name="maximize_button_text" msgid="1650859196290301963">"Khulisa"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Vala"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/attrs.xml b/libs/WindowManager/Shell/res/values/attrs.xml
index 4aaeef8afcb0..2aad4c1c1805 100644
--- a/libs/WindowManager/Shell/res/values/attrs.xml
+++ b/libs/WindowManager/Shell/res/values/attrs.xml
@@ -19,4 +19,8 @@
<attr name="icon" format="reference" />
<attr name="text" format="string" />
</declare-styleable>
+
+ <declare-styleable name="MessageState">
+ <attr name="state_task_focused" format="boolean"/>
+ </declare-styleable>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index a24311fb1f21..68a08513e7f5 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -157,7 +157,7 @@
<string name="accessibility_bubble_dismissed">Bubble dismissed.</string>
<!-- Description of the restart button in the hint of size compatibility mode. [CHAR LIMIT=NONE] -->
- <string name="restart_button_description">Tap to restart this app and go full screen.</string>
+ <string name="restart_button_description">Tap to restart this app for a better view.</string>
<!-- Description of the camera compat button for applying stretched issues treatment in the hint for
compatibility control. [CHAR LIMIT=NONE] -->
@@ -186,4 +186,12 @@
<!-- Button text for dismissing the letterbox education dialog. [CHAR LIMIT=20] -->
<string name="letterbox_education_got_it">Got it</string>
+ <!-- Accessibility description of the letterbox education toast expand to dialog button. [CHAR LIMIT=NONE] -->
+ <string name="letterbox_education_expand_button_description">Expand for more information.</string>
+
+ <!-- Freeform window caption strings -->
+ <!-- Accessibility text for the maximize window button [CHAR LIMIT=NONE] -->
+ <string name="maximize_button_text">Maximize</string>
+ <!-- Accessibility text for the close window button [CHAR LIMIT=NONE] -->
+ <string name="close_button_text">Close</string>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
index 14ba9df93f24..764e650a807c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
@@ -85,6 +85,8 @@ public class RootDisplayAreaOrganizer extends DisplayAreaOrganizer {
}
mDisplayAreasInfo.remove(displayId);
+ mLeashes.get(displayId).release();
+ mLeashes.remove(displayId);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 31f0ef0192ae..7d7c59eb17da 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
+import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -55,6 +56,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.startingsurface.StartingWindowController;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -179,33 +181,41 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
private final Optional<RecentTasksController> mRecentTasks;
@Nullable
+ private final UnfoldAnimationController mUnfoldAnimationController;
+
+ @Nullable
private RunningTaskInfo mLastFocusedTaskInfo;
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
this(null /* taskOrganizerController */, mainExecutor, context, null /* compatUI */,
+ Optional.empty() /* unfoldAnimationController */,
Optional.empty() /* recentTasksController */);
}
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
CompatUIController compatUI) {
this(null /* taskOrganizerController */, mainExecutor, context, compatUI,
+ Optional.empty() /* unfoldAnimationController */,
Optional.empty() /* recentTasksController */);
}
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
CompatUIController compatUI,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasks) {
this(null /* taskOrganizerController */, mainExecutor, context, compatUI,
- recentTasks);
+ unfoldAnimationController, recentTasks);
}
@VisibleForTesting
protected ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController,
ShellExecutor mainExecutor, Context context, @Nullable CompatUIController compatUI,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasks) {
super(taskOrganizerController, mainExecutor);
mCompatUI = compatUI;
mRecentTasks = recentTasks;
+ mUnfoldAnimationController = unfoldAnimationController.orElse(null);
if (compatUI != null) {
compatUI.setCompatUICallback(this);
}
@@ -437,6 +447,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
if (listener != null) {
listener.onTaskAppeared(info.getTaskInfo(), info.getLeash());
}
+ if (mUnfoldAnimationController != null) {
+ mUnfoldAnimationController.onTaskAppeared(info.getTaskInfo(), info.getLeash());
+ }
notifyLocusVisibilityIfNeeded(info.getTaskInfo());
notifyCompatUI(info.getTaskInfo(), listener);
}
@@ -458,6 +471,11 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId);
+
+ if (mUnfoldAnimationController != null) {
+ mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
+ }
+
final TaskAppearedInfo data = mTasks.get(taskInfo.taskId);
final TaskListener oldListener = getTaskListener(data.getTaskInfo());
final TaskListener newListener = getTaskListener(taskInfo);
@@ -482,7 +500,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|| (taskInfo.topActivityType == WindowConfiguration.ACTIVITY_TYPE_HOME
&& taskInfo.isVisible);
final boolean focusTaskChanged = (mLastFocusedTaskInfo == null
- || mLastFocusedTaskInfo.taskId != taskInfo.taskId) && isFocusedOrHome;
+ || mLastFocusedTaskInfo.taskId != taskInfo.taskId
+ || mLastFocusedTaskInfo.getWindowingMode() != taskInfo.getWindowingMode())
+ && isFocusedOrHome;
if (focusTaskChanged) {
for (int i = 0; i < mFocusListeners.size(); i++) {
mFocusListeners.valueAt(i).onFocusTaskChanged(taskInfo);
@@ -507,8 +527,13 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
public void onTaskVanished(RunningTaskInfo taskInfo) {
synchronized (mLock) {
ProtoLog.v(WM_SHELL_TASK_ORG, "Task vanished taskId=%d", taskInfo.taskId);
+ if (mUnfoldAnimationController != null) {
+ mUnfoldAnimationController.onTaskVanished(taskInfo);
+ }
+
final int taskId = taskInfo.taskId;
- final TaskListener listener = getTaskListener(mTasks.get(taskId).getTaskInfo());
+ final TaskAppearedInfo appearedInfo = mTasks.get(taskId);
+ final TaskListener listener = getTaskListener(appearedInfo.getTaskInfo());
mTasks.remove(taskId);
if (listener != null) {
listener.onTaskVanished(taskInfo);
@@ -518,6 +543,11 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
notifyCompatUI(taskInfo, null /* taskListener */);
// Notify the recent tasks that a task has been removed
mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo));
+
+ if (!ENABLE_SHELL_TRANSITIONS && (appearedInfo.getLeash() != null)) {
+ // Preemptively clean up the leash only if shell transitions are not enabled
+ appearedInfo.getLeash().release();
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
new file mode 100644
index 000000000000..82b0270698e4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.activityembedding;
+
+import static android.window.TransitionInfo.FLAG_IS_EMBEDDED;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.transition.Transitions;
+
+/**
+ * Responsible for handling ActivityEmbedding related transitions.
+ */
+public class ActivityEmbeddingController implements Transitions.TransitionHandler {
+
+ private final Context mContext;
+ private final Transitions mTransitions;
+
+ public ActivityEmbeddingController(Context context, Transitions transitions) {
+ mContext = context;
+ mTransitions = transitions;
+ }
+
+ /** Registers to handle transitions. */
+ public void init() {
+ mTransitions.addHandler(this);
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ // TODO(b/207070762) Handle AE animation as a part of other transitions.
+ // Only handle the transition if all containers are embedded.
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (!isEmbedded(change)) {
+ return false;
+ }
+ }
+
+ // TODO(b/207070762) Implement AE animation.
+ startTransaction.apply();
+ finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ return true;
+ }
+
+ @Nullable
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request) {
+ return null;
+ }
+
+ private static boolean isEmbedded(@NonNull TransitionInfo.Change change) {
+ return (change.getFlags() & FLAG_IS_EMBEDDED) != 0;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
index 2aead9392e59..a0dde6ad168d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
@@ -53,6 +53,19 @@ public class Interpolators {
public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
/**
+ * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
+ * is disappearing e.g. when moving off screen.
+ */
+ public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
+ 0.3f, 0f, 0.8f, 0.15f);
+
+ /**
+ * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
+ * is appearing e.g. when coming from off screen
+ */
+ public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
+ 0.05f, 0.7f, 0.1f, 1f);
+ /**
* Interpolator to be used when animating a move based on a click. Pair with enough duration.
*/
public static final Interpolator TOUCH_RESPONSE = new PathInterpolator(0.3f, 0f, 0.1f, 1f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
index b483fe03e80f..312af4ff7bc2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
@@ -829,8 +829,12 @@ class PhysicsAnimator<T> private constructor (target: T) {
/** Cancels all in progress animations on all properties. */
fun cancel() {
- cancelAction(flingAnimations.keys)
- cancelAction(springAnimations.keys)
+ if (flingAnimations.size > 0) {
+ cancelAction(flingAnimations.keys)
+ }
+ if (springAnimations.size > 0) {
+ cancelAction(springAnimations.keys)
+ }
}
/** Cancels in progress animations on the provided properties only. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
deleted file mode 100644
index 3f0b01bef0ce..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.apppairs;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
-
-import android.app.ActivityManager;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.DisplayInsetsController;
-import com.android.wm.shell.common.SurfaceUtils;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.split.SplitLayout;
-import com.android.wm.shell.common.split.SplitWindowManager;
-
-import java.io.PrintWriter;
-
-/**
- * An app-pairs consisting of {@link #mRootTaskInfo} that acts as the hierarchy parent of
- * {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair.
- * Also includes all UI for managing the pair like the divider.
- */
-class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayoutHandler {
- private static final String TAG = AppPair.class.getSimpleName();
-
- private ActivityManager.RunningTaskInfo mRootTaskInfo;
- private SurfaceControl mRootTaskLeash;
- private ActivityManager.RunningTaskInfo mTaskInfo1;
- private SurfaceControl mTaskLeash1;
- private ActivityManager.RunningTaskInfo mTaskInfo2;
- private SurfaceControl mTaskLeash2;
- private SurfaceControl mDimLayer1;
- private SurfaceControl mDimLayer2;
- private final SurfaceSession mSurfaceSession = new SurfaceSession();
-
- private final AppPairsController mController;
- private final SyncTransactionQueue mSyncQueue;
- private final DisplayController mDisplayController;
- private final DisplayImeController mDisplayImeController;
- private final DisplayInsetsController mDisplayInsetsController;
- private SplitLayout mSplitLayout;
-
- private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
- new SplitWindowManager.ParentContainerCallbacks() {
- @Override
- public void attachToParentSurface(SurfaceControl.Builder b) {
- b.setParent(mRootTaskLeash);
- }
-
- @Override
- public void onLeashReady(SurfaceControl leash) {
- mSyncQueue.runInSync(t -> t
- .show(leash)
- .setLayer(leash, Integer.MAX_VALUE)
- .setPosition(leash,
- mSplitLayout.getDividerBounds().left,
- mSplitLayout.getDividerBounds().top));
- }
- };
-
- AppPair(AppPairsController controller) {
- mController = controller;
- mSyncQueue = controller.getSyncTransactionQueue();
- mDisplayController = controller.getDisplayController();
- mDisplayImeController = controller.getDisplayImeController();
- mDisplayInsetsController = controller.getDisplayInsetsController();
- }
-
- int getRootTaskId() {
- return mRootTaskInfo != null ? mRootTaskInfo.taskId : INVALID_TASK_ID;
- }
-
- private int getTaskId1() {
- return mTaskInfo1 != null ? mTaskInfo1.taskId : INVALID_TASK_ID;
- }
-
- private int getTaskId2() {
- return mTaskInfo2 != null ? mTaskInfo2.taskId : INVALID_TASK_ID;
- }
-
- boolean contains(int taskId) {
- return taskId == getRootTaskId() || taskId == getTaskId1() || taskId == getTaskId2();
- }
-
- boolean pair(ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) {
- ProtoLog.v(WM_SHELL_TASK_ORG, "pair task1=%d task2=%d in AppPair=%s",
- task1.taskId, task2.taskId, this);
-
- if (!task1.supportsMultiWindow || !task2.supportsMultiWindow) {
- ProtoLog.e(WM_SHELL_TASK_ORG,
- "Can't pair tasks that doesn't support multi window, "
- + "task1.supportsMultiWindow=%b, task2.supportsMultiWindow=%b",
- task1.supportsMultiWindow, task2.supportsMultiWindow);
- return false;
- }
-
- mTaskInfo1 = task1;
- mTaskInfo2 = task2;
- mSplitLayout = new SplitLayout(TAG + "SplitDivider",
- mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
- mRootTaskInfo.configuration, this /* layoutChangeListener */,
- mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer(),
- SplitLayout.PARALLAX_DISMISSING);
- mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout);
-
- final WindowContainerToken token1 = task1.token;
- final WindowContainerToken token2 = task2.token;
- final WindowContainerTransaction wct = new WindowContainerTransaction();
-
- wct.setHidden(mRootTaskInfo.token, false)
- .reparent(token1, mRootTaskInfo.token, true /* onTop */)
- .reparent(token2, mRootTaskInfo.token, true /* onTop */)
- .setWindowingMode(token1, WINDOWING_MODE_MULTI_WINDOW)
- .setWindowingMode(token2, WINDOWING_MODE_MULTI_WINDOW)
- .setBounds(token1, mSplitLayout.getBounds1())
- .setBounds(token2, mSplitLayout.getBounds2())
- // Moving the root task to top after the child tasks were repareted , or the root
- // task cannot be visible and focused.
- .reorder(mRootTaskInfo.token, true);
- mController.getTaskOrganizer().applyTransaction(wct);
- return true;
- }
-
- void unpair() {
- unpair(null /* toTopToken */);
- }
-
- private void unpair(@Nullable WindowContainerToken toTopToken) {
- final WindowContainerToken token1 = mTaskInfo1.token;
- final WindowContainerToken token2 = mTaskInfo2.token;
- final WindowContainerTransaction wct = new WindowContainerTransaction();
-
- // Reparent out of this container and reset windowing mode.
- wct.setHidden(mRootTaskInfo.token, true)
- .reorder(mRootTaskInfo.token, false)
- .reparent(token1, null, token1 == toTopToken /* onTop */)
- .reparent(token2, null, token2 == toTopToken /* onTop */)
- .setWindowingMode(token1, WINDOWING_MODE_UNDEFINED)
- .setWindowingMode(token2, WINDOWING_MODE_UNDEFINED);
- mController.getTaskOrganizer().applyTransaction(wct);
-
- mTaskInfo1 = null;
- mTaskInfo2 = null;
- mSplitLayout.release();
- mSplitLayout = null;
- }
-
- @Override
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- if (mRootTaskInfo == null || taskInfo.taskId == mRootTaskInfo.taskId) {
- mRootTaskInfo = taskInfo;
- mRootTaskLeash = leash;
- } else if (taskInfo.taskId == getTaskId1()) {
- mTaskInfo1 = taskInfo;
- mTaskLeash1 = leash;
- mSyncQueue.runInSync(t -> mDimLayer1 =
- SurfaceUtils.makeDimLayer(t, mTaskLeash1, "Dim layer", mSurfaceSession));
- } else if (taskInfo.taskId == getTaskId2()) {
- mTaskInfo2 = taskInfo;
- mTaskLeash2 = leash;
- mSyncQueue.runInSync(t -> mDimLayer2 =
- SurfaceUtils.makeDimLayer(t, mTaskLeash2, "Dim layer", mSurfaceSession));
- } else {
- throw new IllegalStateException("Unknown task=" + taskInfo.taskId);
- }
-
- if (mTaskLeash1 == null || mTaskLeash2 == null) return;
-
- mSplitLayout.init();
-
- mSyncQueue.runInSync(t -> t
- .show(mRootTaskLeash)
- .show(mTaskLeash1)
- .show(mTaskLeash2)
- .setPosition(mTaskLeash1,
- mTaskInfo1.positionInParent.x,
- mTaskInfo1.positionInParent.y)
- .setPosition(mTaskLeash2,
- mTaskInfo2.positionInParent.x,
- mTaskInfo2.positionInParent.y));
- }
-
- @Override
- public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- if (!taskInfo.supportsMultiWindow) {
- // Dismiss AppPair if the task no longer supports multi window.
- mController.unpair(mRootTaskInfo.taskId);
- return;
- }
- if (taskInfo.taskId == getRootTaskId()) {
- if (mRootTaskInfo.isVisible != taskInfo.isVisible) {
- mSyncQueue.runInSync(t -> {
- if (taskInfo.isVisible) {
- t.show(mRootTaskLeash);
- } else {
- t.hide(mRootTaskLeash);
- }
- });
- }
- mRootTaskInfo = taskInfo;
-
- if (mSplitLayout != null
- && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
- onLayoutSizeChanged(mSplitLayout);
- }
- } else if (taskInfo.taskId == getTaskId1()) {
- mTaskInfo1 = taskInfo;
- } else if (taskInfo.taskId == getTaskId2()) {
- mTaskInfo2 = taskInfo;
- } else {
- throw new IllegalStateException("Unknown task=" + taskInfo.taskId);
- }
- }
-
- @Override
- public int getSplitItemPosition(WindowContainerToken token) {
- if (token == null) {
- return SPLIT_POSITION_UNDEFINED;
- }
-
- if (token.equals(mTaskInfo1.getToken())) {
- return SPLIT_POSITION_TOP_OR_LEFT;
- } else if (token.equals(mTaskInfo2.getToken())) {
- return SPLIT_POSITION_BOTTOM_OR_RIGHT;
- }
-
- return SPLIT_POSITION_UNDEFINED;
- }
-
- @Override
- public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- if (taskInfo.taskId == getRootTaskId()) {
- // We don't want to release this object back to the pool since the root task went away.
- mController.unpair(mRootTaskInfo.taskId, false /* releaseToPool */);
- } else if (taskInfo.taskId == getTaskId1()) {
- mController.unpair(mRootTaskInfo.taskId);
- mSyncQueue.runInSync(t -> t.remove(mDimLayer1));
- } else if (taskInfo.taskId == getTaskId2()) {
- mController.unpair(mRootTaskInfo.taskId);
- mSyncQueue.runInSync(t -> t.remove(mDimLayer2));
- }
- }
-
- @Override
- public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
- b.setParent(findTaskSurface(taskId));
- }
-
- @Override
- public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
- SurfaceControl.Transaction t) {
- t.reparent(sc, findTaskSurface(taskId));
- }
-
- private SurfaceControl findTaskSurface(int taskId) {
- if (getRootTaskId() == taskId) {
- return mRootTaskLeash;
- } else if (getTaskId1() == taskId) {
- return mTaskLeash1;
- } else if (getTaskId2() == taskId) {
- return mTaskLeash2;
- } else {
- throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
- }
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw, String prefix) {
- final String innerPrefix = prefix + " ";
- final String childPrefix = innerPrefix + " ";
- pw.println(prefix + this);
- if (mRootTaskInfo != null) {
- pw.println(innerPrefix + "Root taskId=" + mRootTaskInfo.taskId
- + " winMode=" + mRootTaskInfo.getWindowingMode());
- }
- if (mTaskInfo1 != null) {
- pw.println(innerPrefix + "1 taskId=" + mTaskInfo1.taskId
- + " winMode=" + mTaskInfo1.getWindowingMode());
- }
- if (mTaskInfo2 != null) {
- pw.println(innerPrefix + "2 taskId=" + mTaskInfo2.taskId
- + " winMode=" + mTaskInfo2.getWindowingMode());
- }
- }
-
- @Override
- public String toString() {
- return TAG + "#" + getRootTaskId();
- }
-
- @Override
- public void onSnappedToDismiss(boolean snappedToEnd) {
- unpair(snappedToEnd ? mTaskInfo1.token : mTaskInfo2.token /* toTopToken */);
- }
-
- @Override
- public void onLayoutPositionChanging(SplitLayout layout) {
- mSyncQueue.runInSync(t ->
- layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
- true /* applyResizingOffset */));
- }
-
- @Override
- public void onLayoutSizeChanging(SplitLayout layout) {
- mSyncQueue.runInSync(t ->
- layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
- true /* applyResizingOffset */));
- }
-
- @Override
- public void onLayoutSizeChanged(SplitLayout layout) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2);
- mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t ->
- layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
- false /* applyResizingOffset */));
- }
-
- @Override
- public void setLayoutOffsetTarget(int offsetX, int offsetY, SplitLayout layout) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- layout.applyLayoutOffsetTarget(wct, offsetX, offsetY, mTaskInfo1, mTaskInfo2);
- mController.getTaskOrganizer().applyTransaction(wct);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
deleted file mode 100644
index a9b1dbc3c23b..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.apppairs;
-
-import android.app.ActivityManager;
-
-import androidx.annotation.NonNull;
-
-import com.android.wm.shell.common.annotations.ExternalThread;
-
-import java.io.PrintWriter;
-
-/**
- * Interface to engage app pairs feature.
- */
-@ExternalThread
-public interface AppPairs {
- /** Pairs indicated tasks. */
- boolean pair(int task1, int task2);
- /** Pairs indicated tasks. */
- boolean pair(ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2);
- /** Unpairs any app-pair containing this task id. */
- void unpair(int taskId);
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
deleted file mode 100644
index 53234ab971d6..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.apppairs;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
-
-import android.app.ActivityManager;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.DisplayInsetsController;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import java.io.PrintWriter;
-
-/**
- * Class manages app-pairs multitasking mode and implements the main interface {@link AppPairs}.
- */
-public class AppPairsController {
- private static final String TAG = AppPairsController.class.getSimpleName();
-
- private final ShellTaskOrganizer mTaskOrganizer;
- private final SyncTransactionQueue mSyncQueue;
- private final ShellExecutor mMainExecutor;
- private final AppPairsImpl mImpl = new AppPairsImpl();
-
- private AppPairsPool mPairsPool;
- // Active app-pairs mapped by root task id key.
- private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>();
- private final DisplayController mDisplayController;
- private final DisplayImeController mDisplayImeController;
- private final DisplayInsetsController mDisplayInsetsController;
-
- public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
- DisplayController displayController, ShellExecutor mainExecutor,
- DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController) {
- mTaskOrganizer = organizer;
- mSyncQueue = syncQueue;
- mDisplayController = displayController;
- mDisplayImeController = displayImeController;
- mDisplayInsetsController = displayInsetsController;
- mMainExecutor = mainExecutor;
- }
-
- public AppPairs asAppPairs() {
- return mImpl;
- }
-
- public void onOrganizerRegistered() {
- if (mPairsPool == null) {
- setPairsPool(new AppPairsPool(this));
- }
- }
-
- @VisibleForTesting
- public void setPairsPool(AppPairsPool pool) {
- mPairsPool = pool;
- }
-
- public boolean pair(int taskId1, int taskId2) {
- final ActivityManager.RunningTaskInfo task1 = mTaskOrganizer.getRunningTaskInfo(taskId1);
- final ActivityManager.RunningTaskInfo task2 = mTaskOrganizer.getRunningTaskInfo(taskId2);
- if (task1 == null || task2 == null) {
- return false;
- }
- return pair(task1, task2);
- }
-
- public boolean pair(ActivityManager.RunningTaskInfo task1,
- ActivityManager.RunningTaskInfo task2) {
- return pairInner(task1, task2) != null;
- }
-
- @VisibleForTesting
- public AppPair pairInner(
- @NonNull ActivityManager.RunningTaskInfo task1,
- @NonNull ActivityManager.RunningTaskInfo task2) {
- final AppPair pair = mPairsPool.acquire();
- if (!pair.pair(task1, task2)) {
- mPairsPool.release(pair);
- return null;
- }
-
- mActiveAppPairs.put(pair.getRootTaskId(), pair);
- return pair;
- }
-
- public void unpair(int taskId) {
- unpair(taskId, true /* releaseToPool */);
- }
-
- public void unpair(int taskId, boolean releaseToPool) {
- AppPair pair = mActiveAppPairs.get(taskId);
- if (pair == null) {
- for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
- final AppPair candidate = mActiveAppPairs.valueAt(i);
- if (candidate.contains(taskId)) {
- pair = candidate;
- break;
- }
- }
- }
- if (pair == null) {
- ProtoLog.v(WM_SHELL_TASK_ORG, "taskId %d isn't isn't in an app-pair.", taskId);
- return;
- }
-
- ProtoLog.v(WM_SHELL_TASK_ORG, "unpair taskId=%d pair=%s", taskId, pair);
- mActiveAppPairs.remove(pair.getRootTaskId());
- pair.unpair();
- if (releaseToPool) {
- mPairsPool.release(pair);
- }
- }
-
- ShellTaskOrganizer getTaskOrganizer() {
- return mTaskOrganizer;
- }
-
- SyncTransactionQueue getSyncTransactionQueue() {
- return mSyncQueue;
- }
-
- DisplayController getDisplayController() {
- return mDisplayController;
- }
-
- DisplayImeController getDisplayImeController() {
- return mDisplayImeController;
- }
-
- DisplayInsetsController getDisplayInsetsController() {
- return mDisplayInsetsController;
- }
-
- public void dump(@NonNull PrintWriter pw, String prefix) {
- final String innerPrefix = prefix + " ";
- final String childPrefix = innerPrefix + " ";
- pw.println(prefix + this);
-
- for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
- mActiveAppPairs.valueAt(i).dump(pw, childPrefix);
- }
-
- if (mPairsPool != null) {
- mPairsPool.dump(pw, prefix);
- }
- }
-
- @Override
- public String toString() {
- return TAG + "#" + mActiveAppPairs.size();
- }
-
- private class AppPairsImpl implements AppPairs {
- @Override
- public boolean pair(int task1, int task2) {
- boolean[] result = new boolean[1];
- try {
- mMainExecutor.executeBlocking(() -> {
- result[0] = AppPairsController.this.pair(task1, task2);
- });
- } catch (InterruptedException e) {
- Slog.e(TAG, "Failed to pair tasks: " + task1 + ", " + task2);
- }
- return result[0];
- }
-
- @Override
- public boolean pair(ActivityManager.RunningTaskInfo task1,
- ActivityManager.RunningTaskInfo task2) {
- boolean[] result = new boolean[1];
- try {
- mMainExecutor.executeBlocking(() -> {
- result[0] = AppPairsController.this.pair(task1, task2);
- });
- } catch (InterruptedException e) {
- Slog.e(TAG, "Failed to pair tasks: " + task1 + ", " + task2);
- }
- return result[0];
- }
-
- @Override
- public void unpair(int taskId) {
- mMainExecutor.execute(() -> {
- AppPairsController.this.unpair(taskId);
- });
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsPool.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsPool.java
deleted file mode 100644
index 5c6037ea0702..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsPool.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.apppairs;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-/**
- * Class that manager pool of {@link AppPair} objects. Helps reduce the need to call system_server
- * to create a root task for the app-pair when needed since we always have one ready to go.
- */
-class AppPairsPool {
- private static final String TAG = AppPairsPool.class.getSimpleName();
-
- @VisibleForTesting
- final AppPairsController mController;
- // The pool
- private final ArrayList<AppPair> mPool = new ArrayList();
-
- AppPairsPool(AppPairsController controller) {
- mController = controller;
- incrementPool();
- }
-
- AppPair acquire() {
- final AppPair entry = mPool.remove(mPool.size() - 1);
- ProtoLog.v(WM_SHELL_TASK_ORG, "acquire entry.taskId=%s listener=%s size=%d",
- entry.getRootTaskId(), entry, mPool.size());
- if (mPool.size() == 0) {
- incrementPool();
- }
- return entry;
- }
-
- void release(AppPair entry) {
- mPool.add(entry);
- ProtoLog.v(WM_SHELL_TASK_ORG, "release entry.taskId=%s listener=%s size=%d",
- entry.getRootTaskId(), entry, mPool.size());
- }
-
- @VisibleForTesting
- void incrementPool() {
- ProtoLog.v(WM_SHELL_TASK_ORG, "incrementPool size=%d", mPool.size());
- final AppPair entry = new AppPair(mController);
- // TODO: multi-display...
- mController.getTaskOrganizer().createRootTask(
- DEFAULT_DISPLAY, WINDOWING_MODE_FULLSCREEN, entry);
- mPool.add(entry);
- }
-
- @VisibleForTesting
- int poolSize() {
- return mPool.size();
- }
-
- public void dump(@NonNull PrintWriter pw, String prefix) {
- final String innerPrefix = prefix + " ";
- final String childPrefix = innerPrefix + " ";
- pw.println(prefix + this);
- for (int i = mPool.size() - 1; i >= 0; --i) {
- mPool.get(i).dump(pw, childPrefix);
- }
- }
-
- @Override
- public String toString() {
- return TAG + "#" + mPool.size();
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/OWNERS
deleted file mode 100644
index 4d9b520e3f0e..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# WM shell sub-modules apppair owner
-chenghsiuchang@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
index 8c0affb0a432..86f9d5b534f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
@@ -29,6 +29,13 @@ import com.android.wm.shell.common.annotations.ExternalThread;
public interface BackAnimation {
/**
+ * Returns a binder that can be passed to an external process to update back animations.
+ */
+ default IBackAnimation createExternalInterface() {
+ return null;
+ }
+
+ /**
* Called when a {@link MotionEvent} is generated by a back gesture.
*
* @param touchX the X touch position of the {@link MotionEvent}.
@@ -47,13 +54,6 @@ public interface BackAnimation {
void setTriggerBack(boolean triggerBack);
/**
- * Returns a binder that can be passed to an external process to update back animations.
- */
- default IBackAnimation createExternalInterface() {
- return null;
- }
-
- /**
* Sets the threshold values that defining edge swipe behavior.
* @param triggerThreshold the min threshold to trigger back.
* @param progressThreshold the max threshold to keep progressing back animation.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 0cf2b28921e1..d53a98cf5b07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -403,7 +403,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
private boolean shouldDispatchToLauncher(int backType) {
return backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
&& mBackToLauncherCallback != null
- && mEnableAnimations.get();
+ && mEnableAnimations.get()
+ && mBackNavigationInfo != null
+ && mBackNavigationInfo.getDepartingAnimationTarget() != null;
}
private static void dispatchOnBackStarted(IOnBackInvokedCallback callback) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index f427a2c4bc95..d7f1292cb717 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -25,6 +25,7 @@ import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_CONTROLLER;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.wm.shell.bubbles.BubblePositioner.TASKBAR_POSITION_BOTTOM;
@@ -79,18 +80,15 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowManager;
-import android.window.WindowContainerTransaction;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
-import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
@@ -103,6 +101,8 @@ import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
+import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.ShellController;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -121,7 +121,7 @@ import java.util.function.IntConsumer;
*
* The controller manages addition, removal, and visible state of bubbles on screen.
*/
-public class BubbleController {
+public class BubbleController implements ConfigurationChangeListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
@@ -157,6 +157,7 @@ public class BubbleController {
private final DisplayController mDisplayController;
private final TaskViewTransitions mTaskViewTransitions;
private final SyncTransactionQueue mSyncQueue;
+ private final ShellController mShellController;
// Used to post to main UI thread
private final ShellExecutor mMainExecutor;
@@ -224,44 +225,9 @@ public class BubbleController {
/** Drag and drop controller to register listener for onDragStarted. */
private DragAndDropController mDragAndDropController;
- /**
- * Creates an instance of the BubbleController.
- */
- public static BubbleController create(Context context,
- @Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
- FloatingContentCoordinator floatingContentCoordinator,
- @Nullable IStatusBarService statusBarService,
- WindowManager windowManager,
- WindowManagerShellWrapper windowManagerShellWrapper,
- UserManager userManager,
- LauncherApps launcherApps,
- TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger,
- ShellTaskOrganizer organizer,
- DisplayController displayController,
- Optional<OneHandedController> oneHandedOptional,
- DragAndDropController dragAndDropController,
- @ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler,
- @ShellBackgroundThread ShellExecutor bgExecutor,
- TaskViewTransitions taskViewTransitions,
- SyncTransactionQueue syncQueue) {
- BubbleLogger logger = new BubbleLogger(uiEventLogger);
- BubblePositioner positioner = new BubblePositioner(context, windowManager);
- BubbleData data = new BubbleData(context, logger, positioner, mainExecutor);
- return new BubbleController(context, data, synchronizer, floatingContentCoordinator,
- new BubbleDataRepository(context, launcherApps, mainExecutor),
- statusBarService, windowManager, windowManagerShellWrapper, userManager,
- launcherApps, logger, taskStackListener, organizer, positioner, displayController,
- oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor,
- taskViewTransitions, syncQueue);
- }
-
- /**
- * Testing constructor.
- */
- @VisibleForTesting
- protected BubbleController(Context context,
+
+ public BubbleController(Context context,
+ ShellController shellController,
BubbleData data,
@Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator,
@@ -284,6 +250,7 @@ public class BubbleController {
TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
mContext = context;
+ mShellController = shellController;
mLauncherApps = launcherApps;
mBarService = statusBarService == null
? IStatusBarService.Stub.asInterface(
@@ -435,17 +402,13 @@ public class BubbleController {
});
mDisplayController.addDisplayChangingController(
- new DisplayChangeController.OnDisplayChangingListener() {
- @Override
- public void onRotateDisplay(int displayId, int fromRotation, int toRotation,
- WindowContainerTransaction t) {
- // This is triggered right before the rotation is applied
- if (fromRotation != toRotation) {
- if (mStackView != null) {
- // Layout listener set on stackView will update the positioner
- // once the rotation is applied
- mStackView.onOrientationChanged();
- }
+ (displayId, fromRotation, toRotation, newDisplayAreaInfo, t) -> {
+ // This is triggered right before the rotation is applied
+ if (fromRotation != toRotation) {
+ if (mStackView != null) {
+ // Layout listener set on stackView will update the positioner
+ // once the rotation is applied
+ mStackView.onOrientationChanged();
}
}
});
@@ -456,6 +419,8 @@ public class BubbleController {
// Clear out any persisted bubbles on disk that no longer have a valid user.
List<UserInfo> users = mUserManager.getAliveUsers();
mDataRepository.sanitizeBubbles(users);
+
+ mShellController.addConfigurationChangeListener(this);
}
@VisibleForTesting
@@ -840,7 +805,8 @@ public class BubbleController {
mSavedBubbleKeysPerUser.remove(userId);
}
- private void updateForThemeChanges() {
+ @Override
+ public void onThemeChanged() {
if (mStackView != null) {
mStackView.onThemeChanged();
}
@@ -860,7 +826,8 @@ public class BubbleController {
}
}
- private void onConfigChanged(Configuration newConfig) {
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
if (mBubblePositioner != null) {
mBubblePositioner.update();
}
@@ -885,6 +852,19 @@ public class BubbleController {
}
}
+ private void onNotificationPanelExpandedChanged(boolean expanded) {
+ if (DEBUG_BUBBLE_GESTURE) {
+ Log.d(TAG, "onNotificationPanelExpandedChanged: expanded=" + expanded);
+ }
+ if (mStackView != null && mStackView.isExpanded()) {
+ if (expanded) {
+ mStackView.stopMonitoringSwipeUpGesture();
+ } else {
+ mStackView.startMonitoringSwipeUpGesture();
+ }
+ }
+ }
+
private void setSysuiProxy(Bubbles.SysuiProxy proxy) {
mSysuiProxy = proxy;
}
@@ -1342,14 +1322,18 @@ public class BubbleController {
mStackView.setBubbleSuppressed(update.unsuppressedBubble, false);
}
+ boolean collapseStack = update.expandedChanged && !update.expanded;
+
// At this point, the correct bubbles are inflated in the stack.
// Make sure the order in bubble data is reflected in bubble row.
if (update.orderChanged && mStackView != null) {
mDataRepository.addBubbles(mCurrentUserId, update.bubbles);
- mStackView.updateBubbleOrder(update.bubbles);
+ // if the stack is going to be collapsed, do not update pointer position
+ // after reordering
+ mStackView.updateBubbleOrder(update.bubbles, !collapseStack);
}
- if (update.expandedChanged && !update.expanded) {
+ if (collapseStack) {
mStackView.setExpanded(false);
mSysuiProxy.requestNotificationShadeTopUi(false, TAG);
}
@@ -1468,6 +1452,18 @@ public class BubbleController {
}
/**
+ * Check if notification panel is in an expanded state.
+ * Makes a call to System UI process and delivers the result via {@code callback} on the
+ * WM Shell main thread.
+ *
+ * @param callback callback that has the result of notification panel expanded state
+ */
+ public void isNotificationPanelExpanded(Consumer<Boolean> callback) {
+ mSysuiProxy.isNotificationPanelExpand(expanded ->
+ mMainExecutor.execute(() -> callback.accept(expanded)));
+ }
+
+ /**
* Description of current bubble state.
*/
private void dump(PrintWriter pw, String[] args) {
@@ -1546,7 +1542,7 @@ public class BubbleController {
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
mBubblePositioner.setImeVisible(imeVisible, imeHeight);
if (mStackView != null) {
- mStackView.animateForIme(imeVisible);
+ mStackView.setImeVisible(imeVisible);
}
}
}
@@ -1691,13 +1687,6 @@ public class BubbleController {
}
@Override
- public void updateForThemeChanges() {
- mMainExecutor.execute(() -> {
- BubbleController.this.updateForThemeChanges();
- });
- }
-
- @Override
public void expandStackAndSelectBubble(BubbleEntry entry) {
mMainExecutor.execute(() -> {
BubbleController.this.expandStackAndSelectBubble(entry);
@@ -1836,10 +1825,9 @@ public class BubbleController {
}
@Override
- public void onConfigChanged(Configuration newConfig) {
- mMainExecutor.execute(() -> {
- BubbleController.this.onConfigChanged(newConfig);
- });
+ public void onNotificationPanelExpandedChanged(boolean expanded) {
+ mMainExecutor.execute(
+ () -> BubbleController.this.onNotificationPanelExpandedChanged(expanded));
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
index dc2ace949f0c..dce6b56261ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java
@@ -46,6 +46,9 @@ public class BubbleDebugConfig {
static final boolean DEBUG_OVERFLOW = false;
static final boolean DEBUG_USER_EDUCATION = false;
static final boolean DEBUG_POSITIONER = false;
+ public static final boolean DEBUG_COLLAPSE_ANIMATOR = false;
+ static final boolean DEBUG_BUBBLE_GESTURE = false;
+ public static boolean DEBUG_EXPANDED_VIEW_DRAGGING = false;
private static final boolean FORCE_SHOW_USER_EDUCATION = false;
private static final String FORCE_SHOW_USER_EDUCATION_SETTING =
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index b8bf1a8e497e..4f225fff1451 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -43,10 +43,13 @@ import android.graphics.CornerPathEffect;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Picture;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.os.RemoteException;
import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.IntProperty;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
@@ -75,6 +78,62 @@ import java.io.PrintWriter;
public class BubbleExpandedView extends LinearLayout {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
+ /** {@link IntProperty} for updating bottom clip */
+ public static final IntProperty<BubbleExpandedView> BOTTOM_CLIP_PROPERTY =
+ new IntProperty<BubbleExpandedView>("bottomClip") {
+ @Override
+ public void setValue(BubbleExpandedView expandedView, int value) {
+ expandedView.setBottomClip(value);
+ }
+
+ @Override
+ public Integer get(BubbleExpandedView expandedView) {
+ return expandedView.mBottomClip;
+ }
+ };
+
+ /** {@link FloatProperty} for updating taskView or overflow alpha */
+ public static final FloatProperty<BubbleExpandedView> CONTENT_ALPHA =
+ new FloatProperty<BubbleExpandedView>("contentAlpha") {
+ @Override
+ public void setValue(BubbleExpandedView expandedView, float value) {
+ expandedView.setContentAlpha(value);
+ }
+
+ @Override
+ public Float get(BubbleExpandedView expandedView) {
+ return expandedView.getContentAlpha();
+ }
+ };
+
+ /** {@link FloatProperty} for updating background and pointer alpha */
+ public static final FloatProperty<BubbleExpandedView> BACKGROUND_ALPHA =
+ new FloatProperty<BubbleExpandedView>("backgroundAlpha") {
+ @Override
+ public void setValue(BubbleExpandedView expandedView, float value) {
+ expandedView.setBackgroundAlpha(value);
+ }
+
+ @Override
+ public Float get(BubbleExpandedView expandedView) {
+ return expandedView.getAlpha();
+ }
+ };
+
+ /** {@link FloatProperty} for updating manage button alpha */
+ public static final FloatProperty<BubbleExpandedView> MANAGE_BUTTON_ALPHA =
+ new FloatProperty<BubbleExpandedView>("manageButtonAlpha") {
+ @Override
+ public void setValue(BubbleExpandedView expandedView, float value) {
+ expandedView.mManageButton.setAlpha(value);
+ }
+
+ @Override
+ public Float get(BubbleExpandedView expandedView) {
+ return expandedView.mManageButton.getAlpha();
+ }
+ };
+
// The triangle pointing to the expanded view
private View mPointerView;
@Nullable private int[] mExpandedViewContainerLocation;
@@ -90,7 +149,7 @@ public class BubbleExpandedView extends LinearLayout {
/**
* Whether we want the {@code TaskView}'s content to be visible (alpha = 1f). If
- * {@link #mIsAlphaAnimating} is true, this may not reflect the {@code TaskView}'s actual alpha
+ * {@link #mIsAnimating} is true, this may not reflect the {@code TaskView}'s actual alpha
* value until the animation ends.
*/
private boolean mIsContentVisible = false;
@@ -99,12 +158,13 @@ public class BubbleExpandedView extends LinearLayout {
* Whether we're animating the {@code TaskView}'s alpha value. If so, we will hold off on
* applying alpha changes from {@link #setContentVisibility} until the animation ends.
*/
- private boolean mIsAlphaAnimating = false;
+ private boolean mIsAnimating = false;
private int mPointerWidth;
private int mPointerHeight;
private float mPointerRadius;
private float mPointerOverlap;
+ private final PointF mPointerPos = new PointF();
private CornerPathEffect mPointerEffect;
private ShapeDrawable mCurrentPointer;
private ShapeDrawable mTopPointer;
@@ -113,11 +173,13 @@ public class BubbleExpandedView extends LinearLayout {
private float mCornerRadius = 0f;
private int mBackgroundColorFloating;
private boolean mUsingMaxHeight;
-
+ private int mTopClip = 0;
+ private int mBottomClip = 0;
@Nullable private Bubble mBubble;
private PendingIntent mPendingIntent;
// TODO(b/170891664): Don't use a flag, set the BubbleOverflow object instead
private boolean mIsOverflow;
+ private boolean mIsClipping;
private BubbleController mController;
private BubbleStackView mStackView;
@@ -268,7 +330,8 @@ public class BubbleExpandedView extends LinearLayout {
mExpandedViewContainer.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
+ Rect clip = new Rect(0, mTopClip, view.getWidth(), view.getHeight() - mBottomClip);
+ outline.setRoundRect(clip, mCornerRadius);
}
});
mExpandedViewContainer.setClipToOutline(true);
@@ -300,9 +363,9 @@ public class BubbleExpandedView extends LinearLayout {
// they should not collapse the stack (which all other touches on areas around the AV
// would do).
if (motionEvent.getRawY() >= avBounds.top
- && motionEvent.getRawY() <= avBounds.bottom
- && (motionEvent.getRawX() < avBounds.left
- || motionEvent.getRawX() > avBounds.right)) {
+ && motionEvent.getRawY() <= avBounds.bottom
+ && (motionEvent.getRawX() < avBounds.left
+ || motionEvent.getRawX() > avBounds.right)) {
return true;
}
@@ -384,7 +447,7 @@ public class BubbleExpandedView extends LinearLayout {
}
void applyThemeAttrs() {
- final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
+ final TypedArray ta = mContext.obtainStyledAttributes(new int[]{
android.R.attr.dialogCornerRadius,
android.R.attr.colorBackgroundFloating});
boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
@@ -429,7 +492,7 @@ public class BubbleExpandedView extends LinearLayout {
* ordering surfaces during animations. When content is drawn on top of the app (e.g. bubble
* being dragged out, the manage menu) this is set to false, otherwise it should be true.
*/
- void setSurfaceZOrderedOnTop(boolean onTop) {
+ public void setSurfaceZOrderedOnTop(boolean onTop) {
if (mTaskView == null) {
return;
}
@@ -510,12 +573,12 @@ public class BubbleExpandedView extends LinearLayout {
}
/**
- * Whether we are currently animating the {@code TaskView}'s alpha value. If this is set to
+ * Whether we are currently animating the {@code TaskView}. If this is set to
* true, calls to {@link #setContentVisibility} will not be applied until this is set to false
* again.
*/
- void setAlphaAnimating(boolean animating) {
- mIsAlphaAnimating = animating;
+ public void setAnimating(boolean animating) {
+ mIsAnimating = animating;
// If we're done animating, apply the correct
if (!animating) {
@@ -524,18 +587,139 @@ public class BubbleExpandedView extends LinearLayout {
}
/**
- * Sets the alpha of the underlying {@code TaskView}, since changing the expanded view's alpha
- * does not affect the {@code TaskView} since it uses a Surface.
+ * Get alpha from underlying {@code TaskView} if this view is for a bubble.
+ * Or get alpha for the overflow view if this view is for overflow.
+ *
+ * @return alpha for the content being shown
*/
- void setTaskViewAlpha(float alpha) {
+ public float getContentAlpha() {
+ if (mIsOverflow) {
+ return mOverflowView.getAlpha();
+ }
if (mTaskView != null) {
+ return mTaskView.getAlpha();
+ }
+ return 1f;
+ }
+
+ /**
+ * Set alpha of the underlying {@code TaskView} if this view is for a bubble.
+ * Or set alpha for the overflow view if this view is for overflow.
+ *
+ * Changing expanded view's alpha does not affect the {@code TaskView} since it uses a Surface.
+ */
+ public void setContentAlpha(float alpha) {
+ if (mIsOverflow) {
+ mOverflowView.setAlpha(alpha);
+ } else if (mTaskView != null) {
mTaskView.setAlpha(alpha);
}
+ }
+
+ /**
+ * Sets the alpha of the background and the pointer view.
+ */
+ public void setBackgroundAlpha(float alpha) {
mPointerView.setAlpha(alpha);
setAlpha(alpha);
}
/**
+ * Set translation Y for the expanded view content.
+ * Excludes manage button and pointer.
+ */
+ public void setContentTranslationY(float translationY) {
+ mExpandedViewContainer.setTranslationY(translationY);
+
+ // Left or right pointer can become detached when moving the view up
+ if (translationY <= 0 && (isShowingLeftPointer() || isShowingRightPointer())) {
+ // Y coordinate where the pointer would start to get detached from the expanded view.
+ // Takes into account bottom clipping and rounded corners
+ float detachPoint =
+ mExpandedViewContainer.getBottom() - mBottomClip - mCornerRadius + translationY;
+ float pointerBottom = mPointerPos.y + mPointerHeight;
+ // If pointer bottom is past detach point, move it in by that many pixels
+ float horizontalShift = 0;
+ if (pointerBottom > detachPoint) {
+ horizontalShift = pointerBottom - detachPoint;
+ }
+ if (isShowingLeftPointer()) {
+ // Move left pointer right
+ movePointerBy(horizontalShift, 0);
+ } else {
+ // Move right pointer left
+ movePointerBy(-horizontalShift, 0);
+ }
+ // Hide pointer if it is moved by entire width
+ mPointerView.setVisibility(
+ horizontalShift > mPointerWidth ? View.INVISIBLE : View.VISIBLE);
+ }
+ }
+
+ /**
+ * Update alpha value for the manage button
+ */
+ public void setManageButtonAlpha(float alpha) {
+ mManageButton.setAlpha(alpha);
+ }
+
+ /**
+ * Set {@link #setTranslationY(float) translationY} for the manage button
+ */
+ public void setManageButtonTranslationY(float translationY) {
+ mManageButton.setTranslationY(translationY);
+ }
+
+ /**
+ * Set top clipping for the view
+ */
+ public void setTopClip(int clip) {
+ mTopClip = clip;
+ onContainerClipUpdate();
+ }
+
+ /**
+ * Set bottom clipping for the view
+ */
+ public void setBottomClip(int clip) {
+ mBottomClip = clip;
+ onContainerClipUpdate();
+ }
+
+ private void onContainerClipUpdate() {
+ if (mTopClip == 0 && mBottomClip == 0) {
+ if (mIsClipping) {
+ mIsClipping = false;
+ if (mTaskView != null) {
+ mTaskView.setClipBounds(null);
+ mTaskView.setEnableSurfaceClipping(false);
+ }
+ mExpandedViewContainer.invalidateOutline();
+ }
+ } else {
+ if (!mIsClipping) {
+ mIsClipping = true;
+ if (mTaskView != null) {
+ mTaskView.setEnableSurfaceClipping(true);
+ }
+ }
+ mExpandedViewContainer.invalidateOutline();
+ if (mTaskView != null) {
+ mTaskView.setClipBounds(new Rect(0, mTopClip, mTaskView.getWidth(),
+ mTaskView.getHeight() - mBottomClip));
+ }
+ }
+ }
+
+ /**
+ * Move pointer from base position
+ */
+ public void movePointerBy(float x, float y) {
+ mPointerView.setTranslationX(mPointerPos.x + x);
+ mPointerView.setTranslationY(mPointerPos.y + y);
+ }
+
+ /**
* Set visibility of contents in the expanded state.
*
* @param visibility {@code true} if the contents should be visible on the screen.
@@ -543,13 +727,13 @@ public class BubbleExpandedView extends LinearLayout {
* Note that this contents visibility doesn't affect visibility at {@link android.view.View},
* and setting {@code false} actually means rendering the contents in transparent.
*/
- void setContentVisibility(boolean visibility) {
+ public void setContentVisibility(boolean visibility) {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
Log.d(TAG, "setContentVisibility: visibility=" + visibility
+ " bubble=" + getBubbleKey());
}
mIsContentVisible = visibility;
- if (mTaskView != null && !mIsAlphaAnimating) {
+ if (mTaskView != null && !mIsAnimating) {
mTaskView.setAlpha(visibility ? 1f : 0f);
mPointerView.setAlpha(visibility ? 1f : 0f);
}
@@ -560,6 +744,44 @@ public class BubbleExpandedView extends LinearLayout {
return mTaskView;
}
+ @VisibleForTesting
+ public BubbleOverflowContainerView getOverflow() {
+ return mOverflowView;
+ }
+
+
+ /**
+ * Return content height: taskView or overflow.
+ * Takes into account clippings set by {@link #setTopClip(int)} and {@link #setBottomClip(int)}
+ *
+ * @return if bubble is for overflow, return overflow height, otherwise return taskView height
+ */
+ public int getContentHeight() {
+ if (mIsOverflow) {
+ return mOverflowView.getHeight() - mTopClip - mBottomClip;
+ }
+ if (mTaskView != null) {
+ return mTaskView.getHeight() - mTopClip - mBottomClip;
+ }
+ return 0;
+ }
+
+ /**
+ * Return bottom position of the content on screen
+ *
+ * @return if bubble is for overflow, return value for overflow, otherwise taskView
+ */
+ public int getContentBottomOnScreen() {
+ Rect out = new Rect();
+ if (mIsOverflow) {
+ mOverflowView.getBoundsOnScreen(out);
+ }
+ if (mTaskView != null) {
+ mTaskView.getBoundsOnScreen(out);
+ }
+ return out.bottom;
+ }
+
int getTaskId() {
return mTaskId;
}
@@ -687,7 +909,9 @@ public class BubbleExpandedView extends LinearLayout {
mTaskView.onLocationChanged();
}
if (mIsOverflow) {
- mOverflowView.show();
+ post(() -> {
+ mOverflowView.show();
+ });
}
}
@@ -730,38 +954,59 @@ public class BubbleExpandedView extends LinearLayout {
post(() -> {
mCurrentPointer = showVertically ? onLeft ? mLeftPointer : mRightPointer : mTopPointer;
updatePointerView();
- float pointerY;
- float pointerX;
if (showVertically) {
- pointerY = bubbleCenter - (mPointerWidth / 2f);
+ mPointerPos.y = bubbleCenter - (mPointerWidth / 2f);
if (!isRtl) {
- pointerX = onLeft
+ mPointerPos.x = onLeft
? -mPointerHeight + mPointerOverlap
: getWidth() - mPaddingRight - mPointerOverlap;
} else {
- pointerX = onLeft
+ mPointerPos.x = onLeft
? -(getWidth() - mPaddingLeft - mPointerOverlap)
: mPointerHeight - mPointerOverlap;
}
} else {
- pointerY = mPointerOverlap;
+ mPointerPos.y = mPointerOverlap;
if (!isRtl) {
- pointerX = bubbleCenter - (mPointerWidth / 2f);
+ mPointerPos.x = bubbleCenter - (mPointerWidth / 2f);
} else {
- pointerX = -(getWidth() - mPaddingLeft - bubbleCenter) + (mPointerWidth / 2f);
+ mPointerPos.x = -(getWidth() - mPaddingLeft - bubbleCenter)
+ + (mPointerWidth / 2f);
}
}
if (animate) {
- mPointerView.animate().translationX(pointerX).translationY(pointerY).start();
+ mPointerView.animate().translationX(mPointerPos.x).translationY(
+ mPointerPos.y).start();
} else {
- mPointerView.setTranslationY(pointerY);
- mPointerView.setTranslationX(pointerX);
+ mPointerView.setTranslationY(mPointerPos.y);
+ mPointerView.setTranslationX(mPointerPos.x);
mPointerView.setVisibility(VISIBLE);
}
});
}
/**
+ * Return true if pointer is shown on the left
+ */
+ public boolean isShowingLeftPointer() {
+ return mCurrentPointer == mLeftPointer;
+ }
+
+ /**
+ * Return true if pointer is shown on the right
+ */
+ public boolean isShowingRightPointer() {
+ return mCurrentPointer == mRightPointer;
+ }
+
+ /**
+ * Return width of the current pointer
+ */
+ public int getPointerWidth() {
+ return mPointerWidth;
+ }
+
+ /**
* Position of the manage button displayed in the expanded view. Used for placing user
* education about the manage button.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index fcd0ed7308ef..9aa285fff19c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -167,7 +167,10 @@ public class BubbleOverflowContainerView extends LinearLayout {
void updateOverflow() {
Resources res = getResources();
- final int columns = res.getInteger(R.integer.bubbles_overflow_columns);
+ int columns = (int) Math.round(getWidth()
+ / (res.getDimension(R.dimen.bubble_name_width)));
+ columns = columns > 0 ? columns : res.getInteger(R.integer.bubbles_overflow_columns);
+
mRecyclerView.setLayoutManager(
new OverflowGridLayoutManager(getContext(), columns));
if (mRecyclerView.getItemDecorationCount() == 0) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index e9729e45731b..dbad5df9cf56 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -16,6 +16,8 @@
package com.android.wm.shell.bubbles;
+import static android.view.View.LAYOUT_DIRECTION_RTL;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
@@ -28,7 +30,6 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import android.view.Surface;
-import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
@@ -366,6 +367,14 @@ public class BubblePositioner {
return mImeVisible ? mImeHeight : 0;
}
+ /** Return top position of the IME if it's visible */
+ public int getImeTop() {
+ if (mImeVisible) {
+ return getScreenRect().bottom - getImeHeight() - getInsets().bottom;
+ }
+ return 0;
+ }
+
/** Sets whether the IME is visible. **/
public void setImeVisible(boolean visible, int height) {
mImeVisible = visible;
@@ -557,16 +566,30 @@ public class BubblePositioner {
* @return the position of the bubble on-screen when the stack is expanded.
*/
public PointF getExpandedBubbleXY(int index, BubbleStackView.StackViewState state) {
- final float positionInRow = index * (mBubbleSize + mSpacingBetweenBubbles);
+ boolean showBubblesVertically = showBubblesVertically();
+ boolean isRtl = mContext.getResources().getConfiguration().getLayoutDirection()
+ == LAYOUT_DIRECTION_RTL;
+
+ int onScreenIndex;
+ if (showBubblesVertically || !isRtl) {
+ onScreenIndex = index;
+ } else {
+ // If bubbles are shown horizontally, check if RTL language is used.
+ // If RTL is active, position first bubble on the right and last on the left.
+ // Last bubble has screen index 0 and first bubble has max screen index value.
+ onScreenIndex = state.numberOfBubbles - 1 - index;
+ }
+
+ final float positionInRow = onScreenIndex * (mBubbleSize + mSpacingBetweenBubbles);
final float expandedStackSize = getExpandedStackSize(state.numberOfBubbles);
- final float centerPosition = showBubblesVertically()
+ final float centerPosition = showBubblesVertically
? mPositionRect.centerY()
: mPositionRect.centerX();
// alignment - centered on the edge
final float rowStart = centerPosition - (expandedStackSize / 2f);
float x;
float y;
- if (showBubblesVertically()) {
+ if (showBubblesVertically) {
int inset = mExpandedViewLargeScreenInsetClosestEdge;
y = rowStart + positionInRow;
int left = mIsLargeScreen
@@ -583,8 +606,8 @@ public class BubblePositioner {
x = rowStart + positionInRow;
}
- if (showBubblesVertically() && mImeVisible) {
- return new PointF(x, getExpandedBubbleYForIme(index, state));
+ if (showBubblesVertically && mImeVisible) {
+ return new PointF(x, getExpandedBubbleYForIme(onScreenIndex, state));
}
return new PointF(x, y);
}
@@ -693,7 +716,7 @@ public class BubblePositioner {
// Start on the left if we're in LTR, right otherwise.
final boolean startOnLeft =
mContext.getResources().getConfiguration().getLayoutDirection()
- != View.LAYOUT_DIRECTION_RTL;
+ != LAYOUT_DIRECTION_RTL;
final float startingVerticalOffset = mContext.getResources().getDimensionPixelOffset(
R.dimen.bubble_stack_starting_offset_y);
// TODO: placement bug here because mPositionRect doesn't handle the overhanging edge
@@ -749,4 +772,21 @@ public class BubblePositioner {
public void setPinnedLocation(PointF point) {
mPinLocation = point;
}
+
+ /**
+ * Navigation bar has an area where system gestures can be started from.
+ *
+ * @return {@link Rect} for system navigation bar gesture zone
+ */
+ public Rect getNavBarGestureZone() {
+ // Gesture zone height from the bottom
+ int gestureZoneHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_gesture_height);
+ Rect screen = getScreenRect();
+ return new Rect(
+ screen.left,
+ screen.bottom - gestureZoneHeight,
+ screen.right,
+ screen.bottom);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 0a334140d616..2d0be066beb5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -21,6 +21,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -44,6 +45,7 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Log;
import android.view.Choreographer;
@@ -56,6 +58,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
+import android.view.WindowManagerPolicyConstants;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.FrameLayout;
@@ -75,8 +78,12 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.animation.PhysicsAnimator;
+import com.android.wm.shell.bubbles.BubblesNavBarMotionEventHandler.MotionEventListener;
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix;
import com.android.wm.shell.bubbles.animation.ExpandedAnimationController;
+import com.android.wm.shell.bubbles.animation.ExpandedViewAnimationController;
+import com.android.wm.shell.bubbles.animation.ExpandedViewAnimationControllerImpl;
+import com.android.wm.shell.bubbles.animation.ExpandedViewAnimationControllerStub;
import com.android.wm.shell.bubbles.animation.PhysicsAnimationLayout;
import com.android.wm.shell.bubbles.animation.StackAnimationController;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -89,6 +96,7 @@ import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -97,6 +105,12 @@ import java.util.stream.Collectors;
*/
public class BubbleStackView extends FrameLayout
implements ViewTreeObserver.OnComputeInternalInsetsListener {
+ /**
+ * Set to {@code true} to enable home gesture handling in bubbles
+ */
+ public static final boolean HOME_GESTURE_ENABLED =
+ SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", true);
+
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES;
/** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */
@@ -123,6 +137,9 @@ public class BubbleStackView extends FrameLayout
private static final float SCRIM_ALPHA = 0.6f;
+ /** Minimum alpha value for scrim when alpha is being changed via drag */
+ private static final float MIN_SCRIM_ALPHA_FOR_DRAG = 0.2f;
+
/**
* How long to wait to animate the stack temporarily invisible after a drag/flyout hide
* animation ends, if we are in fact temporarily invisible.
@@ -148,7 +165,7 @@ public class BubbleStackView extends FrameLayout
* Handler to use for all delayed animations - this way, we can easily cancel them before
* starting a new animation.
*/
- private final ShellExecutor mDelayedAnimationExecutor;
+ private final ShellExecutor mMainExecutor;
private Runnable mDelayedAnimation;
/**
@@ -197,8 +214,10 @@ public class BubbleStackView extends FrameLayout
private PhysicsAnimationLayout mBubbleContainer;
private StackAnimationController mStackAnimationController;
private ExpandedAnimationController mExpandedAnimationController;
+ private ExpandedViewAnimationController mExpandedViewAnimationController;
private View mScrim;
+ private boolean mScrimAnimating;
private View mManageMenuScrim;
private FrameLayout mExpandedViewContainer;
@@ -276,6 +295,9 @@ public class BubbleStackView extends FrameLayout
*/
private int mPointerIndexDown = -1;
+ @Nullable
+ private BubblesNavBarGestureTracker mBubblesNavBarGestureTracker;
+
/** Description of current animation controller state. */
public void dump(PrintWriter pw, String[] args) {
pw.println("Stack view state:");
@@ -693,6 +715,90 @@ public class BubbleStackView extends FrameLayout
}
};
+ /** Touch listener set on the whole view that forwards event to the swipe up listener. */
+ private final RelativeTouchListener mContainerSwipeListener = new RelativeTouchListener() {
+ @Override
+ public boolean onDown(@NonNull View v, @NonNull MotionEvent ev) {
+ // Pass move event on to swipe listener
+ mSwipeUpListener.onDown(ev.getX(), ev.getY());
+ return true;
+ }
+
+ @Override
+ public void onMove(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
+ float viewInitialY, float dx, float dy) {
+ // Pass move event on to swipe listener
+ mSwipeUpListener.onMove(dx, dy);
+ }
+
+ @Override
+ public void onUp(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
+ float viewInitialY, float dx, float dy, float velX, float velY) {
+ // Pass up even on to swipe listener
+ mSwipeUpListener.onUp(velX, velY);
+ }
+ };
+
+ /** MotionEventListener that listens from home gesture swipe event. */
+ private final MotionEventListener mSwipeUpListener = new MotionEventListener() {
+ @Override
+ public void onDown(float x, float y) {}
+
+ @Override
+ public void onMove(float dx, float dy) {
+ if ((mManageEduView != null && mManageEduView.getVisibility() == VISIBLE)
+ || isStackEduShowing()) {
+ return;
+ }
+
+ if (mShowingManage) {
+ showManageMenu(false /* show */);
+ }
+ // Only allow up, normalize for up direction
+ float collapsed = -Math.min(dy, 0);
+ mExpandedViewAnimationController.updateDrag((int) collapsed);
+
+ // Update scrim
+ if (!mScrimAnimating) {
+ mScrim.setAlpha(getScrimAlphaForDrag(collapsed));
+ }
+ }
+
+ @Override
+ public void onCancel() {
+ mExpandedViewAnimationController.animateBackToExpanded();
+ }
+
+ @Override
+ public void onUp(float velX, float velY) {
+ mExpandedViewAnimationController.setSwipeVelocity(velY);
+ if (mExpandedViewAnimationController.shouldCollapse()) {
+ // Update data first and start the animation when we are processing change
+ mBubbleData.setExpanded(false);
+ } else {
+ mExpandedViewAnimationController.animateBackToExpanded();
+
+ // Update scrim
+ if (!mScrimAnimating) {
+ showScrim(true);
+ }
+ }
+ }
+
+ private float getScrimAlphaForDrag(float dragAmount) {
+ // dragAmount should be negative as we allow scroll up only
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ float alphaRange = SCRIM_ALPHA - MIN_SCRIM_ALPHA_FOR_DRAG;
+
+ int dragMax = mExpandedBubble.getExpandedView().getContentHeight();
+ float dragFraction = dragAmount / dragMax;
+
+ return Math.max(SCRIM_ALPHA - alphaRange * dragFraction, MIN_SCRIM_ALPHA_FOR_DRAG);
+ }
+ return SCRIM_ALPHA;
+ }
+ };
+
/** Click listener set on the flyout, which expands the stack when the flyout is tapped. */
private OnClickListener mFlyoutClickListener = new OnClickListener() {
@Override
@@ -766,7 +872,7 @@ public class BubbleStackView extends FrameLayout
ShellExecutor mainExecutor) {
super(context);
- mDelayedAnimationExecutor = mainExecutor;
+ mMainExecutor = mainExecutor;
mBubbleController = bubbleController;
mBubbleData = data;
@@ -796,6 +902,14 @@ public class BubbleStackView extends FrameLayout
mExpandedAnimationController = new ExpandedAnimationController(mPositioner,
onBubbleAnimatedOut, this);
+
+ if (HOME_GESTURE_ENABLED) {
+ mExpandedViewAnimationController =
+ new ExpandedViewAnimationControllerImpl(context, mPositioner);
+ } else {
+ mExpandedViewAnimationController = new ExpandedViewAnimationControllerStub();
+ }
+
mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
// Force LTR by default since most of the Bubbles UI is positioned manually by the user, or
@@ -971,7 +1085,7 @@ public class BubbleStackView extends FrameLayout
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
// We need to be Z ordered on top in order for alpha animations to work.
mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(true);
- mExpandedBubble.getExpandedView().setAlphaAnimating(true);
+ mExpandedBubble.getExpandedView().setAnimating(true);
}
}
@@ -985,14 +1099,15 @@ public class BubbleStackView extends FrameLayout
// = 0f remains in effect.
&& !mExpandedViewTemporarilyHidden) {
mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
- mExpandedBubble.getExpandedView().setAlphaAnimating(false);
+ mExpandedBubble.getExpandedView().setAnimating(false);
}
}
});
mExpandedViewAlphaAnimator.addUpdateListener(valueAnimator -> {
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().setTaskViewAlpha(
- (float) valueAnimator.getAnimatedValue());
+ float alpha = (float) valueAnimator.getAnimatedValue();
+ mExpandedBubble.getExpandedView().setContentAlpha(alpha);
+ mExpandedBubble.getExpandedView().setBackgroundAlpha(alpha);
}
});
@@ -1708,7 +1823,7 @@ public class BubbleStackView extends FrameLayout
/**
* Update bubble order and pointer position.
*/
- public void updateBubbleOrder(List<Bubble> bubbles) {
+ public void updateBubbleOrder(List<Bubble> bubbles, boolean updatePointerPositoion) {
final Runnable reorder = () -> {
for (int i = 0; i < bubbles.size(); i++) {
Bubble bubble = bubbles.get(i);
@@ -1724,7 +1839,10 @@ public class BubbleStackView extends FrameLayout
.map(b -> b.getIconView()).collect(Collectors.toList());
mStackAnimationController.animateReorder(bubbleViews, reorder);
}
- updatePointerPosition(false /* forIme */);
+
+ if (updatePointerPositoion) {
+ updatePointerPosition(false /* forIme */);
+ }
}
/**
@@ -1795,6 +1913,7 @@ public class BubbleStackView extends FrameLayout
private void showNewlySelectedBubble(BubbleViewProvider bubbleToSelect) {
final BubbleViewProvider previouslySelected = mExpandedBubble;
mExpandedBubble = bubbleToSelect;
+ mExpandedViewAnimationController.setExpandedView(mExpandedBubble.getExpandedView());
if (mIsExpanded) {
hideCurrentInputMethod();
@@ -1843,12 +1962,19 @@ public class BubbleStackView extends FrameLayout
return;
}
+ boolean wasExpanded = mIsExpanded;
+
hideCurrentInputMethod();
mBubbleController.getSysuiProxy().onStackExpandChanged(shouldExpand);
- if (mIsExpanded) {
- animateCollapse();
+ if (wasExpanded) {
+ stopMonitoringSwipeUpGesture();
+ if (HOME_GESTURE_ENABLED) {
+ animateCollapse();
+ } else {
+ animateCollapseWithScale();
+ }
logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
} else {
animateExpansion();
@@ -1856,11 +1982,58 @@ public class BubbleStackView extends FrameLayout
logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
logBubbleEvent(mExpandedBubble,
FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
+ if (HOME_GESTURE_ENABLED) {
+ mBubbleController.isNotificationPanelExpanded(notifPanelExpanded -> {
+ if (!notifPanelExpanded && mIsExpanded) {
+ startMonitoringSwipeUpGesture();
+ }
+ });
+ }
}
notifyExpansionChanged(mExpandedBubble, mIsExpanded);
}
/**
+ * Monitor for swipe up gesture that is used to collapse expanded view
+ */
+ void startMonitoringSwipeUpGesture() {
+ if (DEBUG_BUBBLE_GESTURE) {
+ Log.d(TAG, "startMonitoringSwipeUpGesture");
+ }
+ stopMonitoringSwipeUpGestureInternal();
+
+ if (isGestureNavEnabled()) {
+ mBubblesNavBarGestureTracker = new BubblesNavBarGestureTracker(mContext, mPositioner);
+ mBubblesNavBarGestureTracker.start(mSwipeUpListener);
+ setOnTouchListener(mContainerSwipeListener);
+ }
+ }
+
+ private boolean isGestureNavEnabled() {
+ return mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_navBarInteractionMode)
+ == WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+ }
+
+ /**
+ * Stop monitoring for swipe up gesture
+ */
+ void stopMonitoringSwipeUpGesture() {
+ if (DEBUG_BUBBLE_GESTURE) {
+ Log.d(TAG, "stopMonitoringSwipeUpGesture");
+ }
+ stopMonitoringSwipeUpGestureInternal();
+ }
+
+ private void stopMonitoringSwipeUpGestureInternal() {
+ if (mBubblesNavBarGestureTracker != null) {
+ mBubblesNavBarGestureTracker.stop();
+ mBubblesNavBarGestureTracker = null;
+ setOnTouchListener(null);
+ }
+ }
+
+ /**
* Called when back press occurs while bubbles are expanded.
*/
public void onBackPressed() {
@@ -1982,15 +2155,28 @@ public class BubbleStackView extends FrameLayout
}
private void showScrim(boolean show) {
+ AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mScrimAnimating = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mScrimAnimating = false;
+ }
+ };
if (show) {
mScrim.animate()
.setInterpolator(ALPHA_IN)
.alpha(SCRIM_ALPHA)
+ .setListener(listener)
.start();
} else {
mScrim.animate()
.alpha(0f)
.setInterpolator(ALPHA_OUT)
+ .setListener(listener)
.start();
}
}
@@ -2072,11 +2258,12 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
if (mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().setTaskViewAlpha(0f);
+ mExpandedBubble.getExpandedView().setContentAlpha(0f);
+ mExpandedBubble.getExpandedView().setBackgroundAlpha(0f);
// We'll be starting the alpha animation after a slight delay, so set this flag early
// here.
- mExpandedBubble.getExpandedView().setAlphaAnimating(true);
+ mExpandedBubble.getExpandedView().setAnimating(true);
}
mDelayedAnimation = () -> {
@@ -2114,10 +2301,10 @@ public class BubbleStackView extends FrameLayout
})
.start();
};
- mDelayedAnimationExecutor.executeDelayed(mDelayedAnimation, startDelay);
+ mMainExecutor.executeDelayed(mDelayedAnimation, startDelay);
}
- private void animateCollapse() {
+ private void animateCollapseWithScale() {
cancelDelayedExpandCollapseSwitchAnimations();
if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
@@ -2217,6 +2404,68 @@ public class BubbleStackView extends FrameLayout
.start();
}
+ private void animateCollapse() {
+ cancelDelayedExpandCollapseSwitchAnimations();
+
+ if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
+ mManageEduView.hide();
+ }
+
+ mIsExpanded = false;
+ mIsExpansionAnimating = true;
+
+ showScrim(false);
+
+ mBubbleContainer.cancelAllAnimations();
+
+ // If we were in the middle of swapping, the animating-out surface would have been scaling
+ // to zero - finish it off.
+ PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel();
+ mAnimatingOutSurfaceContainer.setScaleX(0f);
+ mAnimatingOutSurfaceContainer.setScaleY(0f);
+
+ // Let the expanded animation controller know that it shouldn't animate child adds/reorders
+ // since we're about to animate collapsed.
+ mExpandedAnimationController.notifyPreparingToCollapse();
+
+ final Runnable collapseBackToStack = () -> mExpandedAnimationController.collapseBackToStack(
+ mStackAnimationController
+ .getStackPositionAlongNearestHorizontalEdge()
+ /* collapseTo */,
+ () -> mBubbleContainer.setActiveController(mStackAnimationController));
+
+ final Runnable after = () -> {
+ final BubbleViewProvider previouslySelected = mExpandedBubble;
+ // TODO(b/231350255): investigate why this call is needed here
+ beforeExpandedViewAnimation();
+ if (mManageEduView != null) {
+ mManageEduView.hide();
+ }
+
+ if (DEBUG_BUBBLE_STACK_VIEW) {
+ Log.d(TAG, "animateCollapse");
+ Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(),
+ mExpandedBubble));
+ }
+ updateOverflowVisibility();
+ updateZOrder();
+ updateBadges(true /* setBadgeForCollapsedStack */);
+ afterExpandedViewAnimation();
+ if (previouslySelected != null) {
+ previouslySelected.setTaskViewVisibility(false);
+ }
+ mExpandedViewAnimationController.reset();
+ };
+ mExpandedViewAnimationController.animateCollapse(collapseBackToStack, after);
+ if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ // When the animation completes, we should no longer be showing the content.
+ // This won't actually update content visibility immediately, if we are currently
+ // animating. But updates the internal state for the content to be hidden after
+ // animation completes.
+ mExpandedBubble.getExpandedView().setContentVisibility(false);
+ }
+ }
+
private void animateSwitchBubbles() {
// If we're no longer expanded, this is meaningless.
if (!mIsExpanded) {
@@ -2277,7 +2526,7 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
- mDelayedAnimationExecutor.executeDelayed(() -> {
+ mMainExecutor.executeDelayed(() -> {
if (!mIsExpanded) {
mIsBubbleSwitchAnimating = false;
return;
@@ -2308,7 +2557,7 @@ public class BubbleStackView extends FrameLayout
* animating flags for those animations.
*/
private void cancelDelayedExpandCollapseSwitchAnimations() {
- mDelayedAnimationExecutor.removeCallbacks(mDelayedAnimation);
+ mMainExecutor.removeCallbacks(mDelayedAnimation);
mIsExpansionAnimating = false;
mIsBubbleSwitchAnimating = false;
@@ -2332,9 +2581,18 @@ public class BubbleStackView extends FrameLayout
/**
* Updates the stack based for IME changes. When collapsed it'll move the stack if it
* overlaps where they IME would be. When expanded it'll shift the expanded bubbles
- * if they might overlap with the IME (this only happens for large screens).
+ * if they might overlap with the IME (this only happens for large screens)
+ * and clip the expanded view.
*/
- public void animateForIme(boolean visible) {
+ public void setImeVisible(boolean visible) {
+ if (HOME_GESTURE_ENABLED) {
+ setImeVisibleInternal(visible);
+ } else {
+ setImeVisibleWithoutClipping(visible);
+ }
+ }
+
+ private void setImeVisibleWithoutClipping(boolean visible) {
if ((mIsExpansionAnimating || mIsBubbleSwitchAnimating) && mIsExpanded) {
// This will update the animation so the bubbles move to position for the IME
mExpandedAnimationController.expandFromStack(() -> {
@@ -2385,6 +2643,62 @@ public class BubbleStackView extends FrameLayout
}
}
+ private void setImeVisibleInternal(boolean visible) {
+ if ((mIsExpansionAnimating || mIsBubbleSwitchAnimating) && mIsExpanded) {
+ // This will update the animation so the bubbles move to position for the IME
+ mExpandedAnimationController.expandFromStack(() -> {
+ updatePointerPosition(false /* forIme */);
+ afterExpandedViewAnimation();
+ mExpandedViewAnimationController.animateForImeVisibilityChange(visible);
+ } /* after */);
+ return;
+ }
+
+ if (!mIsExpanded && getBubbleCount() > 0) {
+ final float stackDestinationY =
+ mStackAnimationController.animateForImeVisibility(visible);
+
+ // How far the stack is animating due to IME, we'll just animate the flyout by that
+ // much too.
+ final float stackDy =
+ stackDestinationY - mStackAnimationController.getStackPosition().y;
+
+ // If the flyout is visible, translate it along with the bubble stack.
+ if (mFlyout.getVisibility() == VISIBLE) {
+ PhysicsAnimator.getInstance(mFlyout)
+ .spring(DynamicAnimation.TRANSLATION_Y,
+ mFlyout.getTranslationY() + stackDy,
+ FLYOUT_IME_ANIMATION_SPRING_CONFIG)
+ .start();
+ }
+ }
+
+ if (mIsExpanded) {
+ mExpandedViewAnimationController.animateForImeVisibilityChange(visible);
+ if (mPositioner.showBubblesVertically()
+ && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+ float selectedY = mPositioner.getExpandedBubbleXY(getState().selectedIndex,
+ getState()).y;
+ float newExpandedViewTop = mPositioner.getExpandedViewY(mExpandedBubble, selectedY);
+ mExpandedBubble.getExpandedView().setImeVisible(visible);
+ if (!mExpandedBubble.getExpandedView().isUsingMaxHeight()) {
+ mExpandedViewContainer.animate().translationY(newExpandedViewTop);
+ }
+ List<Animator> animList = new ArrayList();
+ for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
+ View child = mBubbleContainer.getChildAt(i);
+ float transY = mPositioner.getExpandedBubbleXY(i, getState()).y;
+ ObjectAnimator anim = ObjectAnimator.ofFloat(child, TRANSLATION_Y, transY);
+ animList.add(anim);
+ }
+ updatePointerPosition(true /* forIme */);
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(animList);
+ set.start();
+ }
+ }
+ }
+
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() != MotionEvent.ACTION_DOWN && ev.getActionIndex() != mPointerIndexDown) {
@@ -2473,8 +2787,10 @@ public class BubbleStackView extends FrameLayout
private void dismissBubbleIfExists(@Nullable BubbleViewProvider bubble) {
if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
- if (mIsExpanded && mBubbleData.getBubbles().size() > 1) {
- // If we have more than 1 bubble we will perform the switch animation
+ if (mIsExpanded && mBubbleData.getBubbles().size() > 1
+ && Objects.equals(bubble, mExpandedBubble)) {
+ // If we have more than 1 bubble and it's the current bubble being dismissed,
+ // we will perform the switch animation
mIsBubbleSwitchAnimating = true;
}
mBubbleData.dismissBubbleWithKey(bubble.getKey(), Bubbles.DISMISS_USER_GESTURE);
@@ -2820,7 +3136,7 @@ public class BubbleStackView extends FrameLayout
&& mExpandedBubble.getExpandedView() != null) {
BubbleExpandedView bev = mExpandedBubble.getExpandedView();
bev.setContentVisibility(false);
- bev.setAlphaAnimating(!mIsExpansionAnimating);
+ bev.setAnimating(!mIsExpansionAnimating);
mExpandedViewContainerMatrix.setScaleX(0f);
mExpandedViewContainerMatrix.setScaleY(0f);
mExpandedViewContainerMatrix.setTranslate(0f, 0f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 8a0db0a12711..f8ccf2364b4c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -107,9 +107,6 @@ public interface Bubbles {
/** Tell the stack of bubbles to collapse. */
void collapseStack();
- /** Tell the controller need update its UI to fit theme. */
- void updateForThemeChanges();
-
/**
* Request the stack expand if needed, then select the specified Bubble as current.
* If no bubble exists for this entry, one is created.
@@ -214,6 +211,11 @@ public interface Bubbles {
int modificationType);
/**
+ * Called when notification panel is expanded or collapsed
+ */
+ void onNotificationPanelExpandedChanged(boolean expanded);
+
+ /**
* Called when the status bar has become visible or invisible (either permanently or
* temporarily).
*/
@@ -250,13 +252,6 @@ public interface Bubbles {
*/
void onUserRemoved(int removedUserId);
- /**
- * Called when config changed.
- *
- * @param newConfig the new config.
- */
- void onConfigChanged(Configuration newConfig);
-
/** Description of current bubble state. */
void dump(PrintWriter pw, String[] args);
@@ -285,7 +280,7 @@ public interface Bubbles {
/** Callback to tell SysUi components execute some methods. */
interface SysuiProxy {
- void isNotificationShadeExpand(Consumer<Boolean> callback);
+ void isNotificationPanelExpand(Consumer<Boolean> callback);
void getPendingOrActiveEntry(String key, Consumer<BubbleEntry> callback);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java
new file mode 100644
index 000000000000..e7beeeb06534
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.bubbles;
+
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.content.Context;
+import android.hardware.input.InputManager;
+import android.util.Log;
+import android.view.Choreographer;
+import android.view.InputChannel;
+import android.view.InputEventReceiver;
+import android.view.InputMonitor;
+
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.bubbles.BubblesNavBarMotionEventHandler.MotionEventListener;
+
+/**
+ * Set up tracking bubbles gestures that begin in navigation bar
+ */
+class BubblesNavBarGestureTracker {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "BubblesGestureTracker" : TAG_BUBBLES;
+
+ private static final String GESTURE_MONITOR = "bubbles-gesture";
+ private final Context mContext;
+ private final BubblePositioner mPositioner;
+
+ @Nullable
+ private InputMonitor mInputMonitor;
+ @Nullable
+ private InputEventReceiver mInputEventReceiver;
+
+ BubblesNavBarGestureTracker(Context context, BubblePositioner positioner) {
+ mContext = context;
+ mPositioner = positioner;
+ }
+
+ /**
+ * Start tracking gestures
+ *
+ * @param listener listener that is notified of touch events
+ */
+ void start(MotionEventListener listener) {
+ if (BubbleDebugConfig.DEBUG_BUBBLE_GESTURE) {
+ Log.d(TAG, "start monitoring bubbles swipe up gesture");
+ }
+
+ stopInternal();
+
+ mInputMonitor = InputManager.getInstance().monitorGestureInput(GESTURE_MONITOR,
+ mContext.getDisplayId());
+ InputChannel inputChannel = mInputMonitor.getInputChannel();
+
+ BubblesNavBarMotionEventHandler motionEventHandler =
+ new BubblesNavBarMotionEventHandler(mContext, mPositioner,
+ this::onInterceptTouch, listener);
+ mInputEventReceiver = new BubblesNavBarInputEventReceiver(inputChannel,
+ Choreographer.getInstance(), motionEventHandler);
+ }
+
+ void stop() {
+ if (BubbleDebugConfig.DEBUG_BUBBLE_GESTURE) {
+ Log.d(TAG, "stop monitoring bubbles swipe up gesture");
+ }
+ stopInternal();
+ }
+
+ private void stopInternal() {
+ if (mInputEventReceiver != null) {
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
+ }
+ if (mInputMonitor != null) {
+ mInputMonitor.dispose();
+ mInputMonitor = null;
+ }
+ }
+
+ private void onInterceptTouch() {
+ if (BubbleDebugConfig.DEBUG_BUBBLE_GESTURE) {
+ Log.d(TAG, "intercept touch event");
+ }
+ if (mInputMonitor != null) {
+ mInputMonitor.pilferPointers();
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarInputEventReceiver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarInputEventReceiver.java
new file mode 100644
index 000000000000..45037b87830f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarInputEventReceiver.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.bubbles;
+
+import android.os.Looper;
+import android.view.BatchedInputEventReceiver;
+import android.view.Choreographer;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+
+/**
+ * Bubbles {@link BatchedInputEventReceiver} for monitoring touches from navbar gesture area
+ */
+class BubblesNavBarInputEventReceiver extends BatchedInputEventReceiver {
+
+ private final BubblesNavBarMotionEventHandler mMotionEventHandler;
+
+ BubblesNavBarInputEventReceiver(InputChannel inputChannel,
+ Choreographer choreographer, BubblesNavBarMotionEventHandler motionEventHandler) {
+ super(inputChannel, Looper.myLooper(), choreographer);
+ mMotionEventHandler = motionEventHandler;
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ boolean handled = false;
+ try {
+ if (!(event instanceof MotionEvent)) {
+ return;
+ }
+ handled = mMotionEventHandler.onMotionEvent((MotionEvent) event);
+ } finally {
+ finishInputEvent(event, handled);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java
new file mode 100644
index 000000000000..844526ca0f35
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.bubbles;
+
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Handles {@link MotionEvent}s for bubbles that begin in the nav bar area
+ */
+class BubblesNavBarMotionEventHandler {
+ private static final String TAG =
+ TAG_WITH_CLASS_NAME ? "BubblesNavBarMotionEventHandler" : TAG_BUBBLES;
+ private static final int VELOCITY_UNITS = 1000;
+
+ private final Runnable mOnInterceptTouch;
+ private final MotionEventListener mMotionEventListener;
+ private final int mTouchSlop;
+ private final BubblePositioner mPositioner;
+ private final PointF mTouchDown = new PointF();
+ private boolean mTrackingTouches;
+ private boolean mInterceptingTouches;
+ @Nullable
+ private VelocityTracker mVelocityTracker;
+
+ BubblesNavBarMotionEventHandler(Context context, BubblePositioner positioner,
+ Runnable onInterceptTouch, MotionEventListener motionEventListener) {
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mPositioner = positioner;
+ mOnInterceptTouch = onInterceptTouch;
+ mMotionEventListener = motionEventListener;
+ }
+
+ /**
+ * Handle {@link MotionEvent} and forward it to {@code motionEventListener} defined in
+ * constructor
+ *
+ * @return {@code true} if this {@link MotionEvent} is handled (it started in the gesture area)
+ */
+ public boolean onMotionEvent(MotionEvent motionEvent) {
+ float dx = motionEvent.getX() - mTouchDown.x;
+ float dy = motionEvent.getY() - mTouchDown.y;
+
+ switch (motionEvent.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ if (isInGestureRegion(motionEvent)) {
+ mTouchDown.set(motionEvent.getX(), motionEvent.getY());
+ mMotionEventListener.onDown(motionEvent.getX(), motionEvent.getY());
+ mTrackingTouches = true;
+ return true;
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mTrackingTouches) {
+ if (!mInterceptingTouches && Math.hypot(dx, dy) > mTouchSlop) {
+ mInterceptingTouches = true;
+ mOnInterceptTouch.run();
+ }
+ if (mInterceptingTouches) {
+ getVelocityTracker().addMovement(motionEvent);
+ mMotionEventListener.onMove(dx, dy);
+ }
+ return true;
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ if (mTrackingTouches) {
+ mMotionEventListener.onCancel();
+ finishTracking();
+ return true;
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mTrackingTouches) {
+ if (mInterceptingTouches) {
+ getVelocityTracker().computeCurrentVelocity(VELOCITY_UNITS);
+ mMotionEventListener.onUp(getVelocityTracker().getXVelocity(),
+ getVelocityTracker().getYVelocity());
+ }
+ finishTracking();
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+
+ private boolean isInGestureRegion(MotionEvent ev) {
+ // Only handles touch events beginning in navigation bar system gesture zone
+ if (mPositioner.getNavBarGestureZone().contains((int) ev.getX(), (int) ev.getY())) {
+ if (DEBUG_BUBBLE_GESTURE) {
+ Log.d(TAG, "handling touch y=" + ev.getY()
+ + " navBarGestureZone=" + mPositioner.getNavBarGestureZone());
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private VelocityTracker getVelocityTracker() {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ return mVelocityTracker;
+ }
+
+ private void finishTracking() {
+ mTouchDown.set(0, 0);
+ mTrackingTouches = false;
+ mInterceptingTouches = false;
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ }
+
+ /**
+ * Callback for receiving {@link MotionEvent} updates
+ */
+ interface MotionEventListener {
+ /**
+ * Touch down action.
+ *
+ * @param x x coordinate
+ * @param y y coordinate
+ */
+ void onDown(float x, float y);
+
+ /**
+ * Move action.
+ * Reports distance from point reported in {@link #onDown(float, float)}
+ *
+ * @param dx distance moved on x-axis from starting point, in pixels
+ * @param dy distance moved on y-axis from starting point, in pixels
+ */
+ void onMove(float dx, float dy);
+
+ /**
+ * Touch up action.
+ *
+ * @param velX velocity of the move action on x axis
+ * @param velY velocity of the move actin on y axis
+ */
+ void onUp(float velX, float velY);
+
+ /**
+ * Motion action was cancelled.
+ */
+ void onCancel();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt
index cf0cefec401a..ea9d065d5f53 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt
@@ -17,8 +17,6 @@
package com.android.wm.shell.bubbles
import android.graphics.PointF
-import android.os.Handler
-import android.os.Looper
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.View
@@ -146,6 +144,12 @@ abstract class RelativeTouchListener : View.OnTouchListener {
velocityTracker.clear()
movedEnough = false
}
+
+ MotionEvent.ACTION_CANCEL -> {
+ v.handler.removeCallbacksAndMessages(null)
+ velocityTracker.clear()
+ movedEnough = false
+ }
}
return true
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index 573f42468512..b521cb6a3d38 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -16,12 +16,16 @@
package com.android.wm.shell.bubbles.animation;
+import static android.view.View.LAYOUT_DIRECTION_RTL;
+
import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
+import static com.android.wm.shell.bubbles.BubbleStackView.HOME_GESTURE_ENABLED;
import android.content.res.Resources;
import android.graphics.Path;
import android.graphics.PointF;
import android.view.View;
+import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -61,7 +65,10 @@ public class ExpandedAnimationController
private static final float DAMPING_RATIO_MEDIUM_LOW_BOUNCY = 0.65f;
/** Stiffness for the expand/collapse path-following animation. */
- private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 1000;
+ private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 400;
+
+ /** Stiffness for the expand/collapse animation when home gesture handling is off */
+ private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS_WITHOUT_HOME_GESTURE = 1000;
/**
* Velocity required to dismiss an individual bubble without dragging it into the dismiss
@@ -73,6 +80,11 @@ public class ExpandedAnimationController
new PhysicsAnimator.SpringConfig(
EXPAND_COLLAPSE_ANIM_STIFFNESS, SpringForce.DAMPING_RATIO_NO_BOUNCY);
+ private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfigWithoutHomeGesture =
+ new PhysicsAnimator.SpringConfig(
+ EXPAND_COLLAPSE_ANIM_STIFFNESS_WITHOUT_HOME_GESTURE,
+ SpringForce.DAMPING_RATIO_NO_BOUNCY);
+
/** Horizontal offset between bubbles, which we need to know to re-stack them. */
private float mStackOffsetPx;
/** Size of each bubble. */
@@ -233,6 +245,11 @@ public class ExpandedAnimationController
};
}
+ boolean showBubblesVertically = mPositioner.showBubblesVertically();
+ final boolean isRtl =
+ mLayout.getContext().getResources().getConfiguration().getLayoutDirection()
+ == LAYOUT_DIRECTION_RTL;
+
// Animate each bubble individually, since each path will end in a different spot.
animationsForChildrenFromIndex(0, (index, animation) -> {
final View bubble = mLayout.getChildAt(index);
@@ -267,9 +284,20 @@ public class ExpandedAnimationController
// right side, the first bubble is traveling to the top left, so it leads. During
// collapse to the left, the first bubble has the shortest travel time back to the stack
// position, so it leads (and vice versa).
- final boolean firstBubbleLeads =
- (expanding && !mLayout.isFirstChildXLeftOfCenter(bubble.getTranslationX()))
+ final boolean firstBubbleLeads;
+ if (showBubblesVertically || !isRtl) {
+ firstBubbleLeads =
+ (expanding && !mLayout.isFirstChildXLeftOfCenter(bubble.getTranslationX()))
|| (!expanding && mLayout.isFirstChildXLeftOfCenter(mCollapsePoint.x));
+ } else {
+ // For RTL languages, when showing bubbles horizontally, it is reversed. The bubbles
+ // are positioned right to left. This means that when expanding from left, the top
+ // bubble will lead as it will be positioned on the right. And when expanding from
+ // right, the top bubble will have the least travel distance.
+ firstBubbleLeads =
+ (expanding && mLayout.isFirstChildXLeftOfCenter(bubble.getTranslationX()))
+ || (!expanding && !mLayout.isFirstChildXLeftOfCenter(mCollapsePoint.x));
+ }
final int startDelay = firstBubbleLeads
? (index * 10)
: ((mLayout.getChildCount() - index) * 10);
@@ -278,11 +306,20 @@ public class ExpandedAnimationController
(firstBubbleLeads && index == 0)
|| (!firstBubbleLeads && index == mLayout.getChildCount() - 1);
+ Interpolator interpolator;
+ if (HOME_GESTURE_ENABLED) {
+ // When home gesture is enabled, we use a different animation timing for collapse
+ interpolator = expanding
+ ? Interpolators.EMPHASIZED_ACCELERATE : Interpolators.EMPHASIZED_DECELERATE;
+ } else {
+ interpolator = Interpolators.LINEAR;
+ }
+
animation
.followAnimatedTargetAlongPath(
path,
EXPAND_COLLAPSE_TARGET_ANIM_DURATION /* targetAnimDuration */,
- Interpolators.LINEAR /* targetAnimInterpolator */,
+ interpolator /* targetAnimInterpolator */,
isLeadBubble ? mLeadBubbleEndAction : null /* endAction */,
() -> mLeadBubbleEndAction = null /* endAction */)
.withStartDelay(startDelay)
@@ -525,10 +562,16 @@ public class ExpandedAnimationController
finishRemoval.run();
mOnBubbleAnimatedOutAction.run();
} else {
+ PhysicsAnimator.SpringConfig springConfig;
+ if (HOME_GESTURE_ENABLED) {
+ springConfig = mAnimateOutSpringConfig;
+ } else {
+ springConfig = mAnimateOutSpringConfigWithoutHomeGesture;
+ }
PhysicsAnimator.getInstance(child)
.spring(DynamicAnimation.ALPHA, 0f)
- .spring(DynamicAnimation.SCALE_X, 0f, mAnimateOutSpringConfig)
- .spring(DynamicAnimation.SCALE_Y, 0f, mAnimateOutSpringConfig)
+ .spring(DynamicAnimation.SCALE_X, 0f, springConfig)
+ .spring(DynamicAnimation.SCALE_Y, 0f, springConfig)
.withEndActions(finishRemoval, mOnBubbleAnimatedOutAction)
.start();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationController.java
new file mode 100644
index 000000000000..8a33780bc8d5
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationController.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.bubbles.animation;
+
+import com.android.wm.shell.bubbles.BubbleExpandedView;
+
+/**
+ * Animation controller for bubble expanded view collapsing
+ */
+public interface ExpandedViewAnimationController {
+ /**
+ * Set expanded view that this controller is working with.
+ */
+ void setExpandedView(BubbleExpandedView expandedView);
+
+ /**
+ * Set current collapse value, in pixels.
+ *
+ * @param distance pixels that user dragged the view by
+ */
+ void updateDrag(float distance);
+
+ /**
+ * Set current swipe velocity.
+ * Velocity is directional:
+ * <ul>
+ * <li>velocity < 0 means swipe direction is up</li>
+ * <li>velocity > 0 means swipe direction is down</li>
+ * </ul>
+ */
+ void setSwipeVelocity(float velocity);
+
+ /**
+ * Check if view is dragged past collapse threshold or swipe up velocity exceeds min velocity
+ * required to collapse the view
+ */
+ boolean shouldCollapse();
+
+ /**
+ * Animate view to collapsed state
+ *
+ * @param startStackCollapse runnable that is triggered when bubbles can start moving back to
+ * their collapsed location
+ * @param after runnable to run after animation is complete
+ */
+ void animateCollapse(Runnable startStackCollapse, Runnable after);
+
+ /**
+ * Animate the view back to fully expanded state.
+ */
+ void animateBackToExpanded();
+
+ /**
+ * Animate view for IME visibility change
+ */
+ void animateForImeVisibilityChange(boolean visible);
+
+ /**
+ * Reset the view to fully expanded state
+ */
+ void reset();
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java
new file mode 100644
index 000000000000..845dca34b41f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.bubbles.animation;
+
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_COLLAPSE_ANIMATOR;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_EXPANDED_VIEW_DRAGGING;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.bubbles.BubbleExpandedView.BACKGROUND_ALPHA;
+import static com.android.wm.shell.bubbles.BubbleExpandedView.BOTTOM_CLIP_PROPERTY;
+import static com.android.wm.shell.bubbles.BubbleExpandedView.CONTENT_ALPHA;
+import static com.android.wm.shell.bubbles.BubbleExpandedView.MANAGE_BUTTON_ALPHA;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.ViewConfiguration;
+
+import androidx.annotation.Nullable;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
+import com.android.wm.shell.animation.FlingAnimationUtils;
+import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.bubbles.BubbleExpandedView;
+import com.android.wm.shell.bubbles.BubblePositioner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of {@link ExpandedViewAnimationController} that uses a collapse animation to
+ * hide the {@link BubbleExpandedView}
+ */
+public class ExpandedViewAnimationControllerImpl implements ExpandedViewAnimationController {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "ExpandedViewAnimCtrl" : TAG_BUBBLES;
+
+ private static final float COLLAPSE_THRESHOLD = 0.02f;
+
+ private static final int COLLAPSE_DURATION_MS = 250;
+
+ private static final int MANAGE_BUTTON_ANIM_DURATION_MS = 78;
+
+ private static final int CONTENT_OPACITY_ANIM_DELAY_MS = 93;
+ private static final int CONTENT_OPACITY_ANIM_DURATION_MS = 78;
+
+ private static final int BACKGROUND_OPACITY_ANIM_DELAY_MS = 172;
+ private static final int BACKGROUND_OPACITY_ANIM_DURATION_MS = 78;
+
+ /** Animation fraction threshold for content alpha animation when stack collapse should begin */
+ private static final float STACK_COLLAPSE_THRESHOLD = 0.5f;
+
+ private static final FloatPropertyCompat<ExpandedViewAnimationControllerImpl>
+ COLLAPSE_HEIGHT_PROPERTY =
+ new FloatPropertyCompat<ExpandedViewAnimationControllerImpl>("CollapseSpring") {
+ @Override
+ public float getValue(ExpandedViewAnimationControllerImpl controller) {
+ return controller.getCollapsedAmount();
+ }
+
+ @Override
+ public void setValue(ExpandedViewAnimationControllerImpl controller,
+ float value) {
+ controller.setCollapsedAmount(value);
+ }
+ };
+
+ private final int mMinFlingVelocity;
+ private float mSwipeUpVelocity;
+ private float mSwipeDownVelocity;
+ private final BubblePositioner mPositioner;
+ private final FlingAnimationUtils mFlingAnimationUtils;
+ private int mDraggedAmount;
+ private float mCollapsedAmount;
+ @Nullable
+ private BubbleExpandedView mExpandedView;
+ @Nullable
+ private AnimatorSet mCollapseAnimation;
+ private boolean mNotifiedAboutThreshold;
+ private SpringAnimation mBackToExpandedAnimation;
+ @Nullable
+ private ObjectAnimator mBottomClipAnim;
+
+ public ExpandedViewAnimationControllerImpl(Context context, BubblePositioner positioner) {
+ mFlingAnimationUtils = new FlingAnimationUtils(context.getResources().getDisplayMetrics(),
+ COLLAPSE_DURATION_MS / 1000f);
+ mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
+ mPositioner = positioner;
+ }
+
+ private static void adjustAnimatorSetDuration(AnimatorSet animatorSet,
+ float durationAdjustment) {
+ for (Animator animator : animatorSet.getChildAnimations()) {
+ animator.setStartDelay((long) (animator.getStartDelay() * durationAdjustment));
+ animator.setDuration((long) (animator.getDuration() * durationAdjustment));
+ }
+ }
+
+ @Override
+ public void setExpandedView(BubbleExpandedView expandedView) {
+ if (mExpandedView != null) {
+ if (DEBUG_COLLAPSE_ANIMATOR) {
+ Log.d(TAG, "updating expandedView, resetting previous");
+ }
+ if (mCollapseAnimation != null) {
+ mCollapseAnimation.cancel();
+ }
+ if (mBackToExpandedAnimation != null) {
+ mBackToExpandedAnimation.cancel();
+ }
+ reset();
+ }
+ mExpandedView = expandedView;
+ }
+
+ @Override
+ public void updateDrag(float distance) {
+ if (mExpandedView != null) {
+ mDraggedAmount = OverScroll.dampedScroll(distance, mExpandedView.getContentHeight());
+
+ if (DEBUG_COLLAPSE_ANIMATOR && DEBUG_EXPANDED_VIEW_DRAGGING) {
+ Log.d(TAG, "updateDrag: distance=" + distance + " dragged=" + mDraggedAmount);
+ }
+
+ setCollapsedAmount(mDraggedAmount);
+
+ if (!mNotifiedAboutThreshold && isPastCollapseThreshold()) {
+ mNotifiedAboutThreshold = true;
+ if (DEBUG_COLLAPSE_ANIMATOR) {
+ Log.d(TAG, "notifying over collapse threshold");
+ }
+ vibrateIfEnabled();
+ }
+ }
+ }
+
+ @Override
+ public void setSwipeVelocity(float velocity) {
+ if (velocity < 0) {
+ mSwipeUpVelocity = Math.abs(velocity);
+ mSwipeDownVelocity = 0;
+ } else {
+ mSwipeUpVelocity = 0;
+ mSwipeDownVelocity = velocity;
+ }
+ }
+
+ @Override
+ public boolean shouldCollapse() {
+ if (mSwipeDownVelocity > mMinFlingVelocity) {
+ // Swipe velocity is positive and over fling velocity.
+ // This is a swipe down, always reset to expanded state, regardless of dragged amount.
+ if (DEBUG_COLLAPSE_ANIMATOR) {
+ Log.d(TAG,
+ "not collapsing expanded view, swipe down velocity: " + mSwipeDownVelocity
+ + " minV: " + mMinFlingVelocity);
+ }
+ return false;
+ }
+
+ if (mSwipeUpVelocity > mMinFlingVelocity) {
+ // Swiping up and over fling velocity, collapse the view.
+ if (DEBUG_COLLAPSE_ANIMATOR) {
+ Log.d(TAG,
+ "collapse expanded view, swipe up velocity: " + mSwipeUpVelocity + " minV: "
+ + mMinFlingVelocity);
+ }
+ return true;
+ }
+
+ if (isPastCollapseThreshold()) {
+ if (DEBUG_COLLAPSE_ANIMATOR) {
+ Log.d(TAG, "collapse expanded view, past threshold, dragged: " + mDraggedAmount);
+ }
+ return true;
+ }
+
+ if (DEBUG_COLLAPSE_ANIMATOR) {
+ Log.d(TAG, "not collapsing expanded view");
+ }
+
+ return false;
+ }
+
+ @Override
+ public void animateCollapse(Runnable startStackCollapse, Runnable after) {
+ if (DEBUG_COLLAPSE_ANIMATOR) {
+ Log.d(TAG,
+ "expandedView animate collapse swipeVel=" + mSwipeUpVelocity + " minFlingVel="
+ + mMinFlingVelocity);
+ }
+ if (mExpandedView != null) {
+ // Mark it as animating immediately to avoid updates to the view before animation starts
+ mExpandedView.setAnimating(true);
+
+ if (mCollapseAnimation != null) {
+ mCollapseAnimation.cancel();
+ }
+ mCollapseAnimation = createCollapseAnimation(mExpandedView, startStackCollapse, after);
+
+ if (mSwipeUpVelocity >= mMinFlingVelocity) {
+ int contentHeight = mExpandedView.getContentHeight();
+
+ // Use a temp animator to get adjusted duration value for swipe.
+ // This new value will be used to adjust animation times proportionally in the
+ // animator set. If we adjust animator set duration directly, all child animations
+ // will get the same animation time.
+ ValueAnimator tempAnimator = new ValueAnimator();
+ mFlingAnimationUtils.applyDismissing(tempAnimator, mCollapsedAmount, contentHeight,
+ mSwipeUpVelocity, (contentHeight - mCollapsedAmount));
+
+ float durationAdjustment =
+ (float) tempAnimator.getDuration() / COLLAPSE_DURATION_MS;
+
+ adjustAnimatorSetDuration(mCollapseAnimation, durationAdjustment);
+ mCollapseAnimation.setInterpolator(tempAnimator.getInterpolator());
+ }
+ mCollapseAnimation.start();
+ }
+ }
+
+ @Override
+ public void animateBackToExpanded() {
+ if (DEBUG_COLLAPSE_ANIMATOR) {
+ Log.d(TAG, "expandedView animate back to expanded");
+ }
+ BubbleExpandedView expandedView = mExpandedView;
+ if (expandedView == null) {
+ return;
+ }
+
+ expandedView.setAnimating(true);
+
+ mBackToExpandedAnimation = new SpringAnimation(this, COLLAPSE_HEIGHT_PROPERTY);
+ mBackToExpandedAnimation.setSpring(new SpringForce()
+ .setStiffness(SpringForce.STIFFNESS_LOW)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+ );
+ mBackToExpandedAnimation.addEndListener(new OneTimeEndListener() {
+ @Override
+ public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value,
+ float velocity) {
+ super.onAnimationEnd(animation, canceled, value, velocity);
+ mNotifiedAboutThreshold = false;
+ mBackToExpandedAnimation = null;
+ expandedView.setAnimating(false);
+ }
+ });
+ mBackToExpandedAnimation.setStartValue(mCollapsedAmount);
+ mBackToExpandedAnimation.animateToFinalPosition(0);
+ }
+
+ @Override
+ public void animateForImeVisibilityChange(boolean visible) {
+ if (mExpandedView != null) {
+ if (mBottomClipAnim != null) {
+ mBottomClipAnim.cancel();
+ }
+ int clip = 0;
+ if (visible) {
+ // Clip the expanded view at the top of the IME view
+ clip = mExpandedView.getContentBottomOnScreen() - mPositioner.getImeTop();
+ // Don't allow negative clip value
+ clip = Math.max(clip, 0);
+ }
+ mBottomClipAnim = ObjectAnimator.ofInt(mExpandedView, BOTTOM_CLIP_PROPERTY, clip);
+ mBottomClipAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBottomClipAnim = null;
+ }
+ });
+ mBottomClipAnim.start();
+ }
+ }
+
+ @Override
+ public void reset() {
+ if (DEBUG_COLLAPSE_ANIMATOR) {
+ Log.d(TAG, "reset expandedView collapsed state");
+ }
+ if (mExpandedView == null) {
+ return;
+ }
+ mExpandedView.setAnimating(false);
+
+ if (mCollapseAnimation != null) {
+ mCollapseAnimation.cancel();
+ }
+ if (mBackToExpandedAnimation != null) {
+ mBackToExpandedAnimation.cancel();
+ }
+ mExpandedView.setContentAlpha(1);
+ mExpandedView.setBackgroundAlpha(1);
+ mExpandedView.setManageButtonAlpha(1);
+ setCollapsedAmount(0);
+ mExpandedView.setBottomClip(0);
+ mExpandedView.movePointerBy(0, 0);
+ mCollapsedAmount = 0;
+ mDraggedAmount = 0;
+ mSwipeUpVelocity = 0;
+ mSwipeDownVelocity = 0;
+ mNotifiedAboutThreshold = false;
+ }
+
+ private float getCollapsedAmount() {
+ return mCollapsedAmount;
+ }
+
+ private void setCollapsedAmount(float collapsed) {
+ if (mCollapsedAmount != collapsed) {
+ float previous = mCollapsedAmount;
+ mCollapsedAmount = collapsed;
+
+ if (mExpandedView != null) {
+ if (previous == 0) {
+ // View was not collapsed before. Apply z order change
+ mExpandedView.setSurfaceZOrderedOnTop(true);
+ mExpandedView.setAnimating(true);
+ }
+
+ mExpandedView.setTopClip((int) mCollapsedAmount);
+ // Move up with translationY. Use negative collapsed value
+ mExpandedView.setContentTranslationY(-mCollapsedAmount);
+ mExpandedView.setManageButtonTranslationY(-mCollapsedAmount);
+
+ if (mCollapsedAmount == 0) {
+ // View is no longer collapsed. Revert z order change
+ mExpandedView.setSurfaceZOrderedOnTop(false);
+ mExpandedView.setAnimating(false);
+ }
+ }
+ }
+ }
+
+ private boolean isPastCollapseThreshold() {
+ if (mExpandedView != null) {
+ return mDraggedAmount > mExpandedView.getContentHeight() * COLLAPSE_THRESHOLD;
+ }
+ return false;
+ }
+
+ private AnimatorSet createCollapseAnimation(BubbleExpandedView expandedView,
+ Runnable startStackCollapse, Runnable after) {
+ List<Animator> animatorList = new ArrayList<>();
+ animatorList.add(createHeightAnimation(expandedView));
+ animatorList.add(createManageButtonAnimation());
+ ObjectAnimator contentAlphaAnimation = createContentAlphaAnimation();
+ final boolean[] notified = {false};
+ contentAlphaAnimation.addUpdateListener(animation -> {
+ if (!notified[0] && animation.getAnimatedFraction() > STACK_COLLAPSE_THRESHOLD) {
+ notified[0] = true;
+ // Notify bubbles that they can start moving back to the collapsed position
+ startStackCollapse.run();
+ }
+ });
+ animatorList.add(contentAlphaAnimation);
+ animatorList.add(createBackgroundAlphaAnimation());
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ after.run();
+ }
+ });
+ animatorSet.playTogether(animatorList);
+ return animatorSet;
+ }
+
+ private ValueAnimator createHeightAnimation(BubbleExpandedView expandedView) {
+ ValueAnimator animator = ValueAnimator.ofInt((int) mCollapsedAmount,
+ expandedView.getContentHeight());
+ animator.setInterpolator(Interpolators.EMPHASIZED_ACCELERATE);
+ animator.setDuration(COLLAPSE_DURATION_MS);
+ animator.addUpdateListener(anim -> setCollapsedAmount((int) anim.getAnimatedValue()));
+ return animator;
+ }
+
+ private ObjectAnimator createManageButtonAnimation() {
+ ObjectAnimator animator = ObjectAnimator.ofFloat(mExpandedView, MANAGE_BUTTON_ALPHA, 0f);
+ animator.setDuration(MANAGE_BUTTON_ANIM_DURATION_MS);
+ animator.setInterpolator(Interpolators.LINEAR);
+ return animator;
+ }
+
+ private ObjectAnimator createContentAlphaAnimation() {
+ ObjectAnimator animator = ObjectAnimator.ofFloat(mExpandedView, CONTENT_ALPHA, 0f);
+ animator.setDuration(CONTENT_OPACITY_ANIM_DURATION_MS);
+ animator.setInterpolator(Interpolators.LINEAR);
+ animator.setStartDelay(CONTENT_OPACITY_ANIM_DELAY_MS);
+ return animator;
+ }
+
+ private ObjectAnimator createBackgroundAlphaAnimation() {
+ ObjectAnimator animator = ObjectAnimator.ofFloat(mExpandedView, BACKGROUND_ALPHA, 0f);
+ animator.setDuration(BACKGROUND_OPACITY_ANIM_DURATION_MS);
+ animator.setInterpolator(Interpolators.LINEAR);
+ animator.setStartDelay(BACKGROUND_OPACITY_ANIM_DELAY_MS);
+ return animator;
+ }
+
+ @SuppressLint("MissingPermission")
+ private void vibrateIfEnabled() {
+ if (mExpandedView != null) {
+ mExpandedView.performHapticFeedback(HapticFeedbackConstants.DRAG_CROSSING);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerStub.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerStub.java
new file mode 100644
index 000000000000..bb8a3aaaf551
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerStub.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.bubbles.animation;
+
+import com.android.wm.shell.bubbles.BubbleExpandedView;
+
+/**
+ * Stub implementation {@link ExpandedViewAnimationController} that does not animate the
+ * {@link BubbleExpandedView}
+ */
+public class ExpandedViewAnimationControllerStub implements ExpandedViewAnimationController {
+ @Override
+ public void setExpandedView(BubbleExpandedView expandedView) {
+ }
+
+ @Override
+ public void updateDrag(float distance) {
+ }
+
+ @Override
+ public void setSwipeVelocity(float velocity) {
+ }
+
+ @Override
+ public boolean shouldCollapse() {
+ return false;
+ }
+
+ @Override
+ public void animateCollapse(Runnable startStackCollapse, Runnable after) {
+ }
+
+ @Override
+ public void animateBackToExpanded() {
+ }
+
+ @Override
+ public void animateForImeVisibilityChange(boolean visible) {
+ }
+
+ @Override
+ public void reset() {
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/OverScroll.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/OverScroll.java
new file mode 100644
index 000000000000..d4e76ed0282e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/OverScroll.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.bubbles.animation;
+
+/**
+ * Utility methods for overscroll damping and related effect.
+ *
+ * Copied from packages/apps/Launcher3/src/com/android/launcher3/touch/OverScroll.java
+ */
+public class OverScroll {
+
+ private static final float OVERSCROLL_DAMP_FACTOR = 0.07f;
+
+ /**
+ * This curve determines how the effect of scrolling over the limits of the page diminishes
+ * as the user pulls further and further from the bounds
+ *
+ * @param f The percentage of how much the user has overscrolled.
+ * @return A transformed percentage based on the influence curve.
+ */
+ private static float overScrollInfluenceCurve(float f) {
+ f -= 1.0f;
+ return f * f * f + 1.0f;
+ }
+
+ /**
+ * @param amount The original amount overscrolled.
+ * @param max The maximum amount that the View can overscroll.
+ * @return The dampened overscroll amount.
+ */
+ public static int dampedScroll(float amount, int max) {
+ if (Float.compare(amount, 0) == 0) return 0;
+
+ float f = amount / max;
+ f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+
+ // Clamp this factor, f, to -1 < f < 1
+ if (Math.abs(f) >= 1) {
+ f /= Math.abs(f);
+ }
+
+ return Math.round(OVERSCROLL_DAMP_FACTOR * f * max);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index c32733d4f73c..28c7367662a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -16,11 +16,13 @@
package com.android.wm.shell.common;
+import android.annotation.Nullable;
import android.os.RemoteException;
import android.util.Slog;
-import android.view.IDisplayWindowRotationCallback;
-import android.view.IDisplayWindowRotationController;
+import android.view.IDisplayChangeWindowCallback;
+import android.view.IDisplayChangeWindowController;
import android.view.IWindowManager;
+import android.window.DisplayAreaInfo;
import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
@@ -40,17 +42,17 @@ public class DisplayChangeController {
private final ShellExecutor mMainExecutor;
private final IWindowManager mWmService;
- private final IDisplayWindowRotationController mControllerImpl;
+ private final IDisplayChangeWindowController mControllerImpl;
- private final CopyOnWriteArrayList<OnDisplayChangingListener> mRotationListener =
+ private final CopyOnWriteArrayList<OnDisplayChangingListener> mDisplayChangeListener =
new CopyOnWriteArrayList<>();
public DisplayChangeController(IWindowManager wmService, ShellExecutor mainExecutor) {
mMainExecutor = mainExecutor;
mWmService = wmService;
- mControllerImpl = new DisplayWindowRotationControllerImpl();
+ mControllerImpl = new DisplayChangeWindowControllerImpl();
try {
- mWmService.setDisplayWindowRotationController(mControllerImpl);
+ mWmService.setDisplayChangeWindowController(mControllerImpl);
} catch (RemoteException e) {
throw new RuntimeException("Unable to register rotation controller");
}
@@ -59,63 +61,64 @@ public class DisplayChangeController {
/**
* Adds a display rotation controller.
*/
- public void addRotationListener(OnDisplayChangingListener listener) {
- mRotationListener.add(listener);
+ public void addDisplayChangeListener(OnDisplayChangingListener listener) {
+ mDisplayChangeListener.add(listener);
}
/**
* Removes a display rotation controller.
*/
- public void removeRotationListener(OnDisplayChangingListener listener) {
- mRotationListener.remove(listener);
+ public void removeDisplayChangeListener(OnDisplayChangingListener listener) {
+ mDisplayChangeListener.remove(listener);
}
- /** Query all listeners for changes that should happen on rotation. */
- public void dispatchOnRotateDisplay(WindowContainerTransaction outWct, int displayId,
- final int fromRotation, final int toRotation) {
- for (OnDisplayChangingListener c : mRotationListener) {
- c.onRotateDisplay(displayId, fromRotation, toRotation, outWct);
+ /** Query all listeners for changes that should happen on display change. */
+ public void dispatchOnDisplayChange(WindowContainerTransaction outWct, int displayId,
+ int fromRotation, int toRotation, DisplayAreaInfo newDisplayAreaInfo) {
+ for (OnDisplayChangingListener c : mDisplayChangeListener) {
+ c.onDisplayChange(displayId, fromRotation, toRotation, newDisplayAreaInfo, outWct);
}
}
- private void onRotateDisplay(int displayId, final int fromRotation, final int toRotation,
- IDisplayWindowRotationCallback callback) {
+ private void onDisplayChange(int displayId, int fromRotation, int toRotation,
+ DisplayAreaInfo newDisplayAreaInfo, IDisplayChangeWindowCallback callback) {
WindowContainerTransaction t = new WindowContainerTransaction();
- dispatchOnRotateDisplay(t, displayId, fromRotation, toRotation);
+ dispatchOnDisplayChange(t, displayId, fromRotation, toRotation, newDisplayAreaInfo);
try {
- callback.continueRotateDisplay(toRotation, t);
+ callback.continueDisplayChange(t);
} catch (RemoteException e) {
- Slog.e(TAG, "Failed to continue rotation", e);
+ Slog.e(TAG, "Failed to continue handling display change", e);
}
}
@BinderThread
- private class DisplayWindowRotationControllerImpl
- extends IDisplayWindowRotationController.Stub {
+ private class DisplayChangeWindowControllerImpl
+ extends IDisplayChangeWindowController.Stub {
@Override
- public void onRotateDisplay(int displayId, final int fromRotation,
- final int toRotation, IDisplayWindowRotationCallback callback) {
- mMainExecutor.execute(() -> {
- DisplayChangeController.this.onRotateDisplay(displayId, fromRotation, toRotation,
- callback);
- });
+ public void onDisplayChange(int displayId, int fromRotation, int toRotation,
+ DisplayAreaInfo newDisplayAreaInfo, IDisplayChangeWindowCallback callback) {
+ mMainExecutor.execute(() -> DisplayChangeController.this
+ .onDisplayChange(displayId, fromRotation, toRotation,
+ newDisplayAreaInfo, callback));
}
}
/**
* Give a listener a chance to queue up configuration changes to execute as part of a
- * display rotation. The contents of {@link #onRotateDisplay} must run synchronously.
+ * display rotation. The contents of {@link #onDisplayChange} must run synchronously.
*/
@ShellMainThread
public interface OnDisplayChangingListener {
/**
- * Called before the display is rotated. Contents of this method must run synchronously.
- * @param displayId Id of display that is rotating.
- * @param fromRotation starting rotation of the display.
- * @param toRotation target rotation of the display (after rotating).
+ * Called before the display size has changed.
+ * Contents of this method must run synchronously.
+ * @param displayId display id of the display that is under the change
+ * @param fromRotation rotation before the change
+ * @param toRotation rotation after the change
+ * @param newDisplayAreaInfo display area info after applying the update
* @param t A task transaction to populate.
*/
- void onRotateDisplay(int displayId, int fromRotation, int toRotation,
- WindowContainerTransaction t);
+ void onDisplayChange(int displayId, int fromRotation, int toRotation,
+ @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction t);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 4ba32e93fb3d..764936cceb01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -156,14 +156,14 @@ public class DisplayController {
* Adds a display rotation controller.
*/
public void addDisplayChangingController(OnDisplayChangingListener controller) {
- mChangeController.addRotationListener(controller);
+ mChangeController.addDisplayChangeListener(controller);
}
/**
* Removes a display rotation controller.
*/
public void removeDisplayChangingController(OnDisplayChangingListener controller) {
- mChangeController.removeRotationListener(controller);
+ mChangeController.removeDisplayChangeListener(controller);
}
private void onDisplayAdded(int displayId) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 6a2acf438302..b3f62477077c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -20,6 +20,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
@@ -324,7 +325,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
@Override
- public void topFocusedWindowChanged(String packageName,
+ public void topFocusedWindowChanged(ComponentName component,
InsetsVisibilities requestedVisibilities) {
// Do nothing
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
index b6705446674a..f546f110e87c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.common;
+import android.content.ComponentName;
import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;
@@ -171,14 +172,14 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan
}
}
- private void topFocusedWindowChanged(String packageName,
+ private void topFocusedWindowChanged(ComponentName component,
InsetsVisibilities requestedVisibilities) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
return;
}
for (OnInsetsChangedListener listener : listeners) {
- listener.topFocusedWindowChanged(packageName, requestedVisibilities);
+ listener.topFocusedWindowChanged(component, requestedVisibilities);
}
}
@@ -186,10 +187,10 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan
private class DisplayWindowInsetsControllerImpl
extends IDisplayWindowInsetsController.Stub {
@Override
- public void topFocusedWindowChanged(String packageName,
+ public void topFocusedWindowChanged(ComponentName component,
InsetsVisibilities requestedVisibilities) throws RemoteException {
mMainExecutor.execute(() -> {
- PerDisplay.this.topFocusedWindowChanged(packageName, requestedVisibilities);
+ PerDisplay.this.topFocusedWindowChanged(component, requestedVisibilities);
});
}
@@ -234,10 +235,10 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan
/**
* Called when top focused window changes to determine whether or not to take over insets
* control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false.
- * @param packageName The name of the package that is open in the top focussed window.
+ * @param component The application component that is open in the top focussed window.
* @param requestedVisibilities The insets visibilities requested by the focussed window.
*/
- default void topFocusedWindowChanged(String packageName,
+ default void topFocusedWindowChanged(ComponentName component,
InsetsVisibilities requestedVisibilities) {}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java
index fd3aa05cfc06..ec344d345139 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/InteractionJankMonitorUtils.java
@@ -18,7 +18,9 @@ package com.android.wm.shell.common;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.text.TextUtils;
+import android.view.SurfaceControl;
import android.view.View;
import com.android.internal.jank.InteractionJankMonitor;
@@ -44,6 +46,24 @@ public class InteractionJankMonitorUtils {
}
/**
+ * Begin a trace session.
+ *
+ * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param context the context
+ * @param surface the surface to trace
+ * @param tag the tag to distinguish different flow of same type CUJ.
+ */
+ public static void beginTracing(@InteractionJankMonitor.CujType int cujType,
+ @NonNull Context context, @NonNull SurfaceControl surface, @Nullable String tag) {
+ final InteractionJankMonitor.Configuration.Builder builder =
+ InteractionJankMonitor.Configuration.Builder.withSurface(cujType, context, surface);
+ if (!TextUtils.isEmpty(tag)) {
+ builder.setTag(tag);
+ }
+ InteractionJankMonitor.getInstance().begin(builder);
+ }
+
+ /**
* End a trace session.
*
* @param cujType the specific {@link InteractionJankMonitor.CujType}.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 6305959bb6ac..8bc16bcc9d9d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -206,12 +206,12 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
mSplitLayout = layout;
mSplitWindowManager = splitWindowManager;
mViewHost = viewHost;
- mDividerBounds.set(layout.getDividerBounds());
+ layout.getDividerBounds(mDividerBounds);
onInsetsChanged(insetsState, false /* animate */);
}
void onInsetsChanged(InsetsState insetsState, boolean animate) {
- mTempRect.set(mSplitLayout.getDividerBounds());
+ mSplitLayout.getDividerBounds(mTempRect);
final InsetsSource taskBarInsetsSource =
insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
// Only insets the divider bar with task bar when it's expanded so that the rounded corners
@@ -286,6 +286,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
setTouching();
mStartPos = touchPos;
mMoving = false;
+ mSplitLayout.onStartDragging();
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.addMovement(event);
@@ -301,7 +302,10 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
releaseTouching();
- if (!mMoving) break;
+ if (!mMoving) {
+ mSplitLayout.onDraggingCancelled();
+ break;
+ }
mVelocityTracker.addMovement(event);
mVelocityTracker.computeCurrentVelocity(1000 /* units */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 484294ab295b..74f8bf9ac863 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -50,12 +50,15 @@ import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.R;
import com.android.wm.shell.common.SurfaceUtils;
+import java.util.function.Consumer;
+
/**
* Handles split decor like showing resizing hint for a specific split.
*/
public class SplitDecorManager extends WindowlessWindowManager {
private static final String TAG = SplitDecorManager.class.getSimpleName();
private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground";
+ private static final String GAP_BACKGROUND_SURFACE_NAME = "GapBackground";
private static final long FADE_DURATION = 133;
private final IconProvider mIconProvider;
@@ -67,6 +70,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
private SurfaceControl mHostLeash;
private SurfaceControl mIconLeash;
private SurfaceControl mBackgroundLeash;
+ private SurfaceControl mGapBackgroundLeash;
private boolean mShown;
private boolean mIsResizing;
@@ -141,6 +145,10 @@ public class SplitDecorManager extends WindowlessWindowManager {
t.remove(mBackgroundLeash);
mBackgroundLeash = null;
}
+ if (mGapBackgroundLeash != null) {
+ t.remove(mGapBackgroundLeash);
+ mGapBackgroundLeash = null;
+ }
mHostLeash = null;
mIcon = null;
mResizingIconView = null;
@@ -150,7 +158,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
/** Showing resizing hint. */
public void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds,
- SurfaceControl.Transaction t) {
+ Rect sideBounds, SurfaceControl.Transaction t) {
if (mResizingIconView == null) {
return;
}
@@ -176,6 +184,19 @@ public class SplitDecorManager extends WindowlessWindowManager {
.setLayer(mBackgroundLeash, Integer.MAX_VALUE - 1);
}
+ if (mGapBackgroundLeash == null) {
+ final boolean isLandscape = newBounds.height() == sideBounds.height();
+ final int left = isLandscape ? mBounds.width() : 0;
+ final int top = isLandscape ? 0 : mBounds.height();
+ mGapBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
+ GAP_BACKGROUND_SURFACE_NAME, mSurfaceSession);
+ // Fill up another side bounds area.
+ t.setColor(mGapBackgroundLeash, getResizingBackgroundColor(resizingTask))
+ .setLayer(mGapBackgroundLeash, Integer.MAX_VALUE - 2)
+ .setPosition(mGapBackgroundLeash, left, top)
+ .setWindowCrop(mGapBackgroundLeash, sideBounds.width(), sideBounds.height());
+ }
+
if (mIcon == null && resizingTask.topActivityInfo != null) {
mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
mResizingIconView.setImageDrawable(mIcon);
@@ -193,7 +214,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
newBounds.height() / 2 - mIconSize / 2);
if (animate) {
- startFadeAnimation(show, false /* isResized */);
+ startFadeAnimation(show, null /* finishedConsumer */);
mShown = show;
}
}
@@ -224,14 +245,29 @@ public class SplitDecorManager extends WindowlessWindowManager {
mFadeAnimator.cancel();
}
if (mShown) {
- startFadeAnimation(false /* show */, true /* isResized */);
+ fadeOutDecor(null /* finishedCallback */);
} else {
// Decor surface is hidden so release it directly.
releaseDecor(t);
}
}
- private void startFadeAnimation(boolean show, boolean isResized) {
+ /** Fade-out decor surface with animation end callback, if decor is hidden, run the callback
+ * directly. */
+ public void fadeOutDecor(Runnable finishedCallback) {
+ if (mShown) {
+ startFadeAnimation(false /* show */, transaction -> {
+ releaseDecor(transaction);
+ if (finishedCallback != null) finishedCallback.run();
+ });
+ mShown = false;
+ } else {
+ if (finishedCallback != null) finishedCallback.run();
+ }
+ }
+
+ private void startFadeAnimation(boolean show,
+ Consumer<SurfaceControl.Transaction> finishedConsumer) {
final SurfaceControl.Transaction animT = new SurfaceControl.Transaction();
mFadeAnimator = ValueAnimator.ofFloat(0f, 1f);
mFadeAnimator.setDuration(FADE_DURATION);
@@ -249,7 +285,9 @@ public class SplitDecorManager extends WindowlessWindowManager {
@Override
public void onAnimationStart(@NonNull Animator animation) {
if (show) {
- animT.show(mBackgroundLeash).show(mIconLeash).apply();
+ animT.show(mBackgroundLeash).show(mIconLeash).show(mGapBackgroundLeash).apply();
+ } else {
+ animT.hide(mGapBackgroundLeash).apply();
}
}
@@ -263,8 +301,8 @@ public class SplitDecorManager extends WindowlessWindowManager {
animT.hide(mIconLeash);
}
}
- if (isResized) {
- releaseDecor(animT);
+ if (finishedConsumer != null) {
+ finishedConsumer.accept(animT);
}
animT.apply();
animT.close();
@@ -280,6 +318,11 @@ public class SplitDecorManager extends WindowlessWindowManager {
mBackgroundLeash = null;
}
+ if (mGapBackgroundLeash != null) {
+ t.remove(mGapBackgroundLeash);
+ mGapBackgroundLeash = null;
+ }
+
if (mIcon != null) {
mResizingIconView.setVisibility(View.GONE);
mResizingIconView.setImageDrawable(null);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index bae23575297f..40cf9a32d7a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -24,6 +24,7 @@ import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE;
import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END;
import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START;
import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR;
@@ -31,9 +32,11 @@ import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -55,7 +58,6 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DockedDividerUtils;
import com.android.wm.shell.R;
@@ -78,6 +80,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
public static final int PARALLAX_DISMISSING = 1;
public static final int PARALLAX_ALIGN_CENTER = 2;
+ private static final int FLING_ANIMATION_DURATION = 250;
+
private final int mDividerWindowWidth;
private final int mDividerInsets;
private final int mDividerSize;
@@ -85,7 +89,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private final Rect mTempRect = new Rect();
private final Rect mRootBounds = new Rect();
private final Rect mDividerBounds = new Rect();
+ // Bounds1 final position should be always at top or left
private final Rect mBounds1 = new Rect();
+ // Bounds2 final position should be always at bottom or right
private final Rect mBounds2 = new Rect();
private final Rect mWinBounds1 = new Rect();
private final Rect mWinBounds2 = new Rect();
@@ -178,6 +184,11 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
return outBounds;
}
+ /** Gets root bounds of the whole split layout */
+ public Rect getRootBounds() {
+ return new Rect(mRootBounds);
+ }
+
/** Gets bounds of divider window with screen based coordinate. */
public Rect getDividerBounds() {
return new Rect(mDividerBounds);
@@ -190,6 +201,44 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
return outBounds;
}
+ /** Gets bounds of the primary split with screen based coordinate on the param Rect. */
+ public void getBounds1(Rect rect) {
+ rect.set(mBounds1);
+ }
+
+ /** Gets bounds of the primary split with parent based coordinate on the param Rect. */
+ public void getRefBounds1(Rect rect) {
+ getBounds1(rect);
+ rect.offset(-mRootBounds.left, -mRootBounds.top);
+ }
+
+ /** Gets bounds of the secondary split with screen based coordinate on the param Rect. */
+ public void getBounds2(Rect rect) {
+ rect.set(mBounds2);
+ }
+
+ /** Gets bounds of the secondary split with parent based coordinate on the param Rect. */
+ public void getRefBounds2(Rect rect) {
+ getBounds2(rect);
+ rect.offset(-mRootBounds.left, -mRootBounds.top);
+ }
+
+ /** Gets root bounds of the whole split layout on the param Rect. */
+ public void getRootBounds(Rect rect) {
+ rect.set(mRootBounds);
+ }
+
+ /** Gets bounds of divider window with screen based coordinate on the param Rect. */
+ public void getDividerBounds(Rect rect) {
+ rect.set(mDividerBounds);
+ }
+
+ /** Gets bounds of divider window with parent based coordinate on the param Rect. */
+ public void getRefDividerBounds(Rect rect) {
+ getDividerBounds(rect);
+ rect.offset(-mRootBounds.left, -mRootBounds.top);
+ }
+
/** Returns leash of the current divider bar. */
@Nullable
public SurfaceControl getDividerLeash() {
@@ -270,28 +319,35 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
updateBounds(mDividePosition);
}
- /** Updates recording bounds of divider window and both of the splits. */
private void updateBounds(int position) {
- mDividerBounds.set(mRootBounds);
- mBounds1.set(mRootBounds);
- mBounds2.set(mRootBounds);
+ updateBounds(position, mBounds1, mBounds2, mDividerBounds, true /* setEffectBounds */);
+ }
+
+ /** Updates recording bounds of divider window and both of the splits. */
+ private void updateBounds(int position, Rect bounds1, Rect bounds2, Rect dividerBounds,
+ boolean setEffectBounds) {
+ dividerBounds.set(mRootBounds);
+ bounds1.set(mRootBounds);
+ bounds2.set(mRootBounds);
final boolean isLandscape = isLandscape(mRootBounds);
if (isLandscape) {
position += mRootBounds.left;
- mDividerBounds.left = position - mDividerInsets;
- mDividerBounds.right = mDividerBounds.left + mDividerWindowWidth;
- mBounds1.right = position;
- mBounds2.left = mBounds1.right + mDividerSize;
+ dividerBounds.left = position - mDividerInsets;
+ dividerBounds.right = dividerBounds.left + mDividerWindowWidth;
+ bounds1.right = position;
+ bounds2.left = bounds1.right + mDividerSize;
} else {
position += mRootBounds.top;
- mDividerBounds.top = position - mDividerInsets;
- mDividerBounds.bottom = mDividerBounds.top + mDividerWindowWidth;
- mBounds1.bottom = position;
- mBounds2.top = mBounds1.bottom + mDividerSize;
+ dividerBounds.top = position - mDividerInsets;
+ dividerBounds.bottom = dividerBounds.top + mDividerWindowWidth;
+ bounds1.bottom = position;
+ bounds2.top = bounds1.bottom + mDividerSize;
+ }
+ DockedDividerUtils.sanitizeStackBounds(bounds1, true /** topLeft */);
+ DockedDividerUtils.sanitizeStackBounds(bounds2, false /** topLeft */);
+ if (setEffectBounds) {
+ mSurfaceEffectPolicy.applyDividerPosition(position, isLandscape);
}
- DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */);
- DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */);
- mSurfaceEffectPolicy.applyDividerPosition(position, isLandscape);
}
/** Inflates {@link DividerView} on the root surface. */
@@ -394,11 +450,13 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
switch (snapTarget.flag) {
case FLAG_DISMISS_START:
flingDividePosition(currentPosition, snapTarget.position,
- () -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */));
+ () -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */,
+ EXIT_REASON_DRAG_DIVIDER));
break;
case FLAG_DISMISS_END:
flingDividePosition(currentPosition, snapTarget.position,
- () -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */));
+ () -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */,
+ EXIT_REASON_DRAG_DIVIDER));
break;
default:
flingDividePosition(currentPosition, snapTarget.position,
@@ -407,6 +465,15 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
}
+ void onStartDragging() {
+ InteractionJankMonitorUtils.beginTracing(CUJ_SPLIT_SCREEN_RESIZE, mContext,
+ getDividerLeash(), null /* tag */);
+ }
+
+ void onDraggingCancelled() {
+ InteractionJankMonitorUtils.cancelTracing(CUJ_SPLIT_SCREEN_RESIZE);
+ }
+
void onDoubleTappedDivider() {
mSplitLayoutHandler.onDoubleTappedDivider();
}
@@ -423,28 +490,48 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds,
@Nullable Rect stableInsets) {
final boolean isLandscape = isLandscape(rootBounds);
+ final Rect insets = stableInsets != null ? stableInsets : getDisplayInsets(context);
+
+ // Make split axis insets value same as the larger one to avoid bounds1 and bounds2
+ // have difference after split switching for solving issues on non-resizable app case.
+ if (isLandscape) {
+ final int largerInsets = Math.max(insets.left, insets.right);
+ insets.set(largerInsets, insets.top, largerInsets, insets.bottom);
+ } else {
+ final int largerInsets = Math.max(insets.top, insets.bottom);
+ insets.set(insets.left, largerInsets, insets.right, largerInsets);
+ }
+
return new DividerSnapAlgorithm(
context.getResources(),
rootBounds.width(),
rootBounds.height(),
mDividerSize,
!isLandscape,
- stableInsets != null ? stableInsets : getDisplayInsets(context),
+ insets,
isLandscape ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
}
+ /** Fling divider from current position to end or start position then exit */
+ public void flingDividerToDismiss(boolean toEnd, int reason) {
+ final int target = toEnd ? mDividerSnapAlgorithm.getDismissEndTarget().position
+ : mDividerSnapAlgorithm.getDismissStartTarget().position;
+ flingDividePosition(getDividePosition(), target,
+ () -> mSplitLayoutHandler.onSnappedToDismiss(toEnd, reason));
+ }
+
@VisibleForTesting
void flingDividePosition(int from, int to, @Nullable Runnable flingFinishedCallback) {
if (from == to) {
// No animation run, still callback to stop resizing.
mSplitLayoutHandler.onLayoutSizeChanged(this);
+ InteractionJankMonitorUtils.endTracing(
+ CUJ_SPLIT_SCREEN_RESIZE);
return;
}
- InteractionJankMonitorUtils.beginTracing(InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE,
- mSplitWindowManager.getDividerView(), "Divider fling");
ValueAnimator animator = ValueAnimator
.ofInt(from, to)
- .setDuration(250);
+ .setDuration(FLING_ANIMATION_DURATION);
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
animator.addUpdateListener(
animation -> updateDivideBounds((int) animation.getAnimatedValue()));
@@ -455,7 +542,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
flingFinishedCallback.run();
}
InteractionJankMonitorUtils.endTracing(
- InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE);
+ CUJ_SPLIT_SCREEN_RESIZE);
}
@Override
@@ -466,6 +553,86 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
animator.start();
}
+ /** Swich both surface position with animation. */
+ public void splitSwitching(SurfaceControl.Transaction t, SurfaceControl leash1,
+ SurfaceControl leash2, Runnable finishCallback) {
+ final boolean isLandscape = isLandscape();
+ final Rect insets = getDisplayInsets(mContext);
+ insets.set(isLandscape ? insets.left : 0, isLandscape ? 0 : insets.top,
+ isLandscape ? insets.right : 0, isLandscape ? 0 : insets.bottom);
+
+ final int dividerPos = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
+ isLandscape ? mBounds2.width() : mBounds2.height()).position;
+ final Rect distBounds1 = new Rect();
+ final Rect distBounds2 = new Rect();
+ final Rect distDividerBounds = new Rect();
+ // Compute dist bounds.
+ updateBounds(dividerPos, distBounds2, distBounds1, distDividerBounds,
+ false /* setEffectBounds */);
+ // Offset to real position under root container.
+ distBounds1.offset(-mRootBounds.left, -mRootBounds.top);
+ distBounds2.offset(-mRootBounds.left, -mRootBounds.top);
+ distDividerBounds.offset(-mRootBounds.left, -mRootBounds.top);
+ // DO NOT move to insets area for smooth animation.
+ distBounds1.set(distBounds1.left, distBounds1.top,
+ distBounds1.right - insets.right, distBounds1.bottom - insets.bottom);
+ distBounds2.set(distBounds2.left + insets.left, distBounds2.top + insets.top,
+ distBounds2.right, distBounds2.bottom);
+
+ ValueAnimator animator1 = moveSurface(t, leash1, getRefBounds1(), distBounds1,
+ false /* alignStart */);
+ ValueAnimator animator2 = moveSurface(t, leash2, getRefBounds2(), distBounds2,
+ true /* alignStart */);
+ ValueAnimator animator3 = moveSurface(t, getDividerLeash(), getRefDividerBounds(),
+ distDividerBounds, true /* alignStart */);
+
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(animator1, animator2, animator3);
+ set.setDuration(FLING_ANIMATION_DURATION);
+ set.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mDividePosition = dividerPos;
+ updateBounds(mDividePosition);
+ finishCallback.run();
+ }
+ });
+ set.start();
+ }
+
+ private ValueAnimator moveSurface(SurfaceControl.Transaction t, SurfaceControl leash,
+ Rect start, Rect end, boolean alignStart) {
+ Rect tempStart = new Rect(start);
+ Rect tempEnd = new Rect(end);
+ final float diffX = tempEnd.left - tempStart.left;
+ final float diffY = tempEnd.top - tempStart.top;
+ final float diffWidth = tempEnd.width() - tempStart.width();
+ final float diffHeight = tempEnd.height() - tempStart.height();
+ ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ animator.addUpdateListener(animation -> {
+ if (leash == null) return;
+
+ final float scale = (float) animation.getAnimatedValue();
+ final float distX = tempStart.left + scale * diffX;
+ final float distY = tempStart.top + scale * diffY;
+ final int width = (int) (tempStart.width() + scale * diffWidth);
+ final int height = (int) (tempStart.height() + scale * diffHeight);
+ if (alignStart) {
+ t.setPosition(leash, distX, distY);
+ t.setWindowCrop(leash, width, height);
+ } else {
+ final int offsetX = width - tempStart.width();
+ final int offsetY = height - tempStart.height();
+ t.setPosition(leash, distX + offsetX, distY + offsetY);
+ mTempRect.set(0, 0, width, height);
+ mTempRect.offsetTo(-offsetX, -offsetY);
+ t.setCrop(leash, mTempRect);
+ }
+ t.apply();
+ });
+ return animator;
+ }
+
private static Rect getDisplayInsets(Context context) {
return context.getSystemService(WindowManager.class)
.getMaximumWindowMetrics()
@@ -504,15 +671,15 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
boolean applyResizingOffset) {
final SurfaceControl dividerLeash = getDividerLeash();
if (dividerLeash != null) {
- mTempRect.set(getRefDividerBounds());
+ getRefDividerBounds(mTempRect);
t.setPosition(dividerLeash, mTempRect.left, mTempRect.top);
// Resets layer of divider bar to make sure it is always on top.
t.setLayer(dividerLeash, Integer.MAX_VALUE);
}
- mTempRect.set(getRefBounds1());
+ getRefBounds1(mTempRect);
t.setPosition(leash1, mTempRect.left, mTempRect.top)
.setWindowCrop(leash1, mTempRect.width(), mTempRect.height());
- mTempRect.set(getRefBounds2());
+ getRefBounds2(mTempRect);
t.setPosition(leash2, mTempRect.left, mTempRect.top)
.setWindowCrop(leash2, mTempRect.width(), mTempRect.height());
@@ -560,31 +727,23 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
ActivityManager.RunningTaskInfo taskInfo1, ActivityManager.RunningTaskInfo taskInfo2) {
if (offsetX == 0 && offsetY == 0) {
wct.setBounds(taskInfo1.token, mBounds1);
- wct.setAppBounds(taskInfo1.token, null);
wct.setScreenSizeDp(taskInfo1.token,
SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
wct.setBounds(taskInfo2.token, mBounds2);
- wct.setAppBounds(taskInfo2.token, null);
wct.setScreenSizeDp(taskInfo2.token,
SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
} else {
- mTempRect.set(taskInfo1.configuration.windowConfiguration.getBounds());
+ getBounds1(mTempRect);
mTempRect.offset(offsetX, offsetY);
wct.setBounds(taskInfo1.token, mTempRect);
- mTempRect.set(taskInfo1.configuration.windowConfiguration.getAppBounds());
- mTempRect.offset(offsetX, offsetY);
- wct.setAppBounds(taskInfo1.token, mTempRect);
wct.setScreenSizeDp(taskInfo1.token,
taskInfo1.configuration.screenWidthDp,
taskInfo1.configuration.screenHeightDp);
- mTempRect.set(taskInfo2.configuration.windowConfiguration.getBounds());
+ getBounds2(mTempRect);
mTempRect.offset(offsetX, offsetY);
wct.setBounds(taskInfo2.token, mTempRect);
- mTempRect.set(taskInfo2.configuration.windowConfiguration.getAppBounds());
- mTempRect.offset(offsetX, offsetY);
- wct.setAppBounds(taskInfo2.token, mTempRect);
wct.setScreenSizeDp(taskInfo2.token,
taskInfo2.configuration.screenWidthDp,
taskInfo2.configuration.screenHeightDp);
@@ -602,7 +761,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
public interface SplitLayoutHandler {
/** Calls when dismissing split. */
- void onSnappedToDismiss(boolean snappedToEnd);
+ void onSnappedToDismiss(boolean snappedToEnd, int reason);
/**
* Calls when resizing the split bounds.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
index 9b614875119b..afc706ee9c8e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
@@ -15,6 +15,11 @@
*/
package com.android.wm.shell.common.split;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
import android.annotation.IntDef;
/** Helper utility class of methods and constants that are available to be imported in Launcher. */
@@ -44,4 +49,10 @@ public class SplitScreenConstants {
})
public @interface SplitPosition {
}
+
+ public static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD};
+ public static final int[] CONTROLLED_WINDOWING_MODES =
+ {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
+ public static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE =
+ {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW};
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 99b32a677abe..db8d9d423aca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -39,9 +39,10 @@ import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListen
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
+import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.transition.Transitions;
import java.lang.ref.WeakReference;
@@ -58,7 +59,7 @@ import dagger.Lazy;
* activities are in compatibility mode.
*/
public class CompatUIController implements OnDisplaysChangedListener,
- DisplayImeController.ImePositionProcessor {
+ DisplayImeController.ImePositionProcessor, KeyguardChangeListener {
/** Callback for compat UI interaction. */
public interface CompatUICallback {
@@ -100,13 +101,13 @@ public class CompatUIController implements OnDisplaysChangedListener,
private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0);
private final Context mContext;
+ private final ShellController mShellController;
private final DisplayController mDisplayController;
private final DisplayInsetsController mDisplayInsetsController;
private final DisplayImeController mImeController;
private final SyncTransactionQueue mSyncQueue;
private final ShellExecutor mMainExecutor;
private final Lazy<Transitions> mTransitionsLazy;
- private final CompatUIImpl mImpl = new CompatUIImpl();
private CompatUICallback mCallback;
@@ -118,6 +119,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
private boolean mKeyguardShowing;
public CompatUIController(Context context,
+ ShellController shellController,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
DisplayImeController imeController,
@@ -125,6 +127,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
ShellExecutor mainExecutor,
Lazy<Transitions> transitionsLazy) {
mContext = context;
+ mShellController = shellController;
mDisplayController = displayController;
mDisplayInsetsController = displayInsetsController;
mImeController = imeController;
@@ -134,11 +137,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
mDisplayController.addDisplayWindowListener(this);
mImeController.addPositionProcessor(this);
mCompatUIHintsState = new CompatUIHintsState();
- }
-
- /** Returns implementation of {@link CompatUI}. */
- public CompatUI asCompatUI() {
- return mImpl;
+ shellController.addKeyguardChangeListener(this);
}
/** Sets the callback for UI interactions. */
@@ -223,9 +222,10 @@ public class CompatUIController implements OnDisplaysChangedListener,
layout -> layout.updateVisibility(showOnDisplay(displayId)));
}
- @VisibleForTesting
- void onKeyguardShowingChanged(boolean showing) {
- mKeyguardShowing = showing;
+ @Override
+ public void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
+ boolean animatingDismiss) {
+ mKeyguardShowing = visible;
// Hide the compat UIs when keyguard is showing.
forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId())));
}
@@ -373,19 +373,6 @@ public class CompatUIController implements OnDisplaysChangedListener,
}
}
- /**
- * The interface for calls from outside the Shell, within the host process.
- */
- @ExternalThread
- private class CompatUIImpl implements CompatUI {
- @Override
- public void onKeyguardShowingChanged(boolean showing) {
- mMainExecutor.execute(() -> {
- CompatUIController.this.onKeyguardShowingChanged(showing);
- });
- }
- }
-
/** An implementation of {@link OnInsetsChangedListener} for a given display id. */
private class PerDisplayOnInsetsChangedListener implements OnInsetsChangedListener {
final int mDisplayId;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt
deleted file mode 100644
index 1cd69edf7cd2..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-The dagger modules in this directory can be included by the host SysUI using the Shell library for
-explicity injection of Shell components. Apps using this library are not required to use these
-dagger modules for setup, but it is recommended for them to include them as needed.
-
-The modules are currently inherited as such:
-
-+- WMShellBaseModule (common shell features across SysUI)
- |
- +- WMShellModule (handheld)
- |
- +- TvPipModule (tv pip)
- |
- +- TvWMShellModule (tv) \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 1ea5e21a2c1e..81904e291ad1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -48,6 +48,7 @@ import com.android.wm.shell.pip.tv.TvPipNotificationController;
import com.android.wm.shell.pip.tv.TvPipTaskOrganizer;
import com.android.wm.shell.pip.tv.TvPipTransition;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -64,6 +65,7 @@ public abstract class TvPipModule {
@Provides
static Optional<Pip> providePip(
Context context,
+ ShellController shellController,
TvPipBoundsState tvPipBoundsState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
@@ -81,6 +83,7 @@ public abstract class TvPipModule {
return Optional.of(
TvPipController.create(
context,
+ shellController,
tvPipBoundsState,
tvPipBoundsAlgorithm,
tvPipBoundsController,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index db6131a17114..f85f9d63a827 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -29,17 +29,14 @@ import com.android.internal.logging.UiEventLogger;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootDisplayAreaOrganizer;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
-import com.android.wm.shell.ShellCommandHandler;
-import com.android.wm.shell.ShellCommandHandlerImpl;
-import com.android.wm.shell.ShellInit;
-import com.android.wm.shell.ShellInitImpl;
+import com.android.wm.shell.sysui.ShellCommandHandler;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.TaskViewFactoryController;
import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
-import com.android.wm.shell.apppairs.AppPairs;
-import com.android.wm.shell.apppairs.AppPairsController;
+import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.back.BackAnimationController;
import com.android.wm.shell.bubbles.BubbleController;
@@ -58,20 +55,14 @@ import com.android.wm.shell.common.annotations.ShellAnimationThread;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
-import com.android.wm.shell.compatui.CompatUI;
import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
-import com.android.wm.shell.draganddrop.DragAndDrop;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
-import com.android.wm.shell.fullscreen.FullscreenUnfoldController;
-import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
@@ -87,11 +78,12 @@ import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
-import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
-import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelperController;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInterface;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
import java.util.Optional;
@@ -163,16 +155,13 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static DragAndDropController provideDragAndDropController(Context context,
- DisplayController displayController, UiEventLogger uiEventLogger,
- IconProvider iconProvider, @ShellMainThread ShellExecutor mainExecutor) {
- return new DragAndDropController(context, displayController, uiEventLogger, iconProvider,
- mainExecutor);
- }
-
- @WMSingleton
- @Provides
- static Optional<DragAndDrop> provideDragAndDrop(DragAndDropController dragAndDropController) {
- return Optional.of(dragAndDropController.asDragAndDrop());
+ ShellController shellController,
+ DisplayController displayController,
+ UiEventLogger uiEventLogger,
+ IconProvider iconProvider,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new DragAndDropController(context, shellController, displayController, uiEventLogger,
+ iconProvider, mainExecutor);
}
@WMSingleton
@@ -180,9 +169,11 @@ public abstract class WMShellBaseModule {
static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
Context context,
CompatUIController compatUI,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasksOptional
) {
- return new ShellTaskOrganizer(mainExecutor, context, compatUI, recentTasksOptional);
+ return new ShellTaskOrganizer(mainExecutor, context, compatUI, unfoldAnimationController,
+ recentTasksOptional);
}
@WMSingleton
@@ -194,25 +185,23 @@ public abstract class WMShellBaseModule {
SyncTransactionQueue syncTransactionQueue,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasksOptional
) {
return new KidsModeTaskOrganizer(mainExecutor, mainHandler, context, syncTransactionQueue,
- displayController, displayInsetsController, recentTasksOptional);
- }
-
- @WMSingleton
- @Provides static Optional<CompatUI> provideCompatUI(CompatUIController compatUIController) {
- return Optional.of(compatUIController.asCompatUI());
+ displayController, displayInsetsController, unfoldAnimationController,
+ recentTasksOptional);
}
@WMSingleton
@Provides
static CompatUIController provideCompatUIController(Context context,
+ ShellController shellController,
DisplayController displayController, DisplayInsetsController displayInsetsController,
DisplayImeController imeController, SyncTransactionQueue syncQueue,
@ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy) {
- return new CompatUIController(context, displayController, displayInsetsController,
- imeController, syncQueue, mainExecutor, transitionsLazy);
+ return new CompatUIController(context, shellController, displayController,
+ displayInsetsController, imeController, syncQueue, mainExecutor, transitionsLazy);
}
@WMSingleton
@@ -294,13 +283,11 @@ public abstract class WMShellBaseModule {
static FullscreenTaskListener provideFullscreenTaskListener(
@DynamicOverride Optional<FullscreenTaskListener> fullscreenTaskListener,
SyncTransactionQueue syncQueue,
- Optional<FullscreenUnfoldController> optionalFullscreenUnfoldController,
Optional<RecentTasksController> recentTasksOptional) {
if (fullscreenTaskListener.isPresent()) {
return fullscreenTaskListener.get();
} else {
- return new FullscreenTaskListener(syncQueue, optionalFullscreenUnfoldController,
- recentTasksOptional);
+ return new FullscreenTaskListener(syncQueue, recentTasksOptional);
}
}
@@ -314,31 +301,33 @@ public abstract class WMShellBaseModule {
// Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
@BindsOptionalOf
@DynamicOverride
- abstract FullscreenUnfoldController optionalFullscreenUnfoldController();
+ abstract UnfoldAnimationController optionalUnfoldController();
@WMSingleton
@Provides
- static Optional<FullscreenUnfoldController> provideFullscreenUnfoldController(
- @DynamicOverride Optional<FullscreenUnfoldController> fullscreenUnfoldController,
+ static Optional<UnfoldAnimationController> provideUnfoldController(
+ @DynamicOverride Lazy<Optional<UnfoldAnimationController>>
+ fullscreenUnfoldController,
Optional<ShellUnfoldProgressProvider> progressProvider) {
if (progressProvider.isPresent()
&& progressProvider.get() != ShellUnfoldProgressProvider.NO_PROVIDER) {
- return fullscreenUnfoldController;
+ return fullscreenUnfoldController.get();
}
return Optional.empty();
}
+ @BindsOptionalOf
+ @DynamicOverride
+ abstract UnfoldTransitionHandler optionalUnfoldTransitionHandler();
+
@WMSingleton
@Provides
static Optional<UnfoldTransitionHandler> provideUnfoldTransitionHandler(
Optional<ShellUnfoldProgressProvider> progressProvider,
- TransactionPool transactionPool,
- Transitions transitions,
- @ShellMainThread ShellExecutor executor) {
- if (progressProvider.isPresent()) {
- return Optional.of(
- new UnfoldTransitionHandler(progressProvider.get(), transactionPool, executor,
- transitions));
+ @DynamicOverride Lazy<Optional<UnfoldTransitionHandler>> handler) {
+ if (progressProvider.isPresent()
+ && progressProvider.get() != ShellUnfoldProgressProvider.NO_PROVIDER) {
+ return handler.get();
}
return Optional.empty();
}
@@ -350,12 +339,12 @@ public abstract class WMShellBaseModule {
// Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
@BindsOptionalOf
@DynamicOverride
- abstract FreeformTaskListener optionalFreeformTaskListener();
+ abstract FreeformTaskListener<?> optionalFreeformTaskListener();
@WMSingleton
@Provides
- static Optional<FreeformTaskListener> provideFreeformTaskListener(
- @DynamicOverride Optional<FreeformTaskListener> freeformTaskListener,
+ static Optional<FreeformTaskListener<?>> provideFreeformTaskListener(
+ @DynamicOverride Optional<FreeformTaskListener<?>> freeformTaskListener,
Context context) {
if (FreeformTaskListener.isFreeformEnabled(context)) {
return freeformTaskListener;
@@ -369,17 +358,12 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
- static Optional<HideDisplayCutout> provideHideDisplayCutout(
- Optional<HideDisplayCutoutController> hideDisplayCutoutController) {
- return hideDisplayCutoutController.map((controller) -> controller.asHideDisplayCutout());
- }
-
- @WMSingleton
- @Provides
static Optional<HideDisplayCutoutController> provideHideDisplayCutoutController(Context context,
- DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor) {
+ ShellController shellController, DisplayController displayController,
+ @ShellMainThread ShellExecutor mainExecutor) {
return Optional.ofNullable(
- HideDisplayCutoutController.create(context, displayController, mainExecutor));
+ HideDisplayCutoutController.create(context, shellController, displayController,
+ mainExecutor));
}
//
@@ -408,23 +392,6 @@ public abstract class WMShellBaseModule {
}
//
- // Task to Surface communication
- //
-
- @WMSingleton
- @Provides
- static Optional<TaskSurfaceHelper> provideTaskSurfaceHelper(
- Optional<TaskSurfaceHelperController> taskSurfaceController) {
- return taskSurfaceController.map((controller) -> controller.asTaskSurfaceHelper());
- }
-
- @Provides
- static Optional<TaskSurfaceHelperController> provideTaskSurfaceHelperController(
- ShellTaskOrganizer taskOrganizer, @ShellMainThread ShellExecutor mainExecutor) {
- return Optional.ofNullable(new TaskSurfaceHelperController(taskOrganizer, mainExecutor));
- }
-
- //
// Pip (optional feature)
//
@@ -561,29 +528,6 @@ public abstract class WMShellBaseModule {
return Optional.empty();
}
- // Legacy split (optional feature)
-
- @WMSingleton
- @Provides
- static Optional<LegacySplitScreen> provideLegacySplitScreen(
- Optional<LegacySplitScreenController> splitScreenController) {
- return splitScreenController.map((controller) -> controller.asLegacySplitScreen());
- }
-
- @BindsOptionalOf
- abstract LegacySplitScreenController optionalLegacySplitScreenController();
-
- // App Pairs (optional feature)
-
- @WMSingleton
- @Provides
- static Optional<AppPairs> provideAppPairs(Optional<AppPairsController> appPairsController) {
- return appPairsController.map((controller) -> controller.asAppPairs());
- }
-
- @BindsOptionalOf
- abstract AppPairsController optionalAppPairs();
-
//
// Starting window
//
@@ -644,19 +588,43 @@ public abstract class WMShellBaseModule {
taskViewTransitions);
}
+
//
- // Misc
+ // ActivityEmbedding
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<ActivityEmbeddingController> provideActivityEmbeddingController(
+ Context context, Transitions transitions) {
+ return Optional.of(new ActivityEmbeddingController(context, transitions));
+ }
+
+ //
+ // SysUI -> Shell interface
//
@WMSingleton
@Provides
- static ShellInit provideShellInit(ShellInitImpl impl) {
- return impl.asShellInit();
+ static ShellInterface provideShellSysuiCallbacks(ShellController shellController) {
+ return shellController.asShell();
}
@WMSingleton
@Provides
- static ShellInitImpl provideShellInitImpl(DisplayController displayController,
+ static ShellController provideShellController(@ShellMainThread ShellExecutor mainExecutor) {
+ return new ShellController(mainExecutor);
+ }
+
+ //
+ // Misc
+ //
+
+ @WMSingleton
+ @Provides
+ static ShellInit provideShellInitImpl(
+ ShellController shellController,
+ DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
DragAndDropController dragAndDropController,
@@ -664,17 +632,18 @@ public abstract class WMShellBaseModule {
KidsModeTaskOrganizer kidsModeTaskOrganizer,
Optional<BubbleController> bubblesOptional,
Optional<SplitScreenController> splitScreenOptional,
- Optional<AppPairsController> appPairsOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
- Optional<FullscreenUnfoldController> appUnfoldTransitionController,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
- Optional<FreeformTaskListener> freeformTaskListener,
+ Optional<FreeformTaskListener<?>> freeformTaskListener,
Optional<RecentTasksController> recentTasksOptional,
+ Optional<ActivityEmbeddingController> activityEmbeddingOptional,
Transitions transitions,
StartingWindowController startingWindow,
@ShellMainThread ShellExecutor mainExecutor) {
- return new ShellInitImpl(displayController,
+ return new ShellInit(shellController,
+ displayController,
displayImeController,
displayInsetsController,
dragAndDropController,
@@ -682,44 +651,33 @@ public abstract class WMShellBaseModule {
kidsModeTaskOrganizer,
bubblesOptional,
splitScreenOptional,
- appPairsOptional,
pipTouchHandlerOptional,
fullscreenTaskListener,
- appUnfoldTransitionController,
+ unfoldAnimationController,
unfoldTransitionHandler,
freeformTaskListener,
recentTasksOptional,
+ activityEmbeddingOptional,
transitions,
startingWindow,
mainExecutor);
}
- /**
- * Note, this is only optional because we currently pass this to the SysUI component scope and
- * for non-primary users, we may inject a null-optional for that dependency.
- */
- @WMSingleton
- @Provides
- static Optional<ShellCommandHandler> provideShellCommandHandler(ShellCommandHandlerImpl impl) {
- return Optional.of(impl.asShellCommandHandler());
- }
-
@WMSingleton
@Provides
- static ShellCommandHandlerImpl provideShellCommandHandlerImpl(
+ static ShellCommandHandler provideShellCommandHandlerImpl(
+ ShellController shellController,
ShellTaskOrganizer shellTaskOrganizer,
KidsModeTaskOrganizer kidsModeTaskOrganizer,
- Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<Pip> pipOptional,
Optional<OneHandedController> oneHandedOptional,
Optional<HideDisplayCutoutController> hideDisplayCutout,
- Optional<AppPairsController> appPairsOptional,
Optional<RecentTasksController> recentTasksOptional,
@ShellMainThread ShellExecutor mainExecutor) {
- return new ShellCommandHandlerImpl(shellTaskOrganizer, kidsModeTaskOrganizer,
- legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional,
- hideDisplayCutout, appPairsOptional, recentTasksOptional, mainExecutor);
+ return new ShellCommandHandler(shellController, shellTaskOrganizer,
+ kidsModeTaskOrganizer, splitScreenOptional, pipOptional, oneHandedOptional,
+ hideDisplayCutout, recentTasksOptional, mainExecutor);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index b3799e2cf8d9..0f33f4c08e9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.dagger;
-import android.animation.AnimationHandler;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.os.Handler;
@@ -31,8 +30,11 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
-import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.bubbles.BubbleData;
+import com.android.wm.shell.bubbles.BubbleDataRepository;
+import com.android.wm.shell.bubbles.BubbleLogger;
+import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -43,13 +45,10 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformTaskListener;
-import com.android.wm.shell.fullscreen.FullscreenUnfoldController;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
@@ -67,19 +66,30 @@ import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipController;
+import com.android.wm.shell.pip.phone.PipKeepClearAlgorithm;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.splitscreen.StageTaskUnfoldController;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
import com.android.wm.shell.unfold.UnfoldBackgroundController;
-
+import com.android.wm.shell.unfold.UnfoldTransitionHandler;
+import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
+import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
+import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
+import com.android.wm.shell.unfold.qualifier.UnfoldShellTransition;
+import com.android.wm.shell.unfold.qualifier.UnfoldTransition;
+import com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
+
+import java.util.ArrayList;
+import java.util.List;
import java.util.Optional;
-import javax.inject.Provider;
-
+import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -93,16 +103,40 @@ import dagger.Provides;
* dependencies should go into {@link WMShellBaseModule}.
*/
@Module(includes = WMShellBaseModule.class)
-public class WMShellModule {
+public abstract class WMShellModule {
//
// Bubbles
//
+ @WMSingleton
+ @Provides
+ static BubbleLogger provideBubbleLogger(UiEventLogger uiEventLogger) {
+ return new BubbleLogger(uiEventLogger);
+ }
+
+ @WMSingleton
+ @Provides
+ static BubblePositioner provideBubblePositioner(Context context,
+ WindowManager windowManager) {
+ return new BubblePositioner(context, windowManager);
+ }
+
+ @WMSingleton
+ @Provides
+ static BubbleData provideBubbleData(Context context,
+ BubbleLogger logger,
+ BubblePositioner positioner,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new BubbleData(context, logger, positioner, mainExecutor);
+ }
+
// Note: Handler needed for LauncherApps.register
@WMSingleton
@Provides
static BubbleController provideBubbleController(Context context,
+ ShellController shellController,
+ BubbleData data,
FloatingContentCoordinator floatingContentCoordinator,
IStatusBarService statusBarService,
WindowManager windowManager,
@@ -110,8 +144,9 @@ public class WMShellModule {
UserManager userManager,
LauncherApps launcherApps,
TaskStackListenerImpl taskStackListener,
- UiEventLogger uiEventLogger,
+ BubbleLogger logger,
ShellTaskOrganizer organizer,
+ BubblePositioner positioner,
DisplayController displayController,
@DynamicOverride Optional<OneHandedController> oneHandedOptional,
DragAndDropController dragAndDropController,
@@ -120,24 +155,46 @@ public class WMShellModule {
@ShellBackgroundThread ShellExecutor bgExecutor,
TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
- return BubbleController.create(context, null /* synchronizer */,
- floatingContentCoordinator, statusBarService, windowManager,
- windowManagerShellWrapper, userManager, launcherApps, taskStackListener,
- uiEventLogger, organizer, displayController, oneHandedOptional,
- dragAndDropController, mainExecutor, mainHandler, bgExecutor,
+ return new BubbleController(context, shellController, data, null /* synchronizer */,
+ floatingContentCoordinator,
+ new BubbleDataRepository(context, launcherApps, mainExecutor),
+ statusBarService, windowManager, windowManagerShellWrapper, userManager,
+ launcherApps, logger, taskStackListener, organizer, positioner, displayController,
+ oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor,
taskViewTransitions, syncQueue);
}
//
+ // Window decoration
+ //
+
+ @WMSingleton
+ @Provides
+ static WindowDecorViewModel<?> provideWindowDecorViewModel(
+ Context context,
+ @ShellMainThread Handler mainHandler,
+ ShellTaskOrganizer taskOrganizer,
+ DisplayController displayController,
+ SyncTransactionQueue syncQueue) {
+ return new CaptionWindowDecorViewModel(
+ context,
+ mainHandler,
+ taskOrganizer,
+ displayController,
+ syncQueue);
+ }
+
+ //
// Freeform
//
@WMSingleton
@Provides
@DynamicOverride
- static FreeformTaskListener provideFreeformTaskListener(
+ static FreeformTaskListener<?> provideFreeformTaskListener(
+ WindowDecorViewModel<?> windowDecorViewModel,
SyncTransactionQueue syncQueue) {
- return new FreeformTaskListener(syncQueue);
+ return new FreeformTaskListener<>(windowDecorViewModel, syncQueue);
}
//
@@ -150,12 +207,14 @@ public class WMShellModule {
@Provides
@DynamicOverride
static OneHandedController provideOneHandedController(Context context,
+ ShellController shellController,
WindowManager windowManager, DisplayController displayController,
DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
UiEventLogger uiEventLogger, InteractionJankMonitor jankMonitor,
@ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler) {
- return OneHandedController.create(context, windowManager, displayController, displayLayout,
- taskStackListener, jankMonitor, uiEventLogger, mainExecutor, mainHandler);
+ return OneHandedController.create(context, shellController, windowManager,
+ displayController, displayLayout, taskStackListener, jankMonitor, uiEventLogger,
+ mainExecutor, mainHandler);
}
//
@@ -166,6 +225,7 @@ public class WMShellModule {
@Provides
@DynamicOverride
static SplitScreenController provideSplitScreenController(
+ ShellController shellController,
ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, Context context,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
@@ -174,37 +234,11 @@ public class WMShellModule {
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool, IconProvider iconProvider,
- Optional<RecentTasksController> recentTasks,
- Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
- return new SplitScreenController(shellTaskOrganizer, syncQueue, context,
+ Optional<RecentTasksController> recentTasks) {
+ return new SplitScreenController(shellController, shellTaskOrganizer, syncQueue, context,
rootTaskDisplayAreaOrganizer, mainExecutor, displayController, displayImeController,
displayInsetsController, transitions, transactionPool, iconProvider,
- recentTasks, stageTaskUnfoldControllerProvider);
- }
-
- @WMSingleton
- @Provides
- static LegacySplitScreenController provideLegacySplitScreen(Context context,
- DisplayController displayController, SystemWindows systemWindows,
- DisplayImeController displayImeController, TransactionPool transactionPool,
- ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
- TaskStackListenerImpl taskStackListener, Transitions transitions,
- @ShellMainThread ShellExecutor mainExecutor,
- @ChoreographerSfVsync AnimationHandler sfVsyncAnimationHandler) {
- return new LegacySplitScreenController(context, displayController, systemWindows,
- displayImeController, transactionPool, shellTaskOrganizer, syncQueue,
- taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler);
- }
-
- @WMSingleton
- @Provides
- static AppPairsController provideAppPairs(ShellTaskOrganizer shellTaskOrganizer,
- SyncTransactionQueue syncQueue, DisplayController displayController,
- @ShellMainThread ShellExecutor mainExecutor,
- DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController) {
- return new AppPairsController(shellTaskOrganizer, syncQueue, displayController,
- mainExecutor, displayImeController, displayInsetsController);
+ recentTasks);
}
//
@@ -213,19 +247,23 @@ public class WMShellModule {
@WMSingleton
@Provides
- static Optional<Pip> providePip(Context context, DisplayController displayController,
+ static Optional<Pip> providePip(Context context,
+ ShellController shellController, DisplayController displayController,
PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipBoundsState pipBoundsState, PipMediaController pipMediaController,
+ PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState,
+ PipMotionHelper pipMotionHelper, PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
+ PipTransitionState pipTransitionState,
PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<OneHandedController> oneHandedController,
@ShellMainThread ShellExecutor mainExecutor) {
- return Optional.ofNullable(PipController.create(context, displayController,
- pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState,
- pipMediaController, phonePipMenuController, pipTaskOrganizer,
+ return Optional.ofNullable(PipController.create(context, shellController, displayController,
+ pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState,
+ pipMotionHelper,
+ pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
taskStackListener, pipParamsChangedForwarder, oneHandedController, mainExecutor));
}
@@ -244,6 +282,12 @@ public class WMShellModule {
@WMSingleton
@Provides
+ static PipKeepClearAlgorithm providePipKeepClearAlgorithm() {
+ return new PipKeepClearAlgorithm();
+ }
+
+ @WMSingleton
+ @Provides
static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) {
return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm);
@@ -351,40 +395,77 @@ public class WMShellModule {
//
// Unfold transition
//
-
@WMSingleton
@Provides
@DynamicOverride
- static FullscreenUnfoldController provideFullscreenUnfoldController(
- Context context,
+ static UnfoldAnimationController provideUnfoldAnimationController(
Optional<ShellUnfoldProgressProvider> progressProvider,
- Lazy<UnfoldBackgroundController> unfoldBackgroundController,
- DisplayInsetsController displayInsetsController,
+ TransactionPool transactionPool,
+ @UnfoldTransition SplitTaskUnfoldAnimator splitAnimator,
+ FullscreenUnfoldTaskAnimator fullscreenAnimator,
+ Lazy<Optional<UnfoldTransitionHandler>> unfoldTransitionHandler,
@ShellMainThread ShellExecutor mainExecutor
) {
- return new FullscreenUnfoldController(context, mainExecutor,
- unfoldBackgroundController.get(), progressProvider.get(),
+ final List<UnfoldTaskAnimator> animators = new ArrayList<>();
+ animators.add(splitAnimator);
+ animators.add(fullscreenAnimator);
+
+ return new UnfoldAnimationController(
+ transactionPool,
+ progressProvider.get(),
+ animators,
+ unfoldTransitionHandler,
+ mainExecutor
+ );
+ }
+
+
+ @Provides
+ static FullscreenUnfoldTaskAnimator provideFullscreenUnfoldTaskAnimator(
+ Context context,
+ UnfoldBackgroundController unfoldBackgroundController,
+ DisplayInsetsController displayInsetsController
+ ) {
+ return new FullscreenUnfoldTaskAnimator(context, unfoldBackgroundController,
displayInsetsController);
}
@Provides
- static Optional<StageTaskUnfoldController> provideStageTaskUnfoldController(
- Optional<ShellUnfoldProgressProvider> progressProvider,
+ static SplitTaskUnfoldAnimator provideSplitTaskUnfoldAnimatorBase(
Context context,
- TransactionPool transactionPool,
- Lazy<UnfoldBackgroundController> unfoldBackgroundController,
- DisplayInsetsController displayInsetsController,
- @ShellMainThread ShellExecutor mainExecutor
+ UnfoldBackgroundController backgroundController,
+ @ShellMainThread ShellExecutor executor,
+ Lazy<Optional<SplitScreenController>> splitScreenOptional,
+ DisplayInsetsController displayInsetsController
) {
- return progressProvider.map(shellUnfoldTransitionProgressProvider ->
- new StageTaskUnfoldController(
- context,
- transactionPool,
- shellUnfoldTransitionProgressProvider,
- displayInsetsController,
- unfoldBackgroundController.get(),
- mainExecutor
- ));
+ return new SplitTaskUnfoldAnimator(context, executor, splitScreenOptional,
+ backgroundController, displayInsetsController);
+ }
+
+ @WMSingleton
+ @UnfoldShellTransition
+ @Binds
+ abstract SplitTaskUnfoldAnimator provideShellSplitTaskUnfoldAnimator(
+ SplitTaskUnfoldAnimator splitTaskUnfoldAnimator);
+
+ @WMSingleton
+ @UnfoldTransition
+ @Binds
+ abstract SplitTaskUnfoldAnimator provideSplitTaskUnfoldAnimator(
+ SplitTaskUnfoldAnimator splitTaskUnfoldAnimator);
+
+ @WMSingleton
+ @Provides
+ @DynamicOverride
+ static UnfoldTransitionHandler provideUnfoldTransitionHandler(
+ Optional<ShellUnfoldProgressProvider> progressProvider,
+ FullscreenUnfoldTaskAnimator animator,
+ @UnfoldShellTransition SplitTaskUnfoldAnimator unfoldAnimator,
+ TransactionPool transactionPool,
+ Transitions transitions,
+ @ShellMainThread ShellExecutor executor) {
+ return new UnfoldTransitionHandler(progressProvider.get(), animator,
+ unfoldAnimator, transactionPool, executor, transitions);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md
new file mode 100644
index 000000000000..73a7348d5aca
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md
@@ -0,0 +1,18 @@
+# Window Manager Shell Readme
+
+The following docs present more detail about the implementation of the WMShell library (in no
+particular order):
+
+1) [What is the Shell](overview.md)
+2) [Integration with SystemUI & Launcher](sysui.md)
+3) [Usage of Dagger](dagger.md)
+4) [Threading model in the Shell](threading.md)
+5) [Making changes in the Shell](changes.md)
+6) [Extending the Shell for Products/OEMs](extending.md)
+7) [Debugging in the Shell](debugging.md)
+8) [Testing in the Shell](testing.md)
+
+Todo
+- Per-feature docs
+- Feature flagging
+- Best practices \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
new file mode 100644
index 000000000000..f4e2f20f4637
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
@@ -0,0 +1,73 @@
+# Making changes in the Shell
+
+---
+
+## Code reviews
+
+In addition to the individual reviewers who are most familiar with the changes you are making,
+please also add [wm-code-reviewers@google.com](http://g/wm-code-reviewers) to keep other WM folks
+in the loop.
+
+## Adding new code
+
+### Internal Shell utility classes
+If the new component is used only within the WMShell library, then there are no special
+considerations, go ahead and add it (in the `com.android.wm.shell.common` package for example)
+and make sure the appropriate [unit tests](testing.md) are added.
+
+### Internal Shell components
+If the new component is to be used by other components/features within the Shell library, then
+you can create an appropriate package for this component to add your new code. The current
+pattern is to have a single `<Component name>Controller` that handles the initialization of the
+component.
+
+As mentioned in the [Dagger usage](dagger.md) docs, you need to determine whether it should go into:
+- `WMShellBaseModule` for components that other base & product components will depend on
+- or `WMShellModule`, `TvWmShellModule`, etc. for product specific components that no base
+ components depend on
+
+### SysUI accessible components
+In addition to doing the above, you will also need to provide an interface for calling to SysUI
+from the Shell and vice versa. The current pattern is to have a parallel `Optional<Component name>`
+interface that the `<Component name>Controller` implements and handles on the main Shell thread.
+
+In addition, because components accessible to SysUI injection are explicitly listed, you'll have to
+add an appropriate method in `WMComponent` to get the interface and update the `Builder` in
+`SysUIComponent` to take the interface so it can be injected in SysUI code. The binding between
+the two is done in `SystemUIFactory#init()` which will need to be updated as well.
+
+### Launcher accessible components
+Because Launcher is not a part of SystemUI and is a separate process, exposing controllers to
+Launcher requires a new AIDL interface to be created and implemented by the controller. The
+implementation of the stub interface in the controller otherwise behaves similar to the interface
+to SysUI where it posts the work to the main Shell thread.
+
+### Component initialization
+To initialize the component:
+- On the Shell side, update `ShellInitImpl` to get a signal to initialize when the SysUI is started
+- On the SysUI side, update `WMShell` to setup any bindings for the component that depend on
+ SysUI code
+
+### General Do's & Dont's
+Do:
+- Do add unit tests for all new components
+- Do keep controllers simple and break them down as needed
+
+Don't:
+- **Don't** do initialization in the constructor, only do initialization in the init callbacks.
+ Otherwise it complicates the building of the dependency graph.
+- **Don't** create dependencies from base-module components on specific features (the base module
+ is intended for use with all products)
+ - Try adding a mechanism to register and listen for changes from the base module component instead
+- **Don't** add blocking synchronous calls in the SysUI interface between Shell & SysUI
+ - Try adding a push-mechanism to share data, or an async callback to request data
+
+### Exposing shared code for use in Launcher
+Launcher doesn't currently build against the Shell library, but needs to have access to some shared
+AIDL interfaces and constants. Currently, all AIDL files, and classes under the
+`com.android.wm.shell.util` package are automatically built into the `SystemUISharedLib` that
+Launcher uses.
+
+If the new code doesn't fall into those categories, they can be added explicitly in the Shell's
+[Android.bp](frameworks/base/libs/WindowManager/Shell/Android.bp) file under the
+`wm_shell_util-sources` filegroup. \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/dagger.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/dagger.md
new file mode 100644
index 000000000000..6c01d962adc9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/dagger.md
@@ -0,0 +1,50 @@
+# Usage of Dagger in the Shell library
+
+---
+
+## Dependencies
+
+Dagger is not required to use the Shell library, but it has a lot of obvious benefits:
+
+- Not having to worry about how to instantiate all the dependencies of a class, especially as
+ dependencies evolve (ie. product controller depends on base controller)
+- Can create boundaries within the same app to encourage better code modularity
+
+As such, the Shell also tries to provide some reasonable out-of-the-box modules for use with Dagger.
+
+## Modules
+
+All the Dagger related code in the Shell can be found in the `com.android.wm.shell.dagger` package,
+this is intentional as it keeps the "magic" in a single location. The explicit nature of how
+components in the shell are provided is as a result a bit more verbose, but it makes it easy for
+developers to jump into a few select files and understand how different components are provided
+(especially as products override components).
+
+The module dependency tree looks a bit like:
+- [WMShellConcurrencyModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java)
+ (provides threading-related components)
+ - [WMShellBaseModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java)
+ (provides components that are likely common to all products, ie. DisplayController,
+ Transactions, etc.)
+ - [WMShellModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java)
+ (phone/tablet specific components only)
+ - [TvPipModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java)
+ (PIP specific components for TV)
+ - [TvWMShellModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java)
+ (TV specific components only)
+ - etc.
+
+Ideally features could be abstracted out into their own modules and included as needed by each
+product.
+
+## Overriding base components
+
+In some rare cases, there are base components that can change behavior depending on which
+product it runs on. If there are hooks that can be added to the component, that is the
+preferable approach.
+
+The alternative is to use the [@DynamicOverride](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/DynamicOverride.java)
+annotation to allow the product module to provide an implementation that the base module can
+reference. This is most useful if the existence of the entire component is controlled by the
+product and the override implementation is optional (there is a default implementation). More
+details can be found in the class's javadoc. \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
new file mode 100644
index 000000000000..52f0c4222b64
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
@@ -0,0 +1,69 @@
+# Debugging in the Shell
+
+---
+
+## Logging & ProtoLogs
+
+The interactions in the Shell can be pretty complicated, so having good logging is crucial to
+debugging problems that arise (especially in dogfood). The Shell uses the same efficient Protolog
+mechanism as WM Core, which can be enabled at runtime on debug devices.
+
+**TLDR**&nbsp; Don’t use Logs or Slogs except for error cases, Protologs are much more flexible,
+easy to add and easy to use
+
+### Adding a new ProtoLog
+Update `ShellProtoLogGroup` to include a new log group (ie. NEW_FEATURE) for the content you want to
+log. ProtoLog log calls mirror Log.v/d/e(), and take a format message and arguments:
+```java
+ProtoLog.v(NEW_FEATURE, "Test log w/ params: %d %s", 1, “a”)
+```
+This code itself will not compile by itself, but the `protologtool` will preprocess the file when
+building to check the log state (is enabled) before printing the print format style log.
+
+**Notes**
+- ProtoLogs currently only work from soong builds (ie. via make/mp). We need to reimplement the
+ tool for use with SysUI-studio
+- Non-text ProtoLogs are not currently supported with the Shell library (you can't view them with
+ traces in Winscope)
+
+### Enabling ProtoLog command line logging
+Run these commands to enable protologs for both WM Core and WM Shell to print to logcat.
+```shell
+adb shell wm logging enable-text NEW_FEATURE
+adb shell wm logging disable-text NEW_FEATURE
+```
+
+## Winscope Tracing
+
+The Winscope tool is extremely useful in determining what is happening on-screen in both
+WindowManager and SurfaceFlinger. Follow [go/winscope](http://go/winscope-help) to learn how to
+use the tool.
+
+In addition, there is limited preliminary support for Winscope tracing componetns in the Shell,
+which involves adding trace fields to [wm_shell_trace.proto](frameworks/base/libs/WindowManager/Shell/proto/wm_shell_trace.proto)
+file and ensure it is updated as a part of `WMShell#writeToProto`.
+
+Tracing can be started via the shell command (to be added to the Winscope tool as needed):
+```shell
+adb shell cmd statusbar tracing start
+adb shell cmd statusbar tracing stop
+```
+
+## Dumps
+
+Because the Shell library is built as a part of SystemUI, dumping the state is currently done as a
+part of dumping the SystemUI service. Dumping the Shell specific data can be done by specifying the
+WMShell SysUI service:
+
+```shell
+adb shell dumpsys activity service SystemUIService WMShell
+```
+
+If information should be added to the dump, make updates to:
+- `WMShell` if you are dumping SysUI state
+- `ShellCommandHandler` if you are dumping Shell state
+
+## Debugging in Android Studio
+
+If you are using the [go/sysui-studio](http://go/sysui-studio) project, then you can debug Shell
+code directly from Android Studio like any other app.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/extending.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/extending.md
new file mode 100644
index 000000000000..061ae00e2b25
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/extending.md
@@ -0,0 +1,13 @@
+# Extending the Shell for Products/OEMs
+
+---
+
+## General Do's & Dont's
+
+Do:
+- &nbsp;
+
+Don't
+- **Don't** override classes provided by WMShellBaseModule, it makes it difficult to make
+ simple changes to the Shell library base modules which are shared by all products
+ - If possible add mechanisms to modify the base class behavior \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/overview.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/overview.md
new file mode 100644
index 000000000000..a88ef6aea2ec
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/overview.md
@@ -0,0 +1,58 @@
+# What is the WindowManager Shell
+
+---
+
+## Motivation
+
+The primary motivation for the WindowManager Shell (WMShell) library is to effectively scale
+WindowManager by making it easy&trade; and safe to create windowing features to fit the needs of
+various Android products and form factors.
+
+To achieve this, WindowManager separates the policy of managing windows (WMCore) from the
+presentation of surfaces (WMShell) and provides a minimal interface boundary for the two to
+communicate.
+
+## Who is using the library?
+
+Currently, the WMShell library is used to drive the windowing experience on handheld
+(phones & tablets), TV, Auto, Arc++, and Wear to varying degrees.
+
+## Where does the code live
+
+The core WMShell library code is currently located in the [frameworks/base/libs/WindowManager/Shell](frameworks/base/libs/WindowManager/Shell)
+directory and is included as a part dependency of the host SystemUI apk.
+
+## How do I build the Shell library
+
+The library can be built directly by running (using [go/makepush](http://go/makepush)):
+```shell
+mp :WindowManager-Shell
+```
+But this is mainly useful for inspecting the contents of the library or verifying it builds. The
+various targets can be found in the Shell library's [Android.bp](frameworks/base/libs/WindowManager/Shell/Android.bp)
+file.
+
+Normally, you would build it as a part of the host SystemUI, for example via commandline:
+```shell
+# Phone SystemUI variant
+mp sysuig
+# Building Shell & SysUI changes along w/ framework changes
+mp core services sysuig
+```
+
+Or preferably, if you are making WMShell/SysUI only changes (no other framework changes), then
+building via [go/sysui-studio](http://go/sysui-studio) allows for very quick iteration (one click
+build and push of SysUI in < 30s).
+
+If you are making framework changes and are using `aidegen` to set up your platform IDE, make sure
+to include the appropriate directories to build, for example:
+```shell
+# frameworks/base will include base/libs/WindowManager/Shell and base/packages/SystemUI
+aidegen frameworks/base \
+ vendor/<oem>/packages/SystemUI \
+ ...
+```
+
+## Other useful links
+- [go/o-o-summit-20](go/o-o-summit-20) (Video presentations from the WM team)
+- [go/o-o-summit-21](go/o-o-summit-21) \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md
new file mode 100644
index 000000000000..0dd50b1bee68
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md
@@ -0,0 +1,65 @@
+# Shell & SystemUI
+
+---
+
+## Setup
+
+The SystemUI of various products depend on and build against the WM Shell library. To ensure
+that we don't inadvertently build dependencies between the Shell library and one particular
+product (ie. handheld SysUI), we deliberately separate the initialization of the WM Shell
+component from the SysUI component when set up through Dagger.
+
+**TLDR**&nbsp; Initialize everything as needed in the WM component scope and export only well
+defined interfaces to SysUI.
+
+## Initialization
+
+There are more details in the Dagger docs, but the general overview of the SysUI/Shell
+initialization flow is such:
+
+1) SysUI Global scope is initialize (see `GlobalModule` and its included modules)
+2) WM Shell scope is initialized, for example
+ 1) On phones: `WMComponent` includes `WMShellModule` which includes `WMShellBaseModule`
+ (common to all SysUI)
+ 2) On TVs: `TvWMComponent` includes `TvWMShellModule` which includes `WMShellBaseModule`
+ 3) etc.
+3) SysUI explicitly passes interfaces provided from the `WMComponent` to `SysUIComponent` via
+ the `SysUIComponent#Builder`, then builds the SysUI scoped components
+4) `WMShell` is the SystemUI “service” (in the SysUI scope) that initializes with the app after the
+SystemUI part of the dependency graph has been created. It contains the binding code between the
+interfaces provided by the Shell and the rest of SystemUI.
+5) SysUI can inject the interfaces into its own components
+
+More detail can be found in [go/wm-sysui-dagger](http://go/wm-sysui-dagger).
+
+## Interfaces to Shell components
+
+Within the same process, the WM Shell components can be running on a different thread than the main
+SysUI thread (disabled on certain products). This introduces challenges where we have to be
+careful about how SysUI calls into the Shell and vice versa.
+
+As a result, we enforce explicit interfaces between SysUI and Shell components, and the
+implementations of the interfaces on each side need to post to the right thread before it calls
+into other code.
+
+For example, you might have:
+1) (Shell) ShellFeature interface to be used from SysUI
+2) (Shell) ShellFeatureController handles logic, implements ShellFeature interface and posts to
+ main Shell thread
+3) SysUI application init injects Optional<ShellFeature> as an interface to SysUI to call
+4) (SysUI) SysUIFeature depends on ShellFeature interface
+5) (SysUI) SysUIFeature injects Optional<ShellFeature>, and sets up a callback for the Shell to
+ call, and the callback posts to the main SysUI thread
+
+Adding an interface to a Shell component may seem like a lot of boiler plate, but is currently
+necessary to maintain proper threading and logic isolation.
+
+## Configuration changes & other SysUI events
+
+Aside from direct calls into Shell controllers for exposed features, the Shell also receives
+common event callbacks from SysUI via the `ShellController`. This includes things like:
+
+- Configuration changes
+- TODO: Shell init
+- TODO: Shell command
+- TODO: Keyguard events \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/testing.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/testing.md
new file mode 100644
index 000000000000..8a80333facc4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/testing.md
@@ -0,0 +1,49 @@
+# Testing
+
+---
+
+## Unit tests
+
+New WM Shell unit tests can be added to the
+[Shell/tests/unittest](frameworks/base/libs/WindowManager/Shell/tests/unittest) directory, and can
+be run via command line using `atest`:
+```shell
+atest WMShellUnitTests
+```
+
+If you use the SysUI Studio project, you can run and debug tests directly in the source files
+(click on the little arrows next to the test class or test method).
+
+These unit tests are run as a part of WindowManager presubmit, and the dashboards for these unit
+tests tests can be found at [go/wm-tests](http://go/wm-tests).
+
+This [GCL file](http://go/wm-unit-tests-gcl) configures the tests being run on the server.
+
+## Flicker tests
+
+Flicker tests are tests that perform actions and make assertions on the state in Window Manager
+and SurfaceFlinger traces captured during the run.
+
+New WM Shell Flicker tests can be added to the
+[Shell/tests/flicker](frameworks/base/libs/WindowManager/Shell/tests/flicker) directory, and can
+be run via command line using `atest`:
+```shell
+atest WMShellFlickerTests
+```
+
+**Note**: Currently Flicker tests can only be run from the commandline and not via SysUI Studio
+
+A subset of the flicker tests tests are run as a part of WindowManager presubmit, and the
+dashboards for these tests tests can be found at [go/wm-tests-flicker](http://go/wm-tests-flicker).
+
+## CTS tests
+
+Some windowing features also have CTS tests to ensure consistent behavior across OEMs. For example:
+- Picture-in-Picture:
+ [PinnedStackTests](cts/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java)
+- etc.
+
+These can also be run via commandline only using `atest`, for example:
+```shell
+atest PinnedStackTests
+``` \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md
new file mode 100644
index 000000000000..eac748894432
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md
@@ -0,0 +1,83 @@
+# Threading
+
+---
+
+## Boundaries
+
+```text
+ Thread boundary
+ |
+ WM Shell | SystemUI
+ |
+ |
+FeatureController <-> FeatureInterface <--|--> WMShell <-> SysUI
+ | (^post to shell thread) | (^post to main thread)
+ ... |
+ | |
+ OtherControllers |
+```
+
+## Threads
+
+We currently have multiple threads in use in the Shell library depending on the configuration by
+the product.
+- SysUI main thread (standard main thread)
+- `ShellMainThread` (only used if the resource `config_enableShellMainThread` is set true
+ (ie. phones))
+ - This falls back to the SysUI main thread otherwise
+ - **Note**:
+ - This thread runs with `THREAD_PRIORITY_DISPLAY` priority since so many windowing-critical
+ components depend on it
+ - This is also the UI thread for almost all UI created by the Shell
+ - The Shell main thread Handler (and the Executor that wraps it) is async, so
+ messages/runnables used via this Handler are handled immediately if there is no sync
+ messages prior to it in the queue.
+- `ShellBackgroundThread` (for longer running tasks where we don't want to block the shell main
+ thread)
+ - This is always another thread even if config_enableShellMainThread is not set true
+ - **Note**:
+ - This thread runs with `THREAD_PRIORITY_BACKGROUND` priority
+- `ShellAnimationThread` (currently only used for Transitions and Splitscreen, but potentially all
+ animations could be offloaded here)
+- `ShellSplashScreenThread` (only for use with splashscreens)
+
+## Dagger setup
+
+The threading-related components are provided by the [WMShellConcurrencyModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java),
+for example, the Executors and Handlers for the various threads that are used. You can request
+an executor of the necessary type by using the appropriate annotation for each of the threads (ie.
+`@ShellMainThread Executor`) when injecting into your Shell component.
+
+To get the SysUI main thread, you can use the `@Main` annotation.
+
+## Best practices
+
+### Components
+- Don't do initialization in the Shell component constructors
+ - If the host SysUI is not careful, it may construct the WMComponent dependencies on the main
+ thread, and this reduces the likelihood that components will intiailize on the wrong thread
+ in such cases
+- Be careful of using CountDownLatch and other blocking synchronization mechanisms in Shell code
+ - If the Shell main thread is not a separate thread, this will cause a deadlock
+- Callbacks, Observers, Listeners to any non-shell component should post onto main Shell thread
+ - This includes Binder calls, SysUI calls, BroadcastReceivers, etc. Basically any API that
+ takes a runnable should either be registered with the right Executor/Handler or posted to
+ the main Shell thread manually
+- Since everything in the Shell runs on the main Shell thread, you do **not** need to explicitly
+ `synchronize` your code (unless you are trying to prevent reentrantcy, but that can also be
+ done in other ways)
+
+### Handlers/Executors
+- You generally **never** need to create Handlers explicitly, instead inject `@ShellMainThread
+ ShellExecutor` instead
+ - This is a common pattern to defer logic in UI code, but the Handler created wraps the Looper
+ that is currently running, which can be wrong (see above for initialization vs construction)
+- That said, sometimes Handlers are necessary because Framework API only takes Handlers or you
+ want to dedupe multiple messages
+ - In such cases inject `@ShellMainThread Handler` or use view.getHandler() which should be OK
+ assuming that the view root was initialized on the main Shell thread
+- **Never use Looper.getMainLooper()**
+ - It's likely going to be wrong, you can inject `@Main ShellExecutor` to get the SysUI main thread
+
+### Testing
+- You can use a `TestShellExecutor` to control the processing of messages \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 95de2dc61a43..c5df53b6dbc8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -60,6 +60,8 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.ShellController;
import java.util.ArrayList;
import java.util.Optional;
@@ -68,21 +70,20 @@ import java.util.Optional;
* Handles the global drag and drop handling for the Shell.
*/
public class DragAndDropController implements DisplayController.OnDisplaysChangedListener,
- View.OnDragListener {
+ View.OnDragListener, ConfigurationChangeListener {
private static final String TAG = DragAndDropController.class.getSimpleName();
private final Context mContext;
+ private final ShellController mShellController;
private final DisplayController mDisplayController;
private final DragAndDropEventLogger mLogger;
private final IconProvider mIconProvider;
private SplitScreenController mSplitScreen;
private ShellExecutor mMainExecutor;
- private DragAndDropImpl mImpl;
private ArrayList<DragAndDropListener> mListeners = new ArrayList<>();
private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
- private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
/**
* Listener called during drag events, currently just onDragStarted.
@@ -92,23 +93,24 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
void onDragStarted();
}
- public DragAndDropController(Context context, DisplayController displayController,
- UiEventLogger uiEventLogger, IconProvider iconProvider, ShellExecutor mainExecutor) {
+ public DragAndDropController(Context context,
+ ShellController shellController,
+ DisplayController displayController,
+ UiEventLogger uiEventLogger,
+ IconProvider iconProvider,
+ ShellExecutor mainExecutor) {
mContext = context;
+ mShellController = shellController;
mDisplayController = displayController;
mLogger = new DragAndDropEventLogger(uiEventLogger);
mIconProvider = iconProvider;
mMainExecutor = mainExecutor;
- mImpl = new DragAndDropImpl();
- }
-
- public DragAndDrop asDragAndDrop() {
- return mImpl;
}
public void initialize(Optional<SplitScreenController> splitscreen) {
mSplitScreen = splitscreen.orElse(null);
mDisplayController.addDisplayWindowListener(this);
+ mShellController.addConfigurationChangeListener(this);
}
/** Adds a listener to be notified of drag and drop events. */
@@ -310,13 +312,15 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
return mimeTypes;
}
- private void onThemeChange() {
+ @Override
+ public void onThemeChanged() {
for (int i = 0; i < mDisplayDropTargets.size(); i++) {
mDisplayDropTargets.get(i).dragLayout.onThemeChange();
}
}
- private void onConfigChanged(Configuration newConfig) {
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
for (int i = 0; i < mDisplayDropTargets.size(); i++) {
mDisplayDropTargets.get(i).dragLayout.onConfigChanged(newConfig);
}
@@ -342,21 +346,4 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
dragLayout = dl;
}
}
-
- private class DragAndDropImpl implements DragAndDrop {
-
- @Override
- public void onThemeChanged() {
- mMainExecutor.execute(() -> {
- DragAndDropController.this.onThemeChange();
- });
- }
-
- @Override
- public void onConfigChanged(Configuration newConfig) {
- mMainExecutor.execute(() -> {
- DragAndDropController.this.onConfigChanged(newConfig);
- });
- }
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 756831007c35..435d8eaa563e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -45,12 +45,10 @@ import android.app.WindowConfiguration;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ClipDescription;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.LauncherApps;
-import android.content.pm.ResolveInfo;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Bundle;
@@ -64,11 +62,9 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.InstanceId;
-import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
-import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.lang.annotation.Retention;
@@ -267,50 +263,11 @@ public class DragAndDropPolicy {
mStarter.startShortcut(packageName, id, position, opts, user);
} else {
final PendingIntent launchIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
- mStarter.startIntent(launchIntent, getStartIntentFillInIntent(launchIntent, position),
- position, opts);
+ mStarter.startIntent(launchIntent, null /* fillIntent */, position, opts);
}
}
/**
- * Returns the fill-in intent to use when starting an app from a drop.
- */
- @VisibleForTesting
- Intent getStartIntentFillInIntent(PendingIntent launchIntent, @SplitPosition int position) {
- // Get the drag app
- final List<ResolveInfo> infos = launchIntent.queryIntentComponents(0 /* flags */);
- final ComponentName dragIntentActivity = !infos.isEmpty()
- ? infos.get(0).activityInfo.getComponentName()
- : null;
-
- // Get the current app (either fullscreen or the remaining app post-drop if in splitscreen)
- final boolean inSplitScreen = mSplitScreen != null
- && mSplitScreen.isSplitScreenVisible();
- final ComponentName currentActivity;
- if (!inSplitScreen) {
- currentActivity = mSession.runningTaskInfo != null
- ? mSession.runningTaskInfo.baseActivity
- : null;
- } else {
- final int nonReplacedSplitPosition = position == SPLIT_POSITION_TOP_OR_LEFT
- ? SPLIT_POSITION_BOTTOM_OR_RIGHT
- : SPLIT_POSITION_TOP_OR_LEFT;
- ActivityManager.RunningTaskInfo nonReplacedTaskInfo =
- mSplitScreen.getTaskInfo(nonReplacedSplitPosition);
- currentActivity = nonReplacedTaskInfo.baseActivity;
- }
-
- if (currentActivity.equals(dragIntentActivity)) {
- // Only apply MULTIPLE_TASK if we are dragging the same activity
- final Intent fillInIntent = new Intent();
- fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Adding MULTIPLE_TASK");
- return fillInIntent;
- }
- return null;
- }
-
- /**
* Per-drag session data.
*/
private static class DragSession {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index fef9be36a35f..692e6acb540c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -21,8 +21,6 @@ import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDO
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
-import android.graphics.Point;
-import android.graphics.Rect;
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
@@ -32,26 +30,35 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import java.io.PrintWriter;
/**
* {@link ShellTaskOrganizer.TaskListener} for {@link
* ShellTaskOrganizer#TASK_LISTENER_TYPE_FREEFORM}.
+ *
+ * @param <T> the type of window decoration instance
*/
-public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
+public class FreeformTaskListener<T extends AutoCloseable>
+ implements ShellTaskOrganizer.TaskListener {
private static final String TAG = "FreeformTaskListener";
+ private final WindowDecorViewModel<T> mWindowDecorationViewModel;
private final SyncTransactionQueue mSyncQueue;
- private final SparseArray<State> mTasks = new SparseArray<>();
+ private final SparseArray<State<T>> mTasks = new SparseArray<>();
- private static class State {
+ private static class State<T extends AutoCloseable> {
RunningTaskInfo mTaskInfo;
SurfaceControl mLeash;
+ T mWindowDecoration;
}
- public FreeformTaskListener(SyncTransactionQueue syncQueue) {
+ public FreeformTaskListener(
+ WindowDecorViewModel<T> windowDecorationViewModel,
+ SyncTransactionQueue syncQueue) {
+ mWindowDecorationViewModel = windowDecorationViewModel;
mSyncQueue = syncQueue;
}
@@ -62,23 +69,17 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Appeared: #%d",
taskInfo.taskId);
- final State state = new State();
+ final State<T> state = new State<>();
state.mTaskInfo = taskInfo;
state.mLeash = leash;
+ state.mWindowDecoration =
+ mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash);
mTasks.put(taskInfo.taskId, state);
-
- final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds();
- mSyncQueue.runInSync(t -> {
- Point taskPosition = taskInfo.positionInParent;
- t.setPosition(leash, taskPosition.x, taskPosition.y)
- .setWindowCrop(leash, taskBounds.width(), taskBounds.height())
- .show(leash);
- });
}
@Override
public void onTaskVanished(RunningTaskInfo taskInfo) {
- State state = mTasks.get(taskInfo.taskId);
+ State<T> state = mTasks.get(taskInfo.taskId);
if (state == null) {
Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
return;
@@ -86,11 +87,17 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d",
taskInfo.taskId);
mTasks.remove(taskInfo.taskId);
+
+ try {
+ state.mWindowDecoration.close();
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to release window decoration.", e);
+ }
}
@Override
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
- State state = mTasks.get(taskInfo.taskId);
+ State<T> state = mTasks.get(taskInfo.taskId);
if (state == null) {
throw new RuntimeException(
"Task info changed before appearing: #" + taskInfo.taskId);
@@ -98,15 +105,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d",
taskInfo.taskId);
state.mTaskInfo = taskInfo;
-
- final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds();
- final SurfaceControl leash = state.mLeash;
- mSyncQueue.runInSync(t -> {
- Point taskPosition = taskInfo.positionInParent;
- t.setPosition(leash, taskPosition.x, taskPosition.y)
- .setWindowCrop(leash, taskBounds.width(), taskBounds.height())
- .show(leash);
- });
+ mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index 73e6cba43ec0..79e363bcdb41 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -16,17 +16,13 @@
package com.android.wm.shell.fullscreen;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
import android.app.ActivityManager.RunningTaskInfo;
-import android.app.TaskInfo;
import android.graphics.Point;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
import android.view.SurfaceControl;
import androidx.annotation.NonNull;
@@ -48,22 +44,17 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
private static final String TAG = "FullscreenTaskListener";
private final SyncTransactionQueue mSyncQueue;
- private final FullscreenUnfoldController mFullscreenUnfoldController;
private final Optional<RecentTasksController> mRecentTasksOptional;
private final SparseArray<TaskData> mDataByTaskId = new SparseArray<>();
- private final AnimatableTasksListener mAnimatableTasksListener = new AnimatableTasksListener();
- public FullscreenTaskListener(SyncTransactionQueue syncQueue,
- Optional<FullscreenUnfoldController> unfoldController) {
- this(syncQueue, unfoldController, Optional.empty());
+ public FullscreenTaskListener(SyncTransactionQueue syncQueue) {
+ this(syncQueue, Optional.empty());
}
public FullscreenTaskListener(SyncTransactionQueue syncQueue,
- Optional<FullscreenUnfoldController> unfoldController,
Optional<RecentTasksController> recentTasks) {
mSyncQueue = syncQueue;
- mFullscreenUnfoldController = unfoldController.orElse(null);
mRecentTasksOptional = recentTasks;
}
@@ -76,6 +67,7 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
taskInfo.taskId);
final Point positionInParent = taskInfo.positionInParent;
mDataByTaskId.put(taskInfo.taskId, new TaskData(leash, positionInParent));
+
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
mSyncQueue.runInSync(t -> {
// Reset several properties back to fullscreen (PiP, for example, leaves all these
@@ -87,7 +79,6 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
t.show(leash);
});
- mAnimatableTasksListener.onTaskAppeared(taskInfo);
updateRecentsForVisibleFullscreenTask(taskInfo);
}
@@ -95,7 +86,6 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
- mAnimatableTasksListener.onTaskInfoChanged(taskInfo);
updateRecentsForVisibleFullscreenTask(taskInfo);
final TaskData data = mDataByTaskId.get(taskInfo.taskId);
@@ -115,7 +105,6 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
return;
}
- mAnimatableTasksListener.onTaskVanished(taskInfo);
mDataByTaskId.remove(taskInfo.taskId);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d",
@@ -173,65 +162,4 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
this.positionInParent = positionInParent;
}
}
-
- class AnimatableTasksListener {
- private final SparseBooleanArray mTaskIds = new SparseBooleanArray();
-
- public void onTaskAppeared(RunningTaskInfo taskInfo) {
- final boolean isApplicable = isAnimatable(taskInfo);
- if (isApplicable) {
- mTaskIds.put(taskInfo.taskId, true);
-
- if (mFullscreenUnfoldController != null) {
- SurfaceControl leash = mDataByTaskId.get(taskInfo.taskId).surface;
- mFullscreenUnfoldController.onTaskAppeared(taskInfo, leash);
- }
- }
- }
-
- public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
- final boolean isCurrentlyApplicable = mTaskIds.get(taskInfo.taskId);
- final boolean isApplicable = isAnimatable(taskInfo);
-
- if (isCurrentlyApplicable) {
- if (isApplicable) {
- // Still applicable, send update
- if (mFullscreenUnfoldController != null) {
- mFullscreenUnfoldController.onTaskInfoChanged(taskInfo);
- }
- } else {
- // Became inapplicable
- if (mFullscreenUnfoldController != null) {
- mFullscreenUnfoldController.onTaskVanished(taskInfo);
- }
- mTaskIds.put(taskInfo.taskId, false);
- }
- } else {
- if (isApplicable) {
- // Became applicable
- mTaskIds.put(taskInfo.taskId, true);
-
- if (mFullscreenUnfoldController != null) {
- SurfaceControl leash = mDataByTaskId.get(taskInfo.taskId).surface;
- mFullscreenUnfoldController.onTaskAppeared(taskInfo, leash);
- }
- }
- }
- }
-
- public void onTaskVanished(RunningTaskInfo taskInfo) {
- final boolean isCurrentlyApplicable = mTaskIds.get(taskInfo.taskId);
- if (isCurrentlyApplicable && mFullscreenUnfoldController != null) {
- mFullscreenUnfoldController.onTaskVanished(taskInfo);
- }
- mTaskIds.put(taskInfo.taskId, false);
- }
-
- private boolean isAnimatable(TaskInfo taskInfo) {
- // Filter all visible tasks that are not launcher tasks
- // We do not animate launcher as it handles the animation by itself
- return taskInfo != null && taskInfo.isVisible && taskInfo.getConfiguration()
- .windowConfiguration.getActivityType() != ACTIVITY_TYPE_HOME;
- }
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
index 23f76ca5f6ae..665b035bc41c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java
@@ -19,7 +19,6 @@ package com.android.wm.shell.hidedisplaycutout;
import android.content.Context;
import android.content.res.Configuration;
import android.os.SystemProperties;
-import android.util.Slog;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -27,20 +26,20 @@ import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.ShellController;
import java.io.PrintWriter;
-import java.util.concurrent.TimeUnit;
/**
* Manages the hide display cutout status.
*/
-public class HideDisplayCutoutController {
+public class HideDisplayCutoutController implements ConfigurationChangeListener {
private static final String TAG = "HideDisplayCutoutController";
private final Context mContext;
+ private final ShellController mShellController;
private final HideDisplayCutoutOrganizer mOrganizer;
- private final ShellExecutor mMainExecutor;
- private final HideDisplayCutoutImpl mImpl = new HideDisplayCutoutImpl();
@VisibleForTesting
boolean mEnabled;
@@ -49,8 +48,9 @@ public class HideDisplayCutoutController {
* supported.
*/
@Nullable
- public static HideDisplayCutoutController create(
- Context context, DisplayController displayController, ShellExecutor mainExecutor) {
+ public static HideDisplayCutoutController create(Context context,
+ ShellController shellController, DisplayController displayController,
+ ShellExecutor mainExecutor) {
// The SystemProperty is set for devices that support this feature and is used to control
// whether to create the HideDisplayCutout instance.
// It's defined in the device.mk (e.g. device/google/crosshatch/device.mk).
@@ -60,19 +60,16 @@ public class HideDisplayCutoutController {
HideDisplayCutoutOrganizer organizer =
new HideDisplayCutoutOrganizer(context, displayController, mainExecutor);
- return new HideDisplayCutoutController(context, organizer, mainExecutor);
+ return new HideDisplayCutoutController(context, shellController, organizer);
}
- HideDisplayCutoutController(Context context, HideDisplayCutoutOrganizer organizer,
- ShellExecutor mainExecutor) {
+ HideDisplayCutoutController(Context context, ShellController shellController,
+ HideDisplayCutoutOrganizer organizer) {
mContext = context;
+ mShellController = shellController;
mOrganizer = organizer;
- mMainExecutor = mainExecutor;
updateStatus();
- }
-
- public HideDisplayCutout asHideDisplayCutout() {
- return mImpl;
+ mShellController.addConfigurationChangeListener(this);
}
@VisibleForTesting
@@ -94,7 +91,8 @@ public class HideDisplayCutoutController {
}
}
- private void onConfigurationChanged(Configuration newConfig) {
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
updateStatus();
}
@@ -107,13 +105,4 @@ public class HideDisplayCutoutController {
pw.println(mEnabled);
mOrganizer.dump(pw);
}
-
- private class HideDisplayCutoutImpl implements HideDisplayCutout {
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- mMainExecutor.execute(() -> {
- HideDisplayCutoutController.this.onConfigurationChanged(newConfig);
- });
- }
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
index 3f7d78dda037..9478b347653f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
@@ -128,9 +128,10 @@ class HideDisplayCutoutOrganizer extends DisplayAreaOrganizer {
final WindowContainerTransaction wct = new WindowContainerTransaction();
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- applyBoundsAndOffsets(
- displayAreaInfo.token, mDisplayAreaMap.get(displayAreaInfo.token), wct, t);
+ final SurfaceControl leash = mDisplayAreaMap.get(displayAreaInfo.token);
+ applyBoundsAndOffsets(displayAreaInfo.token, leash, wct, t);
applyTransaction(wct, t);
+ leash.release();
mDisplayAreaMap.remove(displayAreaInfo.token);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
index b4c87b6cbf95..2c8ba0970ccc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
@@ -51,6 +51,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.startingsurface.StartingWindowController;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
import java.io.PrintWriter;
import java.util.List;
@@ -146,9 +147,11 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer {
SyncTransactionQueue syncTransactionQueue,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasks,
KidsModeSettingsObserver kidsModeSettingsObserver) {
- super(taskOrganizerController, mainExecutor, context, /* compatUI= */ null, recentTasks);
+ super(taskOrganizerController, mainExecutor, context, /* compatUI= */ null,
+ unfoldAnimationController, recentTasks);
mContext = context;
mMainHandler = mainHandler;
mSyncQueue = syncTransactionQueue;
@@ -164,8 +167,9 @@ public class KidsModeTaskOrganizer extends ShellTaskOrganizer {
SyncTransactionQueue syncTransactionQueue,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<RecentTasksController> recentTasks) {
- super(mainExecutor, context, /* compatUI= */ null, recentTasks);
+ super(mainExecutor, context, /* compatUI= */ null, unfoldAnimationController, recentTasks);
mContext = context;
mMainHandler = mainHandler;
mSyncQueue = syncTransactionQueue;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java
deleted file mode 100644
index aced072c8c71..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.legacysplitscreen;
-
-import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
-import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.Nullable;
-import android.graphics.Rect;
-import android.util.Slog;
-import android.view.Choreographer;
-import android.view.SurfaceControl;
-import android.window.TaskOrganizer;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
-
-class DividerImeController implements DisplayImeController.ImePositionProcessor {
- private static final String TAG = "DividerImeController";
- private static final boolean DEBUG = LegacySplitScreenController.DEBUG;
-
- private static final float ADJUSTED_NONFOCUS_DIM = 0.3f;
-
- private final LegacySplitScreenTaskListener mSplits;
- private final TransactionPool mTransactionPool;
- private final ShellExecutor mMainExecutor;
- private final TaskOrganizer mTaskOrganizer;
-
- /**
- * These are the y positions of the top of the IME surface when it is hidden and when it is
- * shown respectively. These are NOT necessarily the top of the visible IME itself.
- */
- private int mHiddenTop = 0;
- private int mShownTop = 0;
-
- // The following are target states (what we are curretly animating towards).
- /**
- * {@code true} if, at the end of the animation, the split task positions should be
- * adjusted by height of the IME. This happens when the secondary split is the IME target.
- */
- private boolean mTargetAdjusted = false;
- /**
- * {@code true} if, at the end of the animation, the IME should be shown/visible
- * regardless of what has focus.
- */
- private boolean mTargetShown = false;
- private float mTargetPrimaryDim = 0.f;
- private float mTargetSecondaryDim = 0.f;
-
- // The following are the current (most recent) states set during animation
- /** {@code true} if the secondary split has IME focus. */
- private boolean mSecondaryHasFocus = false;
- /** The dimming currently applied to the primary/secondary splits. */
- private float mLastPrimaryDim = 0.f;
- private float mLastSecondaryDim = 0.f;
- /** The most recent y position of the top of the IME surface */
- private int mLastAdjustTop = -1;
-
- // The following are states reached last time an animation fully completed.
- /** {@code true} if the IME was shown/visible by the last-completed animation. */
- private boolean mImeWasShown = false;
- /** {@code true} if the split positions were adjusted by the last-completed animation. */
- private boolean mAdjusted = false;
-
- /**
- * When some aspect of split-screen needs to animate independent from the IME,
- * this will be non-null and control split animation.
- */
- @Nullable
- private ValueAnimator mAnimation = null;
-
- private boolean mPaused = true;
- private boolean mPausedTargetAdjusted = false;
-
- DividerImeController(LegacySplitScreenTaskListener splits, TransactionPool pool,
- ShellExecutor mainExecutor, TaskOrganizer taskOrganizer) {
- mSplits = splits;
- mTransactionPool = pool;
- mMainExecutor = mainExecutor;
- mTaskOrganizer = taskOrganizer;
- }
-
- private DividerView getView() {
- return mSplits.mSplitScreenController.getDividerView();
- }
-
- private LegacySplitDisplayLayout getLayout() {
- return mSplits.mSplitScreenController.getSplitLayout();
- }
-
- private boolean isDividerHidden() {
- final DividerView view = mSplits.mSplitScreenController.getDividerView();
- return view == null || view.isHidden();
- }
-
- private boolean getSecondaryHasFocus(int displayId) {
- WindowContainerToken imeSplit = mTaskOrganizer.getImeTarget(displayId);
- return imeSplit != null
- && (imeSplit.asBinder() == mSplits.mSecondary.token.asBinder());
- }
-
- void reset() {
- mPaused = true;
- mPausedTargetAdjusted = false;
- mAnimation = null;
- mAdjusted = mTargetAdjusted = false;
- mImeWasShown = mTargetShown = false;
- mTargetPrimaryDim = mTargetSecondaryDim = mLastPrimaryDim = mLastSecondaryDim = 0.f;
- mSecondaryHasFocus = false;
- mLastAdjustTop = -1;
- }
-
- private void updateDimTargets() {
- final boolean splitIsVisible = !getView().isHidden();
- mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible)
- ? ADJUSTED_NONFOCUS_DIM : 0.f;
- mTargetSecondaryDim = (!mSecondaryHasFocus && mTargetShown && splitIsVisible)
- ? ADJUSTED_NONFOCUS_DIM : 0.f;
- }
-
-
- @Override
- public void onImeControlTargetChanged(int displayId, boolean controlling) {
- // Restore the split layout when wm-shell is not controlling IME insets anymore.
- if (!controlling && mTargetShown) {
- mPaused = false;
- mTargetAdjusted = mTargetShown = false;
- mTargetPrimaryDim = mTargetSecondaryDim = 0.f;
- updateImeAdjustState(true /* force */);
- startAsyncAnimation();
- }
- }
-
- @Override
- @ImeAnimationFlags
- public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
- boolean imeShouldShow, boolean imeIsFloating, SurfaceControl.Transaction t) {
- if (isDividerHidden()) {
- return 0;
- }
- mHiddenTop = hiddenTop;
- mShownTop = shownTop;
- mTargetShown = imeShouldShow;
- mSecondaryHasFocus = getSecondaryHasFocus(displayId);
- final boolean targetAdjusted = imeShouldShow && mSecondaryHasFocus
- && !imeIsFloating && !getLayout().mDisplayLayout.isLandscape()
- && !mSplits.mSplitScreenController.isMinimized();
- if (mLastAdjustTop < 0) {
- mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop;
- } else if (mLastAdjustTop != (imeShouldShow ? mShownTop : mHiddenTop)) {
- if (mTargetAdjusted != targetAdjusted && targetAdjusted == mAdjusted) {
- // Check for an "interruption" of an existing animation. In this case, we
- // need to fake-flip the last-known state direction so that the animation
- // completes in the other direction.
- mAdjusted = mTargetAdjusted;
- } else if (targetAdjusted && mTargetAdjusted && mAdjusted) {
- // Already fully adjusted for IME, but IME height has changed; so, force-start
- // an async animation to the new IME height.
- mAdjusted = false;
- }
- }
- if (mPaused) {
- mPausedTargetAdjusted = targetAdjusted;
- if (DEBUG) Slog.d(TAG, " ime starting but paused " + dumpState());
- return (targetAdjusted || mAdjusted) ? IME_ANIMATION_NO_ALPHA : 0;
- }
- mTargetAdjusted = targetAdjusted;
- updateDimTargets();
- if (DEBUG) Slog.d(TAG, " ime starting. " + dumpState());
- if (mAnimation != null || (mImeWasShown && imeShouldShow
- && mTargetAdjusted != mAdjusted)) {
- // We need to animate adjustment independently of the IME position, so
- // start our own animation to drive adjustment. This happens when a
- // different split's editor has gained focus while the IME is still visible.
- startAsyncAnimation();
- }
- updateImeAdjustState();
-
- return (mTargetAdjusted || mAdjusted) ? IME_ANIMATION_NO_ALPHA : 0;
- }
-
- private void updateImeAdjustState() {
- updateImeAdjustState(false /* force */);
- }
-
- private void updateImeAdjustState(boolean force) {
- if (mAdjusted != mTargetAdjusted || force) {
- // Reposition the server's secondary split position so that it evaluates
- // insets properly.
- WindowContainerTransaction wct = new WindowContainerTransaction();
- final LegacySplitDisplayLayout splitLayout = getLayout();
- if (mTargetAdjusted) {
- splitLayout.updateAdjustedBounds(mShownTop, mHiddenTop, mShownTop);
- wct.setBounds(mSplits.mSecondary.token, splitLayout.mAdjustedSecondary);
- // "Freeze" the configuration size so that the app doesn't get a config
- // or relaunch. This is required because normally nav-bar contributes
- // to configuration bounds (via nondecorframe).
- Rect adjustAppBounds = new Rect(mSplits.mSecondary.configuration
- .windowConfiguration.getAppBounds());
- adjustAppBounds.offset(0, splitLayout.mAdjustedSecondary.top
- - splitLayout.mSecondary.top);
- wct.setAppBounds(mSplits.mSecondary.token, adjustAppBounds);
- wct.setScreenSizeDp(mSplits.mSecondary.token,
- mSplits.mSecondary.configuration.screenWidthDp,
- mSplits.mSecondary.configuration.screenHeightDp);
-
- wct.setBounds(mSplits.mPrimary.token, splitLayout.mAdjustedPrimary);
- adjustAppBounds = new Rect(mSplits.mPrimary.configuration
- .windowConfiguration.getAppBounds());
- adjustAppBounds.offset(0, splitLayout.mAdjustedPrimary.top
- - splitLayout.mPrimary.top);
- wct.setAppBounds(mSplits.mPrimary.token, adjustAppBounds);
- wct.setScreenSizeDp(mSplits.mPrimary.token,
- mSplits.mPrimary.configuration.screenWidthDp,
- mSplits.mPrimary.configuration.screenHeightDp);
- } else {
- wct.setBounds(mSplits.mSecondary.token, splitLayout.mSecondary);
- wct.setAppBounds(mSplits.mSecondary.token, null);
- wct.setScreenSizeDp(mSplits.mSecondary.token,
- SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
- wct.setBounds(mSplits.mPrimary.token, splitLayout.mPrimary);
- wct.setAppBounds(mSplits.mPrimary.token, null);
- wct.setScreenSizeDp(mSplits.mPrimary.token,
- SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
- }
-
- if (!mSplits.mSplitScreenController.getWmProxy().queueSyncTransactionIfWaiting(wct)) {
- mTaskOrganizer.applyTransaction(wct);
- }
- }
-
- // Update all the adjusted-for-ime states
- if (!mPaused) {
- final DividerView view = getView();
- if (view != null) {
- view.setAdjustedForIme(mTargetShown, mTargetShown
- ? DisplayImeController.ANIMATION_DURATION_SHOW_MS
- : DisplayImeController.ANIMATION_DURATION_HIDE_MS);
- }
- }
- mSplits.mSplitScreenController.setAdjustedForIme(mTargetShown && !mPaused);
- }
-
- @Override
- public void onImePositionChanged(int displayId, int imeTop,
- SurfaceControl.Transaction t) {
- if (mAnimation != null || isDividerHidden() || mPaused) {
- // Not synchronized with IME anymore, so return.
- return;
- }
- final float fraction = ((float) imeTop - mHiddenTop) / (mShownTop - mHiddenTop);
- final float progress = mTargetShown ? fraction : 1.f - fraction;
- onProgress(progress, t);
- }
-
- @Override
- public void onImeEndPositioning(int displayId, boolean cancelled,
- SurfaceControl.Transaction t) {
- if (mAnimation != null || isDividerHidden() || mPaused) {
- // Not synchronized with IME anymore, so return.
- return;
- }
- onEnd(cancelled, t);
- }
-
- private void onProgress(float progress, SurfaceControl.Transaction t) {
- final DividerView view = getView();
- if (mTargetAdjusted != mAdjusted && !mPaused) {
- final LegacySplitDisplayLayout splitLayout = getLayout();
- final float fraction = mTargetAdjusted ? progress : 1.f - progress;
- mLastAdjustTop = (int) (fraction * mShownTop + (1.f - fraction) * mHiddenTop);
- splitLayout.updateAdjustedBounds(mLastAdjustTop, mHiddenTop, mShownTop);
- view.resizeSplitSurfaces(t, splitLayout.mAdjustedPrimary,
- splitLayout.mAdjustedSecondary);
- }
- final float invProg = 1.f - progress;
- view.setResizeDimLayer(t, true /* primary */,
- mLastPrimaryDim * invProg + progress * mTargetPrimaryDim);
- view.setResizeDimLayer(t, false /* primary */,
- mLastSecondaryDim * invProg + progress * mTargetSecondaryDim);
- }
-
- void setDimsHidden(SurfaceControl.Transaction t, boolean hidden) {
- final DividerView view = getView();
- if (hidden) {
- view.setResizeDimLayer(t, true /* primary */, 0.f /* alpha */);
- view.setResizeDimLayer(t, false /* primary */, 0.f /* alpha */);
- } else {
- updateDimTargets();
- view.setResizeDimLayer(t, true /* primary */, mTargetPrimaryDim);
- view.setResizeDimLayer(t, false /* primary */, mTargetSecondaryDim);
- }
- }
-
- private void onEnd(boolean cancelled, SurfaceControl.Transaction t) {
- if (!cancelled) {
- onProgress(1.f, t);
- mAdjusted = mTargetAdjusted;
- mImeWasShown = mTargetShown;
- mLastAdjustTop = mAdjusted ? mShownTop : mHiddenTop;
- mLastPrimaryDim = mTargetPrimaryDim;
- mLastSecondaryDim = mTargetSecondaryDim;
- }
- }
-
- private void startAsyncAnimation() {
- if (mAnimation != null) {
- mAnimation.cancel();
- }
- mAnimation = ValueAnimator.ofFloat(0.f, 1.f);
- mAnimation.setDuration(DisplayImeController.ANIMATION_DURATION_SHOW_MS);
- if (mTargetAdjusted != mAdjusted) {
- final float fraction =
- ((float) mLastAdjustTop - mHiddenTop) / (mShownTop - mHiddenTop);
- final float progress = mTargetAdjusted ? fraction : 1.f - fraction;
- mAnimation.setCurrentFraction(progress);
- }
-
- mAnimation.addUpdateListener(animation -> {
- SurfaceControl.Transaction t = mTransactionPool.acquire();
- float value = (float) animation.getAnimatedValue();
- onProgress(value, t);
- t.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
- t.apply();
- mTransactionPool.release(t);
- });
- mAnimation.setInterpolator(DisplayImeController.INTERPOLATOR);
- mAnimation.addListener(new AnimatorListenerAdapter() {
- private boolean mCancel = false;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancel = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- SurfaceControl.Transaction t = mTransactionPool.acquire();
- onEnd(mCancel, t);
- t.apply();
- mTransactionPool.release(t);
- mAnimation = null;
- }
- });
- mAnimation.start();
- }
-
- private String dumpState() {
- return "top:" + mHiddenTop + "->" + mShownTop
- + " adj:" + mAdjusted + "->" + mTargetAdjusted + "(" + mLastAdjustTop + ")"
- + " shw:" + mImeWasShown + "->" + mTargetShown
- + " dims:" + mLastPrimaryDim + "," + mLastSecondaryDim
- + "->" + mTargetPrimaryDim + "," + mTargetSecondaryDim
- + " shf:" + mSecondaryHasFocus + " desync:" + (mAnimation != null)
- + " paus:" + mPaused + "[" + mPausedTargetAdjusted + "]";
- }
-
- /** Completely aborts/resets adjustment state */
- public void pause(int displayId) {
- if (DEBUG) Slog.d(TAG, "ime pause posting " + dumpState());
- mMainExecutor.execute(() -> {
- if (DEBUG) Slog.d(TAG, "ime pause run posted " + dumpState());
- if (mPaused) {
- return;
- }
- mPaused = true;
- mPausedTargetAdjusted = mTargetAdjusted;
- mTargetAdjusted = false;
- mTargetPrimaryDim = mTargetSecondaryDim = 0.f;
- updateImeAdjustState();
- startAsyncAnimation();
- if (mAnimation != null) {
- mAnimation.end();
- }
- });
- }
-
- public void resume(int displayId) {
- if (DEBUG) Slog.d(TAG, "ime resume posting " + dumpState());
- mMainExecutor.execute(() -> {
- if (DEBUG) Slog.d(TAG, "ime resume run posted " + dumpState());
- if (!mPaused) {
- return;
- }
- mPaused = false;
- mTargetAdjusted = mPausedTargetAdjusted;
- updateDimTargets();
- final DividerView view = getView();
- if ((mTargetAdjusted != mAdjusted) && !mSplits.mSplitScreenController.isMinimized()
- && view != null) {
- // End unminimize animations since they conflict with adjustment animations.
- view.finishAnimations();
- }
- updateImeAdjustState();
- startAsyncAnimation();
- });
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
deleted file mode 100644
index 73be2835d2cd..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ /dev/null
@@ -1,1314 +0,0 @@
-/*
- * 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.wm.shell.legacysplitscreen;
-
-import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
-import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
-import static android.view.WindowManager.DOCKED_RIGHT;
-
-import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR;
-import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
-import static com.android.wm.shell.common.split.DividerView.TOUCH_ANIMATION_DURATION;
-import static com.android.wm.shell.common.split.DividerView.TOUCH_RELEASE_ANIMATION_DURATION;
-
-import android.animation.AnimationHandler;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.Region.Op;
-import android.hardware.display.DisplayManager;
-import android.os.Bundle;
-import android.util.AttributeSet;
-import android.util.Slog;
-import android.view.Choreographer;
-import android.view.Display;
-import android.view.MotionEvent;
-import android.view.PointerIcon;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.View.OnTouchListener;
-import android.view.ViewConfiguration;
-import android.view.ViewTreeObserver.InternalInsetsInfo;
-import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-import android.widget.FrameLayout;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
-import com.android.internal.policy.DockedDividerUtils;
-import com.android.wm.shell.R;
-import com.android.wm.shell.animation.FlingAnimationUtils;
-import com.android.wm.shell.animation.Interpolators;
-import com.android.wm.shell.common.split.DividerHandleView;
-
-import java.util.function.Consumer;
-
-/**
- * Docked stack divider.
- */
-public class DividerView extends FrameLayout implements OnTouchListener,
- OnComputeInternalInsetsListener {
- private static final String TAG = "DividerView";
- private static final boolean DEBUG = LegacySplitScreenController.DEBUG;
-
- interface DividerCallbacks {
- void onDraggingStart();
- void onDraggingEnd();
- }
-
- public static final int INVALID_RECENTS_GROW_TARGET = -1;
-
- private static final int LOG_VALUE_RESIZE_50_50 = 0;
- private static final int LOG_VALUE_RESIZE_DOCKED_SMALLER = 1;
- private static final int LOG_VALUE_RESIZE_DOCKED_LARGER = 2;
-
- private static final int LOG_VALUE_UNDOCK_MAX_DOCKED = 0;
- private static final int LOG_VALUE_UNDOCK_MAX_OTHER = 1;
-
- private static final int TASK_POSITION_SAME = Integer.MAX_VALUE;
-
- /**
- * How much the background gets scaled when we are in the minimized dock state.
- */
- private static final float MINIMIZE_DOCK_SCALE = 0f;
- private static final float ADJUSTED_FOR_IME_SCALE = 0.5f;
-
- private static final Interpolator IME_ADJUST_INTERPOLATOR =
- new PathInterpolator(0.2f, 0f, 0.1f, 1f);
-
- private DividerHandleView mHandle;
- private View mBackground;
- private MinimizedDockShadow mMinimizedShadow;
- private int mStartX;
- private int mStartY;
- private int mStartPosition;
- private int mDockSide;
- private boolean mMoving;
- private int mTouchSlop;
- private boolean mBackgroundLifted;
- private boolean mIsInMinimizeInteraction;
- SnapTarget mSnapTargetBeforeMinimized;
-
- private int mDividerInsets;
- private final Display mDefaultDisplay;
-
- private int mDividerSize;
- private int mTouchElevation;
- private int mLongPressEntraceAnimDuration;
-
- private final Rect mDockedRect = new Rect();
- private final Rect mDockedTaskRect = new Rect();
- private final Rect mOtherTaskRect = new Rect();
- private final Rect mOtherRect = new Rect();
- private final Rect mDockedInsetRect = new Rect();
- private final Rect mOtherInsetRect = new Rect();
- private final Rect mLastResizeRect = new Rect();
- private final Rect mTmpRect = new Rect();
- private LegacySplitScreenController mSplitScreenController;
- private WindowManagerProxy mWindowManagerProxy;
- private DividerWindowManager mWindowManager;
- private VelocityTracker mVelocityTracker;
- private FlingAnimationUtils mFlingAnimationUtils;
- private LegacySplitDisplayLayout mSplitLayout;
- private DividerImeController mImeController;
- private DividerCallbacks mCallback;
-
- private AnimationHandler mSfVsyncAnimationHandler;
- private ValueAnimator mCurrentAnimator;
- private boolean mEntranceAnimationRunning;
- private boolean mExitAnimationRunning;
- private int mExitStartPosition;
- private boolean mDockedStackMinimized;
- private boolean mHomeStackResizable;
- private boolean mAdjustedForIme;
- private DividerState mState;
-
- private LegacySplitScreenTaskListener mTiles;
- boolean mFirstLayout = true;
- int mDividerPositionX;
- int mDividerPositionY;
-
- private final Matrix mTmpMatrix = new Matrix();
- private final float[] mTmpValues = new float[9];
-
- // The view is removed or in the process of been removed from the system.
- private boolean mRemoved;
-
- // Whether the surface for this view has been hidden regardless of actual visibility. This is
- // used interact with keyguard.
- private boolean mSurfaceHidden = false;
-
- private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
- @Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- final DividerSnapAlgorithm snapAlgorithm = getSnapAlgorithm();
- if (isHorizontalDivision()) {
- info.addAction(new AccessibilityAction(R.id.action_move_tl_full,
- mContext.getString(R.string.accessibility_action_divider_top_full)));
- if (snapAlgorithm.isFirstSplitTargetAvailable()) {
- info.addAction(new AccessibilityAction(R.id.action_move_tl_70,
- mContext.getString(R.string.accessibility_action_divider_top_70)));
- }
- if (snapAlgorithm.showMiddleSplitTargetForAccessibility()) {
- // Only show the middle target if there are more than 1 split target
- info.addAction(new AccessibilityAction(R.id.action_move_tl_50,
- mContext.getString(R.string.accessibility_action_divider_top_50)));
- }
- if (snapAlgorithm.isLastSplitTargetAvailable()) {
- info.addAction(new AccessibilityAction(R.id.action_move_tl_30,
- mContext.getString(R.string.accessibility_action_divider_top_30)));
- }
- info.addAction(new AccessibilityAction(R.id.action_move_rb_full,
- mContext.getString(R.string.accessibility_action_divider_bottom_full)));
- } else {
- info.addAction(new AccessibilityAction(R.id.action_move_tl_full,
- mContext.getString(R.string.accessibility_action_divider_left_full)));
- if (snapAlgorithm.isFirstSplitTargetAvailable()) {
- info.addAction(new AccessibilityAction(R.id.action_move_tl_70,
- mContext.getString(R.string.accessibility_action_divider_left_70)));
- }
- if (snapAlgorithm.showMiddleSplitTargetForAccessibility()) {
- // Only show the middle target if there are more than 1 split target
- info.addAction(new AccessibilityAction(R.id.action_move_tl_50,
- mContext.getString(R.string.accessibility_action_divider_left_50)));
- }
- if (snapAlgorithm.isLastSplitTargetAvailable()) {
- info.addAction(new AccessibilityAction(R.id.action_move_tl_30,
- mContext.getString(R.string.accessibility_action_divider_left_30)));
- }
- info.addAction(new AccessibilityAction(R.id.action_move_rb_full,
- mContext.getString(R.string.accessibility_action_divider_right_full)));
- }
- }
-
- @Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
- int currentPosition = getCurrentPosition();
- SnapTarget nextTarget = null;
- DividerSnapAlgorithm snapAlgorithm = mSplitLayout.getSnapAlgorithm();
- if (action == R.id.action_move_tl_full) {
- nextTarget = snapAlgorithm.getDismissEndTarget();
- } else if (action == R.id.action_move_tl_70) {
- nextTarget = snapAlgorithm.getLastSplitTarget();
- } else if (action == R.id.action_move_tl_50) {
- nextTarget = snapAlgorithm.getMiddleTarget();
- } else if (action == R.id.action_move_tl_30) {
- nextTarget = snapAlgorithm.getFirstSplitTarget();
- } else if (action == R.id.action_move_rb_full) {
- nextTarget = snapAlgorithm.getDismissStartTarget();
- }
- if (nextTarget != null) {
- startDragging(true /* animate */, false /* touching */);
- stopDragging(currentPosition, nextTarget, 250, Interpolators.FAST_OUT_SLOW_IN);
- return true;
- }
- return super.performAccessibilityAction(host, action, args);
- }
- };
-
- private final Runnable mResetBackgroundRunnable = new Runnable() {
- @Override
- public void run() {
- resetBackground();
- }
- };
-
- public DividerView(Context context) {
- this(context, null);
- }
-
- public DividerView(Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- final DisplayManager displayManager =
- (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
- mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
- }
-
- public void setAnimationHandler(AnimationHandler sfVsyncAnimationHandler) {
- mSfVsyncAnimationHandler = sfVsyncAnimationHandler;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mHandle = findViewById(R.id.docked_divider_handle);
- mBackground = findViewById(R.id.docked_divider_background);
- mMinimizedShadow = findViewById(R.id.minimized_dock_shadow);
- mHandle.setOnTouchListener(this);
- final int dividerWindowWidth = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_thickness);
- mDividerInsets = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_insets);
- mDividerSize = dividerWindowWidth - 2 * mDividerInsets;
- mTouchElevation = getResources().getDimensionPixelSize(
- R.dimen.docked_stack_divider_lift_elevation);
- mLongPressEntraceAnimDuration = getResources().getInteger(
- R.integer.long_press_dock_anim_duration);
- mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
- mFlingAnimationUtils = new FlingAnimationUtils(getResources().getDisplayMetrics(), 0.3f);
- boolean landscape = getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
- mHandle.setPointerIcon(PointerIcon.getSystemIcon(getContext(),
- landscape ? TYPE_HORIZONTAL_DOUBLE_ARROW : TYPE_VERTICAL_DOUBLE_ARROW));
- getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- mHandle.setAccessibilityDelegate(mHandleDelegate);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- // Save the current target if not minimized once attached to window
- if (mDockSide != WindowManager.DOCKED_INVALID && !mIsInMinimizeInteraction) {
- saveSnapTargetBeforeMinimized(mSnapTargetBeforeMinimized);
- }
- mFirstLayout = true;
- }
-
- void onDividerRemoved() {
- mRemoved = true;
- mCallback = null;
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (mFirstLayout) {
- // Wait for first layout so that the ViewRootImpl surface has been created.
- initializeSurfaceState();
- mFirstLayout = false;
- }
- int minimizeLeft = 0;
- int minimizeTop = 0;
- if (mDockSide == WindowManager.DOCKED_TOP) {
- minimizeTop = mBackground.getTop();
- } else if (mDockSide == WindowManager.DOCKED_LEFT) {
- minimizeLeft = mBackground.getLeft();
- } else if (mDockSide == WindowManager.DOCKED_RIGHT) {
- minimizeLeft = mBackground.getRight() - mMinimizedShadow.getWidth();
- }
- mMinimizedShadow.layout(minimizeLeft, minimizeTop,
- minimizeLeft + mMinimizedShadow.getMeasuredWidth(),
- minimizeTop + mMinimizedShadow.getMeasuredHeight());
- if (changed) {
- notifySplitScreenBoundsChanged();
- }
- }
-
- void injectDependencies(LegacySplitScreenController splitScreenController,
- DividerWindowManager windowManager, DividerState dividerState,
- DividerCallbacks callback, LegacySplitScreenTaskListener tiles,
- LegacySplitDisplayLayout sdl, DividerImeController imeController,
- WindowManagerProxy wmProxy) {
- mSplitScreenController = splitScreenController;
- mWindowManager = windowManager;
- mState = dividerState;
- mCallback = callback;
- mTiles = tiles;
- mSplitLayout = sdl;
- mImeController = imeController;
- mWindowManagerProxy = wmProxy;
-
- if (mState.mRatioPositionBeforeMinimized == 0) {
- // Set the middle target as the initial state
- mSnapTargetBeforeMinimized = mSplitLayout.getSnapAlgorithm().getMiddleTarget();
- } else {
- repositionSnapTargetBeforeMinimized();
- }
- }
-
- /** Gets non-minimized secondary bounds of split screen. */
- public Rect getNonMinimizedSplitScreenSecondaryBounds() {
- mOtherTaskRect.set(mSplitLayout.mSecondary);
- return mOtherTaskRect;
- }
-
- private boolean inSplitMode() {
- return getVisibility() == VISIBLE;
- }
-
- /** Unlike setVisible, this directly hides the surface without changing view visibility. */
- void setHidden(boolean hidden) {
- if (mSurfaceHidden == hidden) {
- return;
- }
- mSurfaceHidden = hidden;
- post(() -> {
- final SurfaceControl sc = getWindowSurfaceControl();
- if (sc == null) {
- return;
- }
- Transaction t = mTiles.getTransaction();
- if (hidden) {
- t.hide(sc);
- } else {
- t.show(sc);
- }
- mImeController.setDimsHidden(t, hidden);
- t.apply();
- mTiles.releaseTransaction(t);
- });
- }
-
- boolean isHidden() {
- return getVisibility() != View.VISIBLE || mSurfaceHidden;
- }
-
- /** Starts dragging the divider bar. */
- public boolean startDragging(boolean animate, boolean touching) {
- cancelFlingAnimation();
- if (touching) {
- mHandle.setTouching(true, animate);
- }
- mDockSide = mSplitLayout.getPrimarySplitSide();
-
- mWindowManagerProxy.setResizing(true);
- if (touching) {
- mWindowManager.setSlippery(false);
- liftBackground();
- }
- if (mCallback != null) {
- mCallback.onDraggingStart();
- }
- return inSplitMode();
- }
-
- /** Stops dragging the divider bar. */
- public void stopDragging(int position, float velocity, boolean avoidDismissStart,
- boolean logMetrics) {
- mHandle.setTouching(false, true /* animate */);
- fling(position, velocity, avoidDismissStart, logMetrics);
- mWindowManager.setSlippery(true);
- releaseBackground();
- }
-
- private void stopDragging(int position, SnapTarget target, long duration,
- Interpolator interpolator) {
- stopDragging(position, target, duration, 0 /* startDelay*/, 0 /* endDelay */, interpolator);
- }
-
- private void stopDragging(int position, SnapTarget target, long duration,
- Interpolator interpolator, long endDelay) {
- stopDragging(position, target, duration, 0 /* startDelay*/, endDelay, interpolator);
- }
-
- private void stopDragging(int position, SnapTarget target, long duration, long startDelay,
- long endDelay, Interpolator interpolator) {
- mHandle.setTouching(false, true /* animate */);
- flingTo(position, target, duration, startDelay, endDelay, interpolator);
- mWindowManager.setSlippery(true);
- releaseBackground();
- }
-
- private void stopDragging() {
- mHandle.setTouching(false, true /* animate */);
- mWindowManager.setSlippery(true);
- mWindowManagerProxy.setResizing(false);
- releaseBackground();
- }
-
- private void updateDockSide() {
- mDockSide = mSplitLayout.getPrimarySplitSide();
- mMinimizedShadow.setDockSide(mDockSide);
- }
-
- public DividerSnapAlgorithm getSnapAlgorithm() {
- return mDockedStackMinimized ? mSplitLayout.getMinimizedSnapAlgorithm(mHomeStackResizable)
- : mSplitLayout.getSnapAlgorithm();
- }
-
- public int getCurrentPosition() {
- return isHorizontalDivision() ? mDividerPositionY : mDividerPositionX;
- }
-
- public boolean isMinimized() {
- return mDockedStackMinimized;
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- convertToScreenCoordinates(event);
- final int action = event.getAction() & MotionEvent.ACTION_MASK;
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mVelocityTracker = VelocityTracker.obtain();
- mVelocityTracker.addMovement(event);
- mStartX = (int) event.getX();
- mStartY = (int) event.getY();
- boolean result = startDragging(true /* animate */, true /* touching */);
- if (!result) {
-
- // Weren't able to start dragging successfully, so cancel it again.
- stopDragging();
- }
- mStartPosition = getCurrentPosition();
- mMoving = false;
- return result;
- case MotionEvent.ACTION_MOVE:
- mVelocityTracker.addMovement(event);
- int x = (int) event.getX();
- int y = (int) event.getY();
- boolean exceededTouchSlop =
- isHorizontalDivision() && Math.abs(y - mStartY) > mTouchSlop
- || (!isHorizontalDivision() && Math.abs(x - mStartX) > mTouchSlop);
- if (!mMoving && exceededTouchSlop) {
- mStartX = x;
- mStartY = y;
- mMoving = true;
- }
- if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) {
- SnapTarget snapTarget = getSnapAlgorithm().calculateSnapTarget(
- mStartPosition, 0 /* velocity */, false /* hardDismiss */);
- resizeStackSurfaces(calculatePosition(x, y), mStartPosition, snapTarget,
- null /* transaction */);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (!mMoving) {
- stopDragging();
- break;
- }
-
- x = (int) event.getRawX();
- y = (int) event.getRawY();
- mVelocityTracker.addMovement(event);
- mVelocityTracker.computeCurrentVelocity(1000);
- int position = calculatePosition(x, y);
- stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity()
- : mVelocityTracker.getXVelocity(), false /* avoidDismissStart */,
- true /* log */);
- mMoving = false;
- break;
- }
- return true;
- }
-
- private void logResizeEvent(SnapTarget snapTarget) {
- if (snapTarget == mSplitLayout.getSnapAlgorithm().getDismissStartTarget()) {
- MetricsLogger.action(
- mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideTopLeft(mDockSide)
- ? LOG_VALUE_UNDOCK_MAX_OTHER
- : LOG_VALUE_UNDOCK_MAX_DOCKED);
- } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getDismissEndTarget()) {
- MetricsLogger.action(
- mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideBottomRight(mDockSide)
- ? LOG_VALUE_UNDOCK_MAX_OTHER
- : LOG_VALUE_UNDOCK_MAX_DOCKED);
- } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getMiddleTarget()) {
- MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
- LOG_VALUE_RESIZE_50_50);
- } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getFirstSplitTarget()) {
- MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
- dockSideTopLeft(mDockSide)
- ? LOG_VALUE_RESIZE_DOCKED_SMALLER
- : LOG_VALUE_RESIZE_DOCKED_LARGER);
- } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getLastSplitTarget()) {
- MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
- dockSideTopLeft(mDockSide)
- ? LOG_VALUE_RESIZE_DOCKED_LARGER
- : LOG_VALUE_RESIZE_DOCKED_SMALLER);
- }
- }
-
- private void convertToScreenCoordinates(MotionEvent event) {
- event.setLocation(event.getRawX(), event.getRawY());
- }
-
- private void fling(int position, float velocity, boolean avoidDismissStart,
- boolean logMetrics) {
- DividerSnapAlgorithm currentSnapAlgorithm = getSnapAlgorithm();
- SnapTarget snapTarget = currentSnapAlgorithm.calculateSnapTarget(position, velocity);
- if (avoidDismissStart && snapTarget == currentSnapAlgorithm.getDismissStartTarget()) {
- snapTarget = currentSnapAlgorithm.getFirstSplitTarget();
- }
- if (logMetrics) {
- logResizeEvent(snapTarget);
- }
- ValueAnimator anim = getFlingAnimator(position, snapTarget, 0 /* endDelay */);
- mFlingAnimationUtils.apply(anim, position, snapTarget.position, velocity);
- anim.start();
- }
-
- private void flingTo(int position, SnapTarget target, long duration, long startDelay,
- long endDelay, Interpolator interpolator) {
- ValueAnimator anim = getFlingAnimator(position, target, endDelay);
- anim.setDuration(duration);
- anim.setStartDelay(startDelay);
- anim.setInterpolator(interpolator);
- anim.start();
- }
-
- private ValueAnimator getFlingAnimator(int position, final SnapTarget snapTarget,
- final long endDelay) {
- if (mCurrentAnimator != null) {
- cancelFlingAnimation();
- updateDockSide();
- }
- if (DEBUG) Slog.d(TAG, "Getting fling " + position + "->" + snapTarget.position);
- final boolean taskPositionSameAtEnd = snapTarget.flag == SnapTarget.FLAG_NONE;
- ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
- anim.addUpdateListener(animation -> resizeStackSurfaces((int) animation.getAnimatedValue(),
- taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f
- ? TASK_POSITION_SAME
- : snapTarget.taskPosition,
- snapTarget, null /* transaction */));
- Consumer<Boolean> endAction = cancelled -> {
- if (DEBUG) Slog.d(TAG, "End Fling " + cancelled + " min:" + mIsInMinimizeInteraction);
- final boolean wasMinimizeInteraction = mIsInMinimizeInteraction;
- // Reset minimized divider position after unminimized state animation finishes.
- if (!cancelled && !mDockedStackMinimized && mIsInMinimizeInteraction) {
- mIsInMinimizeInteraction = false;
- }
- boolean dismissed = commitSnapFlags(snapTarget);
- mWindowManagerProxy.setResizing(false);
- updateDockSide();
- mCurrentAnimator = null;
- mEntranceAnimationRunning = false;
- mExitAnimationRunning = false;
- if (!dismissed && !wasMinimizeInteraction) {
- mWindowManagerProxy.applyResizeSplits(snapTarget.position, mSplitLayout);
- }
- if (mCallback != null) {
- mCallback.onDraggingEnd();
- }
-
- // Record last snap target the divider moved to
- if (!mIsInMinimizeInteraction) {
- // The last snapTarget position can be negative when the last divider position was
- // offscreen. In that case, save the middle (default) SnapTarget so calculating next
- // position isn't negative.
- final SnapTarget saveTarget;
- if (snapTarget.position < 0) {
- saveTarget = mSplitLayout.getSnapAlgorithm().getMiddleTarget();
- } else {
- saveTarget = snapTarget;
- }
- final DividerSnapAlgorithm snapAlgo = mSplitLayout.getSnapAlgorithm();
- if (saveTarget.position != snapAlgo.getDismissEndTarget().position
- && saveTarget.position != snapAlgo.getDismissStartTarget().position) {
- saveSnapTargetBeforeMinimized(saveTarget);
- }
- }
- notifySplitScreenBoundsChanged();
- };
- anim.addListener(new AnimatorListenerAdapter() {
-
- private boolean mCancelled;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- long delay = 0;
- if (endDelay != 0) {
- delay = endDelay;
- } else if (mCancelled) {
- delay = 0;
- }
- if (delay == 0) {
- endAction.accept(mCancelled);
- } else {
- final Boolean cancelled = mCancelled;
- if (DEBUG) Slog.d(TAG, "Posting endFling " + cancelled + " d:" + delay + "ms");
- getHandler().postDelayed(() -> endAction.accept(cancelled), delay);
- }
- }
- });
- mCurrentAnimator = anim;
- mCurrentAnimator.setAnimationHandler(mSfVsyncAnimationHandler);
- return anim;
- }
-
- private void notifySplitScreenBoundsChanged() {
- if (mSplitLayout.mPrimary == null || mSplitLayout.mSecondary == null) {
- return;
- }
- mOtherTaskRect.set(mSplitLayout.mSecondary);
-
- mTmpRect.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(), mHandle.getBottom());
- if (isHorizontalDivision()) {
- mTmpRect.offsetTo(mHandle.getLeft(), mDividerPositionY);
- } else {
- mTmpRect.offsetTo(mDividerPositionX, mHandle.getTop());
- }
- mWindowManagerProxy.setTouchRegion(mTmpRect);
-
- mTmpRect.set(mSplitLayout.mDisplayLayout.stableInsets());
- switch (mSplitLayout.getPrimarySplitSide()) {
- case WindowManager.DOCKED_LEFT:
- mTmpRect.left = 0;
- break;
- case WindowManager.DOCKED_RIGHT:
- mTmpRect.right = 0;
- break;
- case WindowManager.DOCKED_TOP:
- mTmpRect.top = 0;
- break;
- }
- mSplitScreenController.notifyBoundsChanged(mOtherTaskRect, mTmpRect);
- }
-
- private void cancelFlingAnimation() {
- if (mCurrentAnimator != null) {
- mCurrentAnimator.cancel();
- }
- }
-
- private boolean commitSnapFlags(SnapTarget target) {
- if (target.flag == SnapTarget.FLAG_NONE) {
- return false;
- }
- final boolean dismissOrMaximize;
- if (target.flag == SnapTarget.FLAG_DISMISS_START) {
- dismissOrMaximize = mDockSide == WindowManager.DOCKED_LEFT
- || mDockSide == WindowManager.DOCKED_TOP;
- } else {
- dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT
- || mDockSide == WindowManager.DOCKED_BOTTOM;
- }
- mWindowManagerProxy.dismissOrMaximizeDocked(mTiles, mSplitLayout, dismissOrMaximize);
- Transaction t = mTiles.getTransaction();
- setResizeDimLayer(t, true /* primary */, 0f);
- setResizeDimLayer(t, false /* primary */, 0f);
- t.apply();
- mTiles.releaseTransaction(t);
- return true;
- }
-
- private void liftBackground() {
- if (mBackgroundLifted) {
- return;
- }
- if (isHorizontalDivision()) {
- mBackground.animate().scaleY(1.4f);
- } else {
- mBackground.animate().scaleX(1.4f);
- }
- mBackground.animate()
- .setInterpolator(Interpolators.TOUCH_RESPONSE)
- .setDuration(TOUCH_ANIMATION_DURATION)
- .translationZ(mTouchElevation)
- .start();
-
- // Lift handle as well so it doesn't get behind the background, even though it doesn't
- // cast shadow.
- mHandle.animate()
- .setInterpolator(Interpolators.TOUCH_RESPONSE)
- .setDuration(TOUCH_ANIMATION_DURATION)
- .translationZ(mTouchElevation)
- .start();
- mBackgroundLifted = true;
- }
-
- private void releaseBackground() {
- if (!mBackgroundLifted) {
- return;
- }
- mBackground.animate()
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
- .translationZ(0)
- .scaleX(1f)
- .scaleY(1f)
- .start();
- mHandle.animate()
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
- .translationZ(0)
- .start();
- mBackgroundLifted = false;
- }
-
- private void initializeSurfaceState() {
- int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
- // Recalculate the split-layout's internal tile bounds
- mSplitLayout.resizeSplits(midPos);
- Transaction t = mTiles.getTransaction();
- if (mDockedStackMinimized) {
- int position = mSplitLayout.getMinimizedSnapAlgorithm(mHomeStackResizable)
- .getMiddleTarget().position;
- calculateBoundsForPosition(position, mDockSide, mDockedRect);
- calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
- mOtherRect);
- mDividerPositionX = mDividerPositionY = position;
- resizeSplitSurfaces(t, mDockedRect, mSplitLayout.mPrimary,
- mOtherRect, mSplitLayout.mSecondary);
- } else {
- resizeSplitSurfaces(t, mSplitLayout.mPrimary, null,
- mSplitLayout.mSecondary, null);
- }
- setResizeDimLayer(t, true /* primary */, 0.f /* alpha */);
- setResizeDimLayer(t, false /* secondary */, 0.f /* alpha */);
- t.apply();
- mTiles.releaseTransaction(t);
-
- // Get the actually-visible bar dimensions (relative to full window). This is a thin
- // bar going through the center.
- final Rect dividerBar = isHorizontalDivision()
- ? new Rect(0, mDividerInsets, mSplitLayout.mDisplayLayout.width(),
- mDividerInsets + mDividerSize)
- : new Rect(mDividerInsets, 0, mDividerInsets + mDividerSize,
- mSplitLayout.mDisplayLayout.height());
- final Region touchRegion = new Region(dividerBar);
- // Add in the "draggable" portion. While not visible, this is an expanded area that the
- // user can interact with.
- touchRegion.union(new Rect(mHandle.getLeft(), mHandle.getTop(),
- mHandle.getRight(), mHandle.getBottom()));
- mWindowManager.setTouchRegion(touchRegion);
- }
-
- void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable,
- Transaction t) {
- mHomeStackResizable = isHomeStackResizable;
- updateDockSide();
- if (!minimized) {
- resetBackground();
- }
- mMinimizedShadow.setAlpha(minimized ? 1f : 0f);
- if (mDockedStackMinimized != minimized) {
- mDockedStackMinimized = minimized;
- if (mSplitLayout.mDisplayLayout.rotation() != mDefaultDisplay.getRotation()) {
- // Splitscreen to minimize is about to starts after rotating landscape to seascape,
- // update display info and snap algorithm targets
- repositionSnapTargetBeforeMinimized();
- }
- if (mIsInMinimizeInteraction != minimized || mCurrentAnimator != null) {
- cancelFlingAnimation();
- if (minimized) {
- // Relayout to recalculate the divider shadow when minimizing
- requestLayout();
- mIsInMinimizeInteraction = true;
- resizeStackSurfaces(mSplitLayout.getMinimizedSnapAlgorithm(mHomeStackResizable)
- .getMiddleTarget(), t);
- } else {
- resizeStackSurfaces(mSnapTargetBeforeMinimized, t);
- mIsInMinimizeInteraction = false;
- }
- }
- }
- }
-
- void enterSplitMode(boolean isHomeStackResizable) {
- setHidden(false);
-
- SnapTarget miniMid =
- mSplitLayout.getMinimizedSnapAlgorithm(isHomeStackResizable).getMiddleTarget();
- if (mDockedStackMinimized) {
- mDividerPositionY = mDividerPositionX = miniMid.position;
- }
- }
-
- /**
- * Tries to grab a surface control from ViewRootImpl. If this isn't available for some reason
- * (ie. the window isn't ready yet), it will get the surfacecontrol that the WindowlessWM has
- * assigned to it.
- */
- private SurfaceControl getWindowSurfaceControl() {
- return mWindowManager.mSystemWindows.getViewSurface(this);
- }
-
- void exitSplitMode() {
- // The view is going to be removed right after this function involved, updates the surface
- // in the current thread instead of posting it to the view's UI thread.
- final SurfaceControl sc = getWindowSurfaceControl();
- if (sc == null) {
- return;
- }
- Transaction t = mTiles.getTransaction();
- t.hide(sc);
- mImeController.setDimsHidden(t, true);
- t.apply();
- mTiles.releaseTransaction(t);
-
- // Reset tile bounds
- int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
- mWindowManagerProxy.applyResizeSplits(midPos, mSplitLayout);
- }
-
- void setMinimizedDockStack(boolean minimized, long animDuration,
- boolean isHomeStackResizable) {
- if (DEBUG) Slog.d(TAG, "setMinDock: " + mDockedStackMinimized + "->" + minimized);
- mHomeStackResizable = isHomeStackResizable;
- updateDockSide();
- if (mDockedStackMinimized != minimized) {
- mIsInMinimizeInteraction = true;
- mDockedStackMinimized = minimized;
- stopDragging(minimized
- ? mSnapTargetBeforeMinimized.position
- : getCurrentPosition(),
- minimized
- ? mSplitLayout.getMinimizedSnapAlgorithm(mHomeStackResizable)
- .getMiddleTarget()
- : mSnapTargetBeforeMinimized,
- animDuration, Interpolators.FAST_OUT_SLOW_IN, 0);
- setAdjustedForIme(false, animDuration);
- }
- if (!minimized) {
- mBackground.animate().withEndAction(mResetBackgroundRunnable);
- }
- mBackground.animate()
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setDuration(animDuration)
- .start();
- }
-
- // Needed to end any currently playing animations when they might compete with other anims
- // (specifically, IME adjust animation immediately after leaving minimized). Someday maybe
- // these can be unified, but not today.
- void finishAnimations() {
- if (mCurrentAnimator != null) {
- mCurrentAnimator.end();
- }
- }
-
- void setAdjustedForIme(boolean adjustedForIme, long animDuration) {
- if (mAdjustedForIme == adjustedForIme) {
- return;
- }
- updateDockSide();
- mHandle.animate()
- .setInterpolator(IME_ADJUST_INTERPOLATOR)
- .setDuration(animDuration)
- .alpha(adjustedForIme ? 0f : 1f)
- .start();
- if (mDockSide == WindowManager.DOCKED_TOP) {
- mBackground.setPivotY(0);
- mBackground.animate()
- .scaleY(adjustedForIme ? ADJUSTED_FOR_IME_SCALE : 1f);
- }
- if (!adjustedForIme) {
- mBackground.animate().withEndAction(mResetBackgroundRunnable);
- }
- mBackground.animate()
- .setInterpolator(IME_ADJUST_INTERPOLATOR)
- .setDuration(animDuration)
- .start();
- mAdjustedForIme = adjustedForIme;
- }
-
- private void saveSnapTargetBeforeMinimized(SnapTarget target) {
- mSnapTargetBeforeMinimized = target;
- mState.mRatioPositionBeforeMinimized = (float) target.position
- / (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height()
- : mSplitLayout.mDisplayLayout.width());
- }
-
- private void resetBackground() {
- mBackground.setPivotX(mBackground.getWidth() / 2);
- mBackground.setPivotY(mBackground.getHeight() / 2);
- mBackground.setScaleX(1f);
- mBackground.setScaleY(1f);
- mMinimizedShadow.setAlpha(0f);
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- }
-
- private void repositionSnapTargetBeforeMinimized() {
- int position = (int) (mState.mRatioPositionBeforeMinimized
- * (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height()
- : mSplitLayout.mDisplayLayout.width()));
-
- // Set the snap target before minimized but do not save until divider is attached and not
- // minimized because it does not know its minimized state yet.
- mSnapTargetBeforeMinimized =
- mSplitLayout.getSnapAlgorithm().calculateNonDismissingSnapTarget(position);
- }
-
- private int calculatePosition(int touchX, int touchY) {
- return isHorizontalDivision() ? calculateYPosition(touchY) : calculateXPosition(touchX);
- }
-
- public boolean isHorizontalDivision() {
- return getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
- }
-
- private int calculateXPosition(int touchX) {
- return mStartPosition + touchX - mStartX;
- }
-
- private int calculateYPosition(int touchY) {
- return mStartPosition + touchY - mStartY;
- }
-
- private void alignTopLeft(Rect containingRect, Rect rect) {
- int width = rect.width();
- int height = rect.height();
- rect.set(containingRect.left, containingRect.top,
- containingRect.left + width, containingRect.top + height);
- }
-
- private void alignBottomRight(Rect containingRect, Rect rect) {
- int width = rect.width();
- int height = rect.height();
- rect.set(containingRect.right - width, containingRect.bottom - height,
- containingRect.right, containingRect.bottom);
- }
-
- private void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
- DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect,
- mSplitLayout.mDisplayLayout.width(), mSplitLayout.mDisplayLayout.height(),
- mDividerSize);
- }
-
- private void resizeStackSurfaces(SnapTarget taskSnapTarget, Transaction t) {
- resizeStackSurfaces(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget, t);
- }
-
- void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect otherRect) {
- resizeSplitSurfaces(t, dockedRect, null, otherRect, null);
- }
-
- private void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect dockedTaskRect,
- Rect otherRect, Rect otherTaskRect) {
- dockedTaskRect = dockedTaskRect == null ? dockedRect : dockedTaskRect;
- otherTaskRect = otherTaskRect == null ? otherRect : otherTaskRect;
-
- mDividerPositionX = mSplitLayout.getPrimarySplitSide() == DOCKED_RIGHT
- ? otherRect.right : dockedRect.right;
- mDividerPositionY = dockedRect.bottom;
-
- if (DEBUG) {
- Slog.d(TAG, "Resizing split surfaces: " + dockedRect + " " + dockedTaskRect
- + " " + otherRect + " " + otherTaskRect);
- }
-
- t.setPosition(mTiles.mPrimarySurface, dockedTaskRect.left, dockedTaskRect.top);
- Rect crop = new Rect(dockedRect);
- crop.offsetTo(-Math.min(dockedTaskRect.left - dockedRect.left, 0),
- -Math.min(dockedTaskRect.top - dockedRect.top, 0));
- t.setWindowCrop(mTiles.mPrimarySurface, crop);
- t.setPosition(mTiles.mSecondarySurface, otherTaskRect.left, otherTaskRect.top);
- crop.set(otherRect);
- crop.offsetTo(-(otherTaskRect.left - otherRect.left),
- -(otherTaskRect.top - otherRect.top));
- t.setWindowCrop(mTiles.mSecondarySurface, crop);
- final SurfaceControl dividerCtrl = getWindowSurfaceControl();
- if (dividerCtrl != null) {
- if (isHorizontalDivision()) {
- t.setPosition(dividerCtrl, 0, mDividerPositionY - mDividerInsets);
- } else {
- t.setPosition(dividerCtrl, mDividerPositionX - mDividerInsets, 0);
- }
- }
- }
-
- void setResizeDimLayer(Transaction t, boolean primary, float alpha) {
- SurfaceControl dim = primary ? mTiles.mPrimaryDim : mTiles.mSecondaryDim;
- if (alpha <= 0.001f) {
- t.hide(dim);
- } else {
- t.setAlpha(dim, alpha);
- t.show(dim);
- }
- }
-
- void resizeStackSurfaces(int position, int taskPosition, SnapTarget taskSnapTarget,
- Transaction transaction) {
- if (mRemoved) {
- // This divider view has been removed so shouldn't have any additional influence.
- return;
- }
- calculateBoundsForPosition(position, mDockSide, mDockedRect);
- calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
- mOtherRect);
-
- if (mDockedRect.equals(mLastResizeRect) && !mEntranceAnimationRunning) {
- return;
- }
-
- // Make sure shadows are updated
- if (mBackground.getZ() > 0f) {
- mBackground.invalidate();
- }
-
- final boolean ownTransaction = transaction == null;
- final Transaction t = ownTransaction ? mTiles.getTransaction() : transaction;
- mLastResizeRect.set(mDockedRect);
- if (mIsInMinimizeInteraction) {
- calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
- mDockedTaskRect);
- calculateBoundsForPosition(mSnapTargetBeforeMinimized.position,
- DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect);
-
- // Move a right-docked-app to line up with the divider while dragging it
- if (mDockSide == DOCKED_RIGHT) {
- mDockedTaskRect.offset(Math.max(position, -mDividerSize)
- - mDockedTaskRect.left + mDividerSize, 0);
- }
- resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
- if (ownTransaction) {
- t.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
- t.apply();
- mTiles.releaseTransaction(t);
- }
- return;
- }
-
- if (mEntranceAnimationRunning && taskPosition != TASK_POSITION_SAME) {
- calculateBoundsForPosition(taskPosition, mDockSide, mDockedTaskRect);
-
- // Move a docked app if from the right in position with the divider up to insets
- if (mDockSide == DOCKED_RIGHT) {
- mDockedTaskRect.offset(Math.max(position, -mDividerSize)
- - mDockedTaskRect.left + mDividerSize, 0);
- }
- calculateBoundsForPosition(taskPosition, DockedDividerUtils.invertDockSide(mDockSide),
- mOtherTaskRect);
- resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
- } else if (mExitAnimationRunning && taskPosition != TASK_POSITION_SAME) {
- calculateBoundsForPosition(taskPosition, mDockSide, mDockedTaskRect);
- mDockedInsetRect.set(mDockedTaskRect);
- calculateBoundsForPosition(mExitStartPosition,
- DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect);
- mOtherInsetRect.set(mOtherTaskRect);
- applyExitAnimationParallax(mOtherTaskRect, position);
-
- // Move a right-docked-app to line up with the divider while dragging it
- if (mDockSide == DOCKED_RIGHT) {
- mDockedTaskRect.offset(position + mDividerSize, 0);
- }
- resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
- } else if (taskPosition != TASK_POSITION_SAME) {
- calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
- mOtherRect);
- int dockSideInverted = DockedDividerUtils.invertDockSide(mDockSide);
- int taskPositionDocked =
- restrictDismissingTaskPosition(taskPosition, mDockSide, taskSnapTarget);
- int taskPositionOther =
- restrictDismissingTaskPosition(taskPosition, dockSideInverted, taskSnapTarget);
- calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect);
- calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect);
- mTmpRect.set(0, 0, mSplitLayout.mDisplayLayout.width(),
- mSplitLayout.mDisplayLayout.height());
- alignTopLeft(mDockedRect, mDockedTaskRect);
- alignTopLeft(mOtherRect, mOtherTaskRect);
- mDockedInsetRect.set(mDockedTaskRect);
- mOtherInsetRect.set(mOtherTaskRect);
- if (dockSideTopLeft(mDockSide)) {
- alignTopLeft(mTmpRect, mDockedInsetRect);
- alignBottomRight(mTmpRect, mOtherInsetRect);
- } else {
- alignBottomRight(mTmpRect, mDockedInsetRect);
- alignTopLeft(mTmpRect, mOtherInsetRect);
- }
- applyDismissingParallax(mDockedTaskRect, mDockSide, taskSnapTarget, position,
- taskPositionDocked);
- applyDismissingParallax(mOtherTaskRect, dockSideInverted, taskSnapTarget, position,
- taskPositionOther);
- resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
- } else {
- resizeSplitSurfaces(t, mDockedRect, null, mOtherRect, null);
- }
- SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position);
- float dimFraction = getDimFraction(position, closestDismissTarget);
- setResizeDimLayer(t, isDismissTargetPrimary(closestDismissTarget), dimFraction);
- if (ownTransaction) {
- t.apply();
- mTiles.releaseTransaction(t);
- }
- }
-
- private void applyExitAnimationParallax(Rect taskRect, int position) {
- if (mDockSide == WindowManager.DOCKED_TOP) {
- taskRect.offset(0, (int) ((position - mExitStartPosition) * 0.25f));
- } else if (mDockSide == WindowManager.DOCKED_LEFT) {
- taskRect.offset((int) ((position - mExitStartPosition) * 0.25f), 0);
- } else if (mDockSide == WindowManager.DOCKED_RIGHT) {
- taskRect.offset((int) ((mExitStartPosition - position) * 0.25f), 0);
- }
- }
-
- private float getDimFraction(int position, SnapTarget dismissTarget) {
- if (mEntranceAnimationRunning) {
- return 0f;
- }
- float fraction = getSnapAlgorithm().calculateDismissingFraction(position);
- fraction = Math.max(0, Math.min(fraction, 1f));
- fraction = DIM_INTERPOLATOR.getInterpolation(fraction);
- return fraction;
- }
-
- /**
- * When the snap target is dismissing one side, make sure that the dismissing side doesn't get
- * 0 size.
- */
- private int restrictDismissingTaskPosition(int taskPosition, int dockSide,
- SnapTarget snapTarget) {
- if (snapTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(dockSide)) {
- return Math.max(mSplitLayout.getSnapAlgorithm().getFirstSplitTarget().position,
- mStartPosition);
- } else if (snapTarget.flag == SnapTarget.FLAG_DISMISS_END
- && dockSideBottomRight(dockSide)) {
- return Math.min(mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position,
- mStartPosition);
- } else {
- return taskPosition;
- }
- }
-
- /**
- * Applies a parallax to the task when dismissing.
- */
- private void applyDismissingParallax(Rect taskRect, int dockSide, SnapTarget snapTarget,
- int position, int taskPosition) {
- float fraction = Math.min(1, Math.max(0,
- mSplitLayout.getSnapAlgorithm().calculateDismissingFraction(position)));
- SnapTarget dismissTarget = null;
- SnapTarget splitTarget = null;
- int start = 0;
- if (position <= mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position
- && dockSideTopLeft(dockSide)) {
- dismissTarget = mSplitLayout.getSnapAlgorithm().getDismissStartTarget();
- splitTarget = mSplitLayout.getSnapAlgorithm().getFirstSplitTarget();
- start = taskPosition;
- } else if (position >= mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position
- && dockSideBottomRight(dockSide)) {
- dismissTarget = mSplitLayout.getSnapAlgorithm().getDismissEndTarget();
- splitTarget = mSplitLayout.getSnapAlgorithm().getLastSplitTarget();
- start = splitTarget.position;
- }
- if (dismissTarget != null && fraction > 0f
- && isDismissing(splitTarget, position, dockSide)) {
- fraction = calculateParallaxDismissingFraction(fraction, dockSide);
- int offsetPosition = (int) (start + fraction
- * (dismissTarget.position - splitTarget.position));
- int width = taskRect.width();
- int height = taskRect.height();
- switch (dockSide) {
- case WindowManager.DOCKED_LEFT:
- taskRect.left = offsetPosition - width;
- taskRect.right = offsetPosition;
- break;
- case WindowManager.DOCKED_RIGHT:
- taskRect.left = offsetPosition + mDividerSize;
- taskRect.right = offsetPosition + width + mDividerSize;
- break;
- case WindowManager.DOCKED_TOP:
- taskRect.top = offsetPosition - height;
- taskRect.bottom = offsetPosition;
- break;
- case WindowManager.DOCKED_BOTTOM:
- taskRect.top = offsetPosition + mDividerSize;
- taskRect.bottom = offsetPosition + height + mDividerSize;
- break;
- }
- }
- }
-
- /**
- * @return for a specified {@code fraction}, this returns an adjusted value that simulates a
- * slowing down parallax effect
- */
- private static float calculateParallaxDismissingFraction(float fraction, int dockSide) {
- float result = SLOWDOWN_INTERPOLATOR.getInterpolation(fraction) / 3.5f;
-
- // Less parallax at the top, just because.
- if (dockSide == WindowManager.DOCKED_TOP) {
- result /= 2f;
- }
- return result;
- }
-
- private static boolean isDismissing(SnapTarget snapTarget, int position, int dockSide) {
- if (dockSide == WindowManager.DOCKED_TOP || dockSide == WindowManager.DOCKED_LEFT) {
- return position < snapTarget.position;
- } else {
- return position > snapTarget.position;
- }
- }
-
- private boolean isDismissTargetPrimary(SnapTarget dismissTarget) {
- return (dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide))
- || (dismissTarget.flag == SnapTarget.FLAG_DISMISS_END
- && dockSideBottomRight(mDockSide));
- }
-
- /**
- * @return true if and only if {@code dockSide} is top or left
- */
- private static boolean dockSideTopLeft(int dockSide) {
- return dockSide == WindowManager.DOCKED_TOP || dockSide == WindowManager.DOCKED_LEFT;
- }
-
- /**
- * @return true if and only if {@code dockSide} is bottom or right
- */
- private static boolean dockSideBottomRight(int dockSide) {
- return dockSide == WindowManager.DOCKED_BOTTOM || dockSide == WindowManager.DOCKED_RIGHT;
- }
-
- @Override
- public void onComputeInternalInsets(InternalInsetsInfo inoutInfo) {
- inoutInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- inoutInfo.touchableRegion.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(),
- mHandle.getBottom());
- inoutInfo.touchableRegion.op(mBackground.getLeft(), mBackground.getTop(),
- mBackground.getRight(), mBackground.getBottom(), Op.UNION);
- }
-
- void onUndockingTask() {
- int dockSide = mSplitLayout.getPrimarySplitSide();
- if (inSplitMode()) {
- startDragging(false /* animate */, false /* touching */);
- SnapTarget target = dockSideTopLeft(dockSide)
- ? mSplitLayout.getSnapAlgorithm().getDismissEndTarget()
- : mSplitLayout.getSnapAlgorithm().getDismissStartTarget();
-
- // Don't start immediately - give a little bit time to settle the drag resize change.
- mExitAnimationRunning = true;
- mExitStartPosition = getCurrentPosition();
- stopDragging(mExitStartPosition, target, 336 /* duration */, 100 /* startDelay */,
- 0 /* endDelay */, Interpolators.FAST_OUT_SLOW_IN);
- }
- }
-
- private int calculatePositionForInsetBounds() {
- mSplitLayout.mDisplayLayout.getStableBounds(mTmpRect);
- return DockedDividerUtils.calculatePositionForBounds(mTmpRect, mDockSide, mDividerSize);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerWindowManager.java
deleted file mode 100644
index 2c3ae68e4749..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerWindowManager.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.wm.shell.legacysplitscreen;
-
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
-import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
-import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static android.view.WindowManager.SHELL_ROOT_LAYER_DIVIDER;
-
-import android.graphics.PixelFormat;
-import android.graphics.Region;
-import android.os.Binder;
-import android.view.View;
-import android.view.WindowManager;
-
-import com.android.wm.shell.common.SystemWindows;
-
-/**
- * Manages the window parameters of the docked stack divider.
- */
-final class DividerWindowManager {
-
- private static final String WINDOW_TITLE = "DockedStackDivider";
-
- final SystemWindows mSystemWindows;
- private WindowManager.LayoutParams mLp;
- private View mView;
-
- DividerWindowManager(SystemWindows systemWindows) {
- mSystemWindows = systemWindows;
- }
-
- /** Add a divider view */
- void add(View view, int width, int height, int displayId) {
- mLp = new WindowManager.LayoutParams(
- width, height, TYPE_DOCK_DIVIDER,
- FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
- | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY,
- PixelFormat.TRANSLUCENT);
- mLp.token = new Binder();
- mLp.setTitle(WINDOW_TITLE);
- mLp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
- mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
- mSystemWindows.addView(view, mLp, displayId, SHELL_ROOT_LAYER_DIVIDER);
- mView = view;
- }
-
- void remove() {
- if (mView != null) {
- mSystemWindows.removeView(mView);
- }
- mView = null;
- }
-
- void setSlippery(boolean slippery) {
- boolean changed = false;
- if (slippery && (mLp.flags & FLAG_SLIPPERY) == 0) {
- mLp.flags |= FLAG_SLIPPERY;
- changed = true;
- } else if (!slippery && (mLp.flags & FLAG_SLIPPERY) != 0) {
- mLp.flags &= ~FLAG_SLIPPERY;
- changed = true;
- }
- if (changed) {
- mSystemWindows.updateViewLayout(mView, mLp);
- }
- }
-
- void setTouchable(boolean touchable) {
- if (mView == null) {
- return;
- }
- boolean changed = false;
- if (!touchable && (mLp.flags & FLAG_NOT_TOUCHABLE) == 0) {
- mLp.flags |= FLAG_NOT_TOUCHABLE;
- changed = true;
- } else if (touchable && (mLp.flags & FLAG_NOT_TOUCHABLE) != 0) {
- mLp.flags &= ~FLAG_NOT_TOUCHABLE;
- changed = true;
- }
- if (changed) {
- mSystemWindows.updateViewLayout(mView, mLp);
- }
- }
-
- /** Sets the touch region to `touchRegion`. Use null to unset.*/
- void setTouchRegion(Region touchRegion) {
- if (mView == null) {
- return;
- }
- mSystemWindows.setTouchableRegion(mView, touchRegion);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivity.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivity.java
deleted file mode 100644
index 4fe28e630114..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivity.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.legacysplitscreen;
-
-import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
-import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
-
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnTouchListener;
-import android.widget.TextView;
-
-import com.android.wm.shell.R;
-
-/**
- * Translucent activity that gets started on top of a task in multi-window to inform the user that
- * we forced the activity below to be resizable.
- *
- * Note: This activity runs on the main thread of the process hosting the Shell lib.
- */
-public class ForcedResizableInfoActivity extends Activity implements OnTouchListener {
-
- public static final String EXTRA_FORCED_RESIZEABLE_REASON = "extra_forced_resizeable_reason";
-
- private static final long DISMISS_DELAY = 2500;
-
- private final Runnable mFinishRunnable = new Runnable() {
- @Override
- public void run() {
- finish();
- }
- };
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.forced_resizable_activity);
- TextView tv = findViewById(com.android.internal.R.id.message);
- int reason = getIntent().getIntExtra(EXTRA_FORCED_RESIZEABLE_REASON, -1);
- String text;
- switch (reason) {
- case FORCED_RESIZEABLE_REASON_SPLIT_SCREEN:
- text = getString(R.string.dock_forced_resizable);
- break;
- case FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY:
- text = getString(R.string.forced_resizable_secondary_display);
- break;
- default:
- throw new IllegalArgumentException("Unexpected forced resizeable reason: "
- + reason);
- }
- tv.setText(text);
- getWindow().setTitle(text);
- getWindow().getDecorView().setOnTouchListener(this);
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- getWindow().getDecorView().postDelayed(mFinishRunnable, DISMISS_DELAY);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- finish();
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- finish();
- return true;
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- finish();
- return true;
- }
-
- @Override
- public void finish() {
- super.finish();
- overridePendingTransition(0, R.anim.forced_resizable_exit);
- }
-
- @Override
- public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
- // Do nothing
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivityController.java
deleted file mode 100644
index 139544f951ce..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivityController.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.legacysplitscreen;
-
-
-import static com.android.wm.shell.legacysplitscreen.ForcedResizableInfoActivity.EXTRA_FORCED_RESIZEABLE_REASON;
-
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.Intent;
-import android.os.UserHandle;
-import android.util.ArraySet;
-import android.widget.Toast;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.common.ShellExecutor;
-
-import java.util.function.Consumer;
-
-/**
- * Controller that decides when to show the {@link ForcedResizableInfoActivity}.
- */
-final class ForcedResizableInfoActivityController implements DividerView.DividerCallbacks {
-
- private static final String SELF_PACKAGE_NAME = "com.android.systemui";
-
- private static final int TIMEOUT = 1000;
- private final Context mContext;
- private final ShellExecutor mMainExecutor;
- private final ArraySet<PendingTaskRecord> mPendingTasks = new ArraySet<>();
- private final ArraySet<String> mPackagesShownInSession = new ArraySet<>();
- private boolean mDividerDragging;
-
- private final Runnable mTimeoutRunnable = this::showPending;
-
- private final Consumer<Boolean> mDockedStackExistsListener = exists -> {
- if (!exists) {
- mPackagesShownInSession.clear();
- }
- };
-
- /** Record of force resized task that's pending to be handled. */
- private class PendingTaskRecord {
- int mTaskId;
- /**
- * {@link android.app.ITaskStackListener#FORCED_RESIZEABLE_REASON_SPLIT_SCREEN} or
- * {@link android.app.ITaskStackListener#FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY}
- */
- int mReason;
-
- PendingTaskRecord(int taskId, int reason) {
- this.mTaskId = taskId;
- this.mReason = reason;
- }
- }
-
- ForcedResizableInfoActivityController(Context context,
- LegacySplitScreenController splitScreenController,
- ShellExecutor mainExecutor) {
- mContext = context;
- mMainExecutor = mainExecutor;
- splitScreenController.registerInSplitScreenListener(mDockedStackExistsListener);
- }
-
- @Override
- public void onDraggingStart() {
- mDividerDragging = true;
- mMainExecutor.removeCallbacks(mTimeoutRunnable);
- }
-
- @Override
- public void onDraggingEnd() {
- mDividerDragging = false;
- showPending();
- }
-
- void onAppTransitionFinished() {
- if (!mDividerDragging) {
- showPending();
- }
- }
-
- void activityForcedResizable(String packageName, int taskId, int reason) {
- if (debounce(packageName)) {
- return;
- }
- mPendingTasks.add(new PendingTaskRecord(taskId, reason));
- postTimeout();
- }
-
- void activityDismissingSplitScreen() {
- Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
- Toast.LENGTH_SHORT).show();
- }
-
- void activityLaunchOnSecondaryDisplayFailed() {
- Toast.makeText(mContext, R.string.activity_launch_on_secondary_display_failed_text,
- Toast.LENGTH_SHORT).show();
- }
-
- private void showPending() {
- mMainExecutor.removeCallbacks(mTimeoutRunnable);
- for (int i = mPendingTasks.size() - 1; i >= 0; i--) {
- PendingTaskRecord pendingRecord = mPendingTasks.valueAt(i);
- Intent intent = new Intent(mContext, ForcedResizableInfoActivity.class);
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchTaskId(pendingRecord.mTaskId);
- // Set as task overlay and allow to resume, so that when an app enters split-screen and
- // becomes paused, the overlay will still be shown.
- options.setTaskOverlay(true, true /* canResume */);
- intent.putExtra(EXTRA_FORCED_RESIZEABLE_REASON, pendingRecord.mReason);
- mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
- }
- mPendingTasks.clear();
- }
-
- private void postTimeout() {
- mMainExecutor.removeCallbacks(mTimeoutRunnable);
- mMainExecutor.executeDelayed(mTimeoutRunnable, TIMEOUT);
- }
-
- private boolean debounce(String packageName) {
- if (packageName == null) {
- return false;
- }
-
- // We launch ForcedResizableInfoActivity into a task that was forced resizable, so that
- // triggers another notification. So ignore our own activity.
- if (SELF_PACKAGE_NAME.equals(packageName)) {
- return true;
- }
- boolean debounce = mPackagesShownInSession.contains(packageName);
- mPackagesShownInSession.add(packageName);
- return debounce;
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java
deleted file mode 100644
index f201634d3d4a..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.legacysplitscreen;
-
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.util.RotationUtils.rotateBounds;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Rect;
-import android.util.TypedValue;
-import android.window.WindowContainerTransaction;
-
-import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.internal.policy.DockedDividerUtils;
-import com.android.wm.shell.common.DisplayLayout;
-
-/**
- * Handles split-screen related internal display layout. In general, this represents the
- * WM-facing understanding of the splits.
- */
-public class LegacySplitDisplayLayout {
- /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
- * restrict IME adjustment so that a min portion of top stack remains visible.*/
- private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
-
- private static final int DIVIDER_WIDTH_INACTIVE_DP = 4;
-
- LegacySplitScreenTaskListener mTiles;
- DisplayLayout mDisplayLayout;
- Context mContext;
-
- // Lazy stuff
- boolean mResourcesValid = false;
- int mDividerSize;
- int mDividerSizeInactive;
- private DividerSnapAlgorithm mSnapAlgorithm = null;
- private DividerSnapAlgorithm mMinimizedSnapAlgorithm = null;
- Rect mPrimary = null;
- Rect mSecondary = null;
- Rect mAdjustedPrimary = null;
- Rect mAdjustedSecondary = null;
- final Rect mTmpBounds = new Rect();
-
- public LegacySplitDisplayLayout(Context ctx, DisplayLayout dl,
- LegacySplitScreenTaskListener taskTiles) {
- mTiles = taskTiles;
- mDisplayLayout = dl;
- mContext = ctx;
- }
-
- void rotateTo(int newRotation) {
- mDisplayLayout.rotateTo(mContext.getResources(), newRotation);
- final Configuration config = new Configuration();
- config.unset();
- config.orientation = mDisplayLayout.getOrientation();
- Rect tmpRect = new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
- tmpRect.inset(mDisplayLayout.nonDecorInsets());
- config.windowConfiguration.setAppBounds(tmpRect);
- tmpRect.set(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
- tmpRect.inset(mDisplayLayout.stableInsets());
- config.screenWidthDp = (int) (tmpRect.width() / mDisplayLayout.density());
- config.screenHeightDp = (int) (tmpRect.height() / mDisplayLayout.density());
- mContext = mContext.createConfigurationContext(config);
- mSnapAlgorithm = null;
- mMinimizedSnapAlgorithm = null;
- mResourcesValid = false;
- }
-
- private void updateResources() {
- if (mResourcesValid) {
- return;
- }
- mResourcesValid = true;
- Resources res = mContext.getResources();
- mDividerSize = DockedDividerUtils.getDividerSize(res,
- DockedDividerUtils.getDividerInsets(res));
- mDividerSizeInactive = (int) TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP, DIVIDER_WIDTH_INACTIVE_DP, res.getDisplayMetrics());
- }
-
- int getPrimarySplitSide() {
- switch (mDisplayLayout.getNavigationBarPosition(mContext.getResources())) {
- case DisplayLayout.NAV_BAR_BOTTOM:
- return mDisplayLayout.isLandscape() ? DOCKED_LEFT : DOCKED_TOP;
- case DisplayLayout.NAV_BAR_LEFT:
- return DOCKED_RIGHT;
- case DisplayLayout.NAV_BAR_RIGHT:
- return DOCKED_LEFT;
- default:
- return DOCKED_INVALID;
- }
- }
-
- DividerSnapAlgorithm getSnapAlgorithm() {
- if (mSnapAlgorithm == null) {
- updateResources();
- boolean isHorizontalDivision = !mDisplayLayout.isLandscape();
- mSnapAlgorithm = new DividerSnapAlgorithm(mContext.getResources(),
- mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize,
- isHorizontalDivision, mDisplayLayout.stableInsets(), getPrimarySplitSide());
- }
- return mSnapAlgorithm;
- }
-
- DividerSnapAlgorithm getMinimizedSnapAlgorithm(boolean homeStackResizable) {
- if (mMinimizedSnapAlgorithm == null) {
- updateResources();
- boolean isHorizontalDivision = !mDisplayLayout.isLandscape();
- mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(mContext.getResources(),
- mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize,
- isHorizontalDivision, mDisplayLayout.stableInsets(), getPrimarySplitSide(),
- true /* isMinimized */, homeStackResizable);
- }
- return mMinimizedSnapAlgorithm;
- }
-
- /**
- * Resize primary bounds and secondary bounds by divider position.
- *
- * @param position divider position.
- * @return true if calculated bounds changed.
- */
- boolean resizeSplits(int position) {
- mPrimary = mPrimary == null ? new Rect() : mPrimary;
- mSecondary = mSecondary == null ? new Rect() : mSecondary;
- int dockSide = getPrimarySplitSide();
- boolean boundsChanged;
-
- mTmpBounds.set(mPrimary);
- DockedDividerUtils.calculateBoundsForPosition(position, dockSide, mPrimary,
- mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize);
- boundsChanged = !mPrimary.equals(mTmpBounds);
-
- mTmpBounds.set(mSecondary);
- DockedDividerUtils.calculateBoundsForPosition(position,
- DockedDividerUtils.invertDockSide(dockSide), mSecondary, mDisplayLayout.width(),
- mDisplayLayout.height(), mDividerSize);
- boundsChanged |= !mSecondary.equals(mTmpBounds);
- return boundsChanged;
- }
-
- void resizeSplits(int position, WindowContainerTransaction t) {
- if (resizeSplits(position)) {
- t.setBounds(mTiles.mPrimary.token, mPrimary);
- t.setBounds(mTiles.mSecondary.token, mSecondary);
-
- t.setSmallestScreenWidthDp(mTiles.mPrimary.token,
- getSmallestWidthDpForBounds(mContext, mDisplayLayout, mPrimary));
- t.setSmallestScreenWidthDp(mTiles.mSecondary.token,
- getSmallestWidthDpForBounds(mContext, mDisplayLayout, mSecondary));
- }
- }
-
- Rect calcResizableMinimizedHomeStackBounds() {
- DividerSnapAlgorithm.SnapTarget miniMid =
- getMinimizedSnapAlgorithm(true /* resizable */).getMiddleTarget();
- Rect homeBounds = new Rect();
- DockedDividerUtils.calculateBoundsForPosition(miniMid.position,
- DockedDividerUtils.invertDockSide(getPrimarySplitSide()), homeBounds,
- mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize);
- return homeBounds;
- }
-
- /**
- * Updates the adjustment depending on it's current state.
- */
- void updateAdjustedBounds(int currImeTop, int hiddenTop, int shownTop) {
- adjustForIME(mDisplayLayout, currImeTop, hiddenTop, shownTop, mDividerSize,
- mDividerSizeInactive, mPrimary, mSecondary);
- }
-
- /** Assumes top/bottom split. Splits are not adjusted for left/right splits. */
- private void adjustForIME(DisplayLayout dl, int currImeTop, int hiddenTop, int shownTop,
- int dividerWidth, int dividerWidthInactive, Rect primaryBounds, Rect secondaryBounds) {
- if (mAdjustedPrimary == null) {
- mAdjustedPrimary = new Rect();
- mAdjustedSecondary = new Rect();
- }
-
- final Rect displayStableRect = new Rect();
- dl.getStableBounds(displayStableRect);
-
- final float shownFraction = ((float) (currImeTop - hiddenTop)) / (shownTop - hiddenTop);
- final int currDividerWidth =
- (int) (dividerWidthInactive * shownFraction + dividerWidth * (1.f - shownFraction));
-
- // Calculate the highest we can move the bottom of the top stack to keep 30% visible.
- final int minTopStackBottom = displayStableRect.top
- + (int) ((mPrimary.bottom - displayStableRect.top) * ADJUSTED_STACK_FRACTION_MIN);
- // Based on that, calculate the maximum amount we'll allow the ime to shift things.
- final int maxOffset = mPrimary.bottom - minTopStackBottom;
- // Calculate how much we would shift things without limits (basically the height of ime).
- final int desiredOffset = hiddenTop - shownTop;
- // Calculate an "adjustedTop" which is the currImeTop but restricted by our constraints.
- // We want an effect where the adjustment only occurs during the "highest" portion of the
- // ime animation. This is done by shifting the adjustment values by the difference in
- // offsets (effectively playing the whole adjustment animation some fixed amount of pixels
- // below the ime top).
- final int topCorrection = Math.max(0, desiredOffset - maxOffset);
- final int adjustedTop = currImeTop + topCorrection;
- // The actual yOffset is the distance between adjustedTop and the bottom of the display.
- // Since our adjustedTop values are playing "below" the ime, we clamp at 0 so we only
- // see adjustment upward.
- final int yOffset = Math.max(0, dl.height() - adjustedTop);
-
- // TOP
- // Reduce the offset by an additional small amount to squish the divider bar.
- mAdjustedPrimary.set(primaryBounds);
- mAdjustedPrimary.offset(0, -yOffset + (dividerWidth - currDividerWidth));
-
- // BOTTOM
- mAdjustedSecondary.set(secondaryBounds);
- mAdjustedSecondary.offset(0, -yOffset);
- }
-
- static int getSmallestWidthDpForBounds(@NonNull Context context, DisplayLayout dl,
- Rect bounds) {
- int dividerSize = DockedDividerUtils.getDividerSize(context.getResources(),
- DockedDividerUtils.getDividerInsets(context.getResources()));
-
- int minWidth = Integer.MAX_VALUE;
-
- // Go through all screen orientations and find the orientation in which the task has the
- // smallest width.
- Rect tmpRect = new Rect();
- Rect rotatedDisplayRect = new Rect();
- Rect displayRect = new Rect(0, 0, dl.width(), dl.height());
-
- DisplayLayout tmpDL = new DisplayLayout();
- for (int rotation = 0; rotation < 4; rotation++) {
- tmpDL.set(dl);
- tmpDL.rotateTo(context.getResources(), rotation);
- DividerSnapAlgorithm snap = initSnapAlgorithmForRotation(context, tmpDL, dividerSize);
-
- tmpRect.set(bounds);
- rotateBounds(tmpRect, displayRect, dl.rotation(), rotation);
- rotatedDisplayRect.set(0, 0, tmpDL.width(), tmpDL.height());
- final int dockSide = getPrimarySplitSide(tmpRect, rotatedDisplayRect,
- tmpDL.getOrientation());
- final int position = DockedDividerUtils.calculatePositionForBounds(tmpRect, dockSide,
- dividerSize);
-
- final int snappedPosition =
- snap.calculateNonDismissingSnapTarget(position).position;
- DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, tmpRect,
- tmpDL.width(), tmpDL.height(), dividerSize);
- Rect insettedDisplay = new Rect(rotatedDisplayRect);
- insettedDisplay.inset(tmpDL.stableInsets());
- tmpRect.intersect(insettedDisplay);
- minWidth = Math.min(tmpRect.width(), minWidth);
- }
- return (int) (minWidth / dl.density());
- }
-
- static DividerSnapAlgorithm initSnapAlgorithmForRotation(Context context, DisplayLayout dl,
- int dividerSize) {
- final Configuration config = new Configuration();
- config.unset();
- config.orientation = dl.getOrientation();
- Rect tmpRect = new Rect(0, 0, dl.width(), dl.height());
- tmpRect.inset(dl.nonDecorInsets());
- config.windowConfiguration.setAppBounds(tmpRect);
- tmpRect.set(0, 0, dl.width(), dl.height());
- tmpRect.inset(dl.stableInsets());
- config.screenWidthDp = (int) (tmpRect.width() / dl.density());
- config.screenHeightDp = (int) (tmpRect.height() / dl.density());
- final Context rotationContext = context.createConfigurationContext(config);
- return new DividerSnapAlgorithm(
- rotationContext.getResources(), dl.width(), dl.height(), dividerSize,
- config.orientation == ORIENTATION_PORTRAIT, dl.stableInsets());
- }
-
- /**
- * Get the current primary-split side. Determined by its location of {@param bounds} within
- * {@param displayRect} but if both are the same, it will try to dock to each side and determine
- * if allowed in its respected {@param orientation}.
- *
- * @param bounds bounds of the primary split task to get which side is docked
- * @param displayRect bounds of the display that contains the primary split task
- * @param orientation the origination of device
- * @return current primary-split side
- */
- static int getPrimarySplitSide(Rect bounds, Rect displayRect, int orientation) {
- if (orientation == ORIENTATION_PORTRAIT) {
- // Portrait mode, docked either at the top or the bottom.
- final int diff = (displayRect.bottom - bounds.bottom) - (bounds.top - displayRect.top);
- if (diff < 0) {
- return DOCKED_BOTTOM;
- } else {
- // Top is default
- return DOCKED_TOP;
- }
- } else if (orientation == ORIENTATION_LANDSCAPE) {
- // Landscape mode, docked either on the left or on the right.
- final int diff = (displayRect.right - bounds.right) - (bounds.left - displayRect.left);
- if (diff < 0) {
- return DOCKED_RIGHT;
- }
- return DOCKED_LEFT;
- }
- return DOCKED_INVALID;
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreen.java
deleted file mode 100644
index 499a9c5fa631..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreen.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.legacysplitscreen;
-
-import android.graphics.Rect;
-import android.window.WindowContainerToken;
-
-import com.android.wm.shell.common.annotations.ExternalThread;
-
-import java.io.PrintWriter;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-
-/**
- * Interface to engage split screen feature.
- */
-@ExternalThread
-public interface LegacySplitScreen {
- /** Called when keyguard showing state changed. */
- void onKeyguardVisibilityChanged(boolean isShowing);
-
- /** Returns {@link DividerView}. */
- DividerView getDividerView();
-
- /** Returns {@code true} if one of the split screen is in minimized mode. */
- boolean isMinimized();
-
- /** Returns {@code true} if the home stack is resizable. */
- boolean isHomeStackResizable();
-
- /** Returns {@code true} if the divider is visible. */
- boolean isDividerVisible();
-
- /** Switch to minimized state if appropriate. */
- void setMinimized(boolean minimized);
-
- /** Called when there's a task undocking. */
- void onUndockingTask();
-
- /** Called when app transition finished. */
- void onAppTransitionFinished();
-
- /** Dumps current status of Split Screen. */
- void dump(PrintWriter pw);
-
- /** Registers listener that gets called whenever the existence of the divider changes. */
- void registerInSplitScreenListener(Consumer<Boolean> listener);
-
- /** Unregisters listener that gets called whenever the existence of the divider changes. */
- void unregisterInSplitScreenListener(Consumer<Boolean> listener);
-
- /** Registers listener that gets called whenever the split screen bounds changes. */
- void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener);
-
- /** @return the container token for the secondary split root task. */
- WindowContainerToken getSecondaryRoot();
-
- /**
- * Splits the primary task if feasible, this is to preserve legacy way to toggle split screen.
- * Like triggering split screen through long pressing recents app button or through
- * {@link android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN}.
- *
- * @return {@code true} if it successes to split the primary task.
- */
- boolean splitPrimaryTask();
-
- /**
- * Exits the split to make the primary task fullscreen.
- */
- void dismissSplitToPrimaryTask();
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
deleted file mode 100644
index 67e487de0993..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
+++ /dev/null
@@ -1,762 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.legacysplitscreen;
-
-import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import android.animation.AnimationHandler;
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityTaskManager;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.util.Slog;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.Toast;
-import android.window.TaskOrganizer;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.wm.shell.R;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayChangeController;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.SystemWindows;
-import com.android.wm.shell.common.TaskStackListenerCallback;
-import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.transition.Transitions;
-
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-
-/**
- * Controls split screen feature.
- */
-public class LegacySplitScreenController implements DisplayController.OnDisplaysChangedListener {
- static final boolean DEBUG = false;
-
- private static final String TAG = "SplitScreenCtrl";
- private static final int DEFAULT_APP_TRANSITION_DURATION = 336;
-
- private final Context mContext;
- private final DisplayChangeController.OnDisplayChangingListener mRotationController;
- private final DisplayController mDisplayController;
- private final DisplayImeController mImeController;
- private final DividerImeController mImePositionProcessor;
- private final DividerState mDividerState = new DividerState();
- private final ForcedResizableInfoActivityController mForcedResizableController;
- private final ShellExecutor mMainExecutor;
- private final AnimationHandler mSfVsyncAnimationHandler;
- private final LegacySplitScreenTaskListener mSplits;
- private final SystemWindows mSystemWindows;
- final TransactionPool mTransactionPool;
- private final WindowManagerProxy mWindowManagerProxy;
- private final TaskOrganizer mTaskOrganizer;
- private final SplitScreenImpl mImpl = new SplitScreenImpl();
-
- private final CopyOnWriteArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners
- = new CopyOnWriteArrayList<>();
- private final ArrayList<WeakReference<BiConsumer<Rect, Rect>>> mBoundsChangedListeners =
- new ArrayList<>();
-
-
- private DividerWindowManager mWindowManager;
- private DividerView mView;
-
- // Keeps track of real-time split geometry including snap positions and ime adjustments
- private LegacySplitDisplayLayout mSplitLayout;
-
- // Transient: this contains the layout calculated for a new rotation requested by WM. This is
- // kept around so that we can wait for a matching configuration change and then use the exact
- // layout that we sent back to WM.
- private LegacySplitDisplayLayout mRotateSplitLayout;
-
- private boolean mIsKeyguardShowing;
- private boolean mVisible = false;
- private volatile boolean mMinimized = false;
- private volatile boolean mAdjustedForIme = false;
- private boolean mHomeStackResizable = false;
-
- public LegacySplitScreenController(Context context,
- DisplayController displayController, SystemWindows systemWindows,
- DisplayImeController imeController, TransactionPool transactionPool,
- ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
- TaskStackListenerImpl taskStackListener, Transitions transitions,
- ShellExecutor mainExecutor, AnimationHandler sfVsyncAnimationHandler) {
- mContext = context;
- mDisplayController = displayController;
- mSystemWindows = systemWindows;
- mImeController = imeController;
- mMainExecutor = mainExecutor;
- mSfVsyncAnimationHandler = sfVsyncAnimationHandler;
- mForcedResizableController = new ForcedResizableInfoActivityController(context, this,
- mainExecutor);
- mTransactionPool = transactionPool;
- mWindowManagerProxy = new WindowManagerProxy(syncQueue, shellTaskOrganizer);
- mTaskOrganizer = shellTaskOrganizer;
- mSplits = new LegacySplitScreenTaskListener(this, shellTaskOrganizer, transitions,
- syncQueue);
- mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mMainExecutor,
- shellTaskOrganizer);
- mRotationController =
- (display, fromRotation, toRotation, wct) -> {
- if (!mSplits.isSplitScreenSupported() || mWindowManagerProxy == null) {
- return;
- }
- WindowContainerTransaction t = new WindowContainerTransaction();
- DisplayLayout displayLayout =
- new DisplayLayout(mDisplayController.getDisplayLayout(display));
- LegacySplitDisplayLayout sdl =
- new LegacySplitDisplayLayout(mContext, displayLayout, mSplits);
- sdl.rotateTo(toRotation);
- mRotateSplitLayout = sdl;
- // snap resets to middle target when not minimized and rotation changed.
- final int position = mMinimized ? mView.mSnapTargetBeforeMinimized.position
- : sdl.getSnapAlgorithm().getMiddleTarget().position;
- DividerSnapAlgorithm snap = sdl.getSnapAlgorithm();
- final DividerSnapAlgorithm.SnapTarget target =
- snap.calculateNonDismissingSnapTarget(position);
- sdl.resizeSplits(target.position, t);
-
- if (isSplitActive() && mHomeStackResizable) {
- mWindowManagerProxy
- .applyHomeTasksMinimized(sdl, mSplits.mSecondary.token, t);
- }
- if (mWindowManagerProxy.queueSyncTransactionIfWaiting(t)) {
- // Because sync transactions are serialized, its possible for an "older"
- // bounds-change to get applied after a screen rotation. In that case, we
- // want to actually defer on that rather than apply immediately. Of course,
- // this means that the bounds may not change until after the rotation so
- // the user might see some artifacts. This should be rare.
- Slog.w(TAG, "Screen rotated while other operations were pending, this may"
- + " result in some graphical artifacts.");
- } else {
- wct.merge(t, true /* transfer */);
- }
- };
-
- mWindowManager = new DividerWindowManager(mSystemWindows);
-
- // No need to listen to display window container or create root tasks if the device is not
- // using legacy split screen.
- if (!context.getResources().getBoolean(com.android.internal.R.bool.config_useLegacySplit)) {
- return;
- }
-
-
- mDisplayController.addDisplayWindowListener(this);
- // Don't initialize the divider or anything until we get the default display.
-
- taskStackListener.addListener(
- new TaskStackListenerCallback() {
- @Override
- public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
- boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
- if (!wasVisible || task.getWindowingMode()
- != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || !mSplits.isSplitScreenSupported()) {
- return;
- }
-
- if (isMinimized()) {
- onUndockingTask();
- }
- }
-
- @Override
- public void onActivityForcedResizable(String packageName, int taskId,
- int reason) {
- mForcedResizableController.activityForcedResizable(packageName, taskId,
- reason);
- }
-
- @Override
- public void onActivityDismissingDockedStack() {
- mForcedResizableController.activityDismissingSplitScreen();
- }
-
- @Override
- public void onActivityLaunchOnSecondaryDisplayFailed() {
- mForcedResizableController.activityLaunchOnSecondaryDisplayFailed();
- }
- });
- }
-
- public LegacySplitScreen asLegacySplitScreen() {
- return mImpl;
- }
-
- public void onSplitScreenSupported() {
- // Set starting tile bounds based on middle target
- final WindowContainerTransaction tct = new WindowContainerTransaction();
- int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
- mSplitLayout.resizeSplits(midPos, tct);
- mTaskOrganizer.applyTransaction(tct);
- }
-
- public void onKeyguardVisibilityChanged(boolean showing) {
- if (!isSplitActive() || mView == null) {
- return;
- }
- mView.setHidden(showing);
- mIsKeyguardShowing = showing;
- }
-
- @Override
- public void onDisplayAdded(int displayId) {
- if (displayId != DEFAULT_DISPLAY) {
- return;
- }
- mSplitLayout = new LegacySplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
- mDisplayController.getDisplayLayout(displayId), mSplits);
- mImeController.addPositionProcessor(mImePositionProcessor);
- mDisplayController.addDisplayChangingController(mRotationController);
- if (!ActivityTaskManager.supportsSplitScreenMultiWindow(mContext)) {
- removeDivider();
- return;
- }
- try {
- mSplits.init();
- } catch (Exception e) {
- Slog.e(TAG, "Failed to register docked stack listener", e);
- removeDivider();
- return;
- }
- }
-
- @Override
- public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
- if (displayId != DEFAULT_DISPLAY || !mSplits.isSplitScreenSupported()) {
- return;
- }
- mSplitLayout = new LegacySplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
- mDisplayController.getDisplayLayout(displayId), mSplits);
- if (mRotateSplitLayout == null) {
- int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
- final WindowContainerTransaction tct = new WindowContainerTransaction();
- mSplitLayout.resizeSplits(midPos, tct);
- mTaskOrganizer.applyTransaction(tct);
- } else if (mSplitLayout.mDisplayLayout.rotation()
- == mRotateSplitLayout.mDisplayLayout.rotation()) {
- mSplitLayout.mPrimary = new Rect(mRotateSplitLayout.mPrimary);
- mSplitLayout.mSecondary = new Rect(mRotateSplitLayout.mSecondary);
- mRotateSplitLayout = null;
- }
- if (isSplitActive()) {
- update(newConfig);
- }
- }
-
- public boolean isMinimized() {
- return mMinimized;
- }
-
- public boolean isHomeStackResizable() {
- return mHomeStackResizable;
- }
-
- public DividerView getDividerView() {
- return mView;
- }
-
- public boolean isDividerVisible() {
- return mView != null && mView.getVisibility() == View.VISIBLE;
- }
-
- /**
- * This indicates that at-least one of the splits has content. This differs from
- * isDividerVisible because the divider is only visible once *everything* is in split mode
- * while this only cares if some things are (eg. while entering/exiting as well).
- */
- public boolean isSplitActive() {
- return mSplits.mPrimary != null && mSplits.mSecondary != null
- && (mSplits.mPrimary.topActivityType != ACTIVITY_TYPE_UNDEFINED
- || mSplits.mSecondary.topActivityType != ACTIVITY_TYPE_UNDEFINED);
- }
-
- public void addDivider(Configuration configuration) {
- Context dctx = mDisplayController.getDisplayContext(mContext.getDisplayId());
- mView = (DividerView)
- LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
- mView.setAnimationHandler(mSfVsyncAnimationHandler);
- DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
- mView.injectDependencies(this, mWindowManager, mDividerState, mForcedResizableController,
- mSplits, mSplitLayout, mImePositionProcessor, mWindowManagerProxy);
- mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
- mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */);
- final int size = dctx.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_thickness);
- final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
- final int width = landscape ? size : displayLayout.width();
- final int height = landscape ? displayLayout.height() : size;
- mWindowManager.add(mView, width, height, mContext.getDisplayId());
- }
-
- public void removeDivider() {
- if (mView != null) {
- mView.onDividerRemoved();
- }
- mWindowManager.remove();
- }
-
- public void update(Configuration configuration) {
- final boolean isDividerHidden = mView != null && mIsKeyguardShowing;
-
- removeDivider();
- addDivider(configuration);
-
- if (mMinimized) {
- mView.setMinimizedDockStack(true, mHomeStackResizable, null /* transaction */);
- updateTouchable();
- }
- mView.setHidden(isDividerHidden);
- }
-
- public void onTaskVanished() {
- removeDivider();
- }
-
- public void updateVisibility(final boolean visible) {
- if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible);
- if (mVisible != visible) {
- mVisible = visible;
- mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
-
- if (visible) {
- mView.enterSplitMode(mHomeStackResizable);
- // Update state because animations won't finish.
- mWindowManagerProxy.runInSync(
- t -> mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, t));
-
- } else {
- mView.exitSplitMode();
- mWindowManagerProxy.runInSync(
- t -> mView.setMinimizedDockStack(false, mHomeStackResizable, t));
- }
- // Notify existence listeners
- synchronized (mDockedStackExistsListeners) {
- mDockedStackExistsListeners.removeIf(wf -> {
- Consumer<Boolean> l = wf.get();
- if (l != null) l.accept(visible);
- return l == null;
- });
- }
- }
- }
-
- public void setMinimized(final boolean minimized) {
- if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
- mMainExecutor.execute(() -> {
- if (DEBUG) Slog.d(TAG, "run posted ext setMinimized " + minimized + " vis:" + mVisible);
- if (!mVisible) {
- return;
- }
- setHomeMinimized(minimized);
- });
- }
-
- public void setHomeMinimized(final boolean minimized) {
- if (DEBUG) {
- Slog.d(TAG, "setHomeMinimized min:" + mMinimized + "->" + minimized + " hrsz:"
- + mHomeStackResizable + " split:" + isDividerVisible());
- }
- WindowContainerTransaction wct = new WindowContainerTransaction();
- final boolean minimizedChanged = mMinimized != minimized;
- // Update minimized state
- if (minimizedChanged) {
- mMinimized = minimized;
- }
- // Always set this because we could be entering split when mMinimized is already true
- wct.setFocusable(mSplits.mPrimary.token, !mMinimized);
-
- // Sync state to DividerView if it exists.
- if (mView != null) {
- final int displayId = mView.getDisplay() != null
- ? mView.getDisplay().getDisplayId() : DEFAULT_DISPLAY;
- // pause ime here (before updateMinimizedDockedStack)
- if (mMinimized) {
- mImePositionProcessor.pause(displayId);
- }
- if (minimizedChanged) {
- // This conflicts with IME adjustment, so only call it when things change.
- mView.setMinimizedDockStack(minimized, getAnimDuration(), mHomeStackResizable);
- }
- if (!mMinimized) {
- // afterwards so it can end any animations started in view
- mImePositionProcessor.resume(displayId);
- }
- }
- updateTouchable();
-
- // If we are only setting focusability, a sync transaction isn't necessary (in fact it
- // can interrupt other animations), so see if it can be submitted on pending instead.
- if (!mWindowManagerProxy.queueSyncTransactionIfWaiting(wct)) {
- mTaskOrganizer.applyTransaction(wct);
- }
- }
-
- public void setAdjustedForIme(boolean adjustedForIme) {
- if (mAdjustedForIme == adjustedForIme) {
- return;
- }
- mAdjustedForIme = adjustedForIme;
- updateTouchable();
- }
-
- public void updateTouchable() {
- mWindowManager.setTouchable(!mAdjustedForIme);
- }
-
- public void onUndockingTask() {
- if (mView != null) {
- mView.onUndockingTask();
- }
- }
-
- public void onAppTransitionFinished() {
- if (mView == null) {
- return;
- }
- mForcedResizableController.onAppTransitionFinished();
- }
-
- public void dump(PrintWriter pw) {
- pw.print(" mVisible="); pw.println(mVisible);
- pw.print(" mMinimized="); pw.println(mMinimized);
- pw.print(" mAdjustedForIme="); pw.println(mAdjustedForIme);
- }
-
- public long getAnimDuration() {
- float transitionScale = Settings.Global.getFloat(mContext.getContentResolver(),
- Settings.Global.TRANSITION_ANIMATION_SCALE,
- mContext.getResources().getFloat(
- com.android.internal.R.dimen
- .config_appTransitionAnimationDurationScaleDefault));
- final long transitionDuration = DEFAULT_APP_TRANSITION_DURATION;
- return (long) (transitionDuration * transitionScale);
- }
-
- public void registerInSplitScreenListener(Consumer<Boolean> listener) {
- listener.accept(isDividerVisible());
- synchronized (mDockedStackExistsListeners) {
- mDockedStackExistsListeners.add(new WeakReference<>(listener));
- }
- }
-
- public void unregisterInSplitScreenListener(Consumer<Boolean> listener) {
- synchronized (mDockedStackExistsListeners) {
- for (int i = mDockedStackExistsListeners.size() - 1; i >= 0; i--) {
- if (mDockedStackExistsListeners.get(i) == listener) {
- mDockedStackExistsListeners.remove(i);
- }
- }
- }
- }
-
- public void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) {
- synchronized (mBoundsChangedListeners) {
- mBoundsChangedListeners.add(new WeakReference<>(listener));
- }
- }
-
- public boolean splitPrimaryTask() {
- try {
- if (ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED) {
- return false;
- }
- } catch (RemoteException e) {
- return false;
- }
- if (isSplitActive() || mSplits.mPrimary == null) {
- return false;
- }
-
- // Try fetching the top running task.
- final List<RunningTaskInfo> runningTasks =
- ActivityTaskManager.getInstance().getTasks(1 /* maxNum */);
- if (runningTasks == null || runningTasks.isEmpty()) {
- return false;
- }
- // Note: The set of running tasks from the system is ordered by recency.
- final RunningTaskInfo topRunningTask = runningTasks.get(0);
- final int activityType = topRunningTask.getActivityType();
- if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
- return false;
- }
-
- if (!topRunningTask.supportsSplitScreenMultiWindow) {
- Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
- Toast.LENGTH_SHORT).show();
- return false;
- }
-
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- // Clear out current windowing mode before reparenting to split task.
- wct.setWindowingMode(topRunningTask.token, WINDOWING_MODE_UNDEFINED);
- wct.reparent(topRunningTask.token, mSplits.mPrimary.token, true /* onTop */);
- mWindowManagerProxy.applySyncTransaction(wct);
- return true;
- }
-
- public void dismissSplitToPrimaryTask() {
- startDismissSplit(true /* toPrimaryTask */);
- }
-
- /** Notifies the bounds of split screen changed. */
- public void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) {
- synchronized (mBoundsChangedListeners) {
- mBoundsChangedListeners.removeIf(wf -> {
- BiConsumer<Rect, Rect> l = wf.get();
- if (l != null) l.accept(secondaryWindowBounds, secondaryWindowInsets);
- return l == null;
- });
- }
- }
-
- public void startEnterSplit() {
- update(mDisplayController.getDisplayContext(
- mContext.getDisplayId()).getResources().getConfiguration());
- // Set resizable directly here because applyEnterSplit already resizes home stack.
- mHomeStackResizable = mWindowManagerProxy.applyEnterSplit(mSplits,
- mRotateSplitLayout != null ? mRotateSplitLayout : mSplitLayout);
- }
-
- public void prepareEnterSplitTransition(WindowContainerTransaction outWct) {
- // Set resizable directly here because buildEnterSplit already resizes home stack.
- mHomeStackResizable = mWindowManagerProxy.buildEnterSplit(outWct, mSplits,
- mRotateSplitLayout != null ? mRotateSplitLayout : mSplitLayout);
- }
-
- public void finishEnterSplitTransition(boolean minimized) {
- update(mDisplayController.getDisplayContext(
- mContext.getDisplayId()).getResources().getConfiguration());
- if (minimized) {
- ensureMinimizedSplit();
- } else {
- ensureNormalSplit();
- }
- }
-
- public void startDismissSplit(boolean toPrimaryTask) {
- startDismissSplit(toPrimaryTask, false /* snapped */);
- }
-
- public void startDismissSplit(boolean toPrimaryTask, boolean snapped) {
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mSplits.getSplitTransitions().dismissSplit(
- mSplits, mSplitLayout, !toPrimaryTask, snapped);
- } else {
- mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, !toPrimaryTask);
- onDismissSplit();
- }
- }
-
- public void onDismissSplit() {
- updateVisibility(false /* visible */);
- mMinimized = false;
- // Resets divider bar position to undefined, so new divider bar will apply default position
- // next time entering split mode.
- mDividerState.mRatioPositionBeforeMinimized = 0;
- removeDivider();
- mImePositionProcessor.reset();
- }
-
- public void ensureMinimizedSplit() {
- setHomeMinimized(true /* minimized */);
- if (mView != null && !isDividerVisible()) {
- // Wasn't in split-mode yet, so enter now.
- if (DEBUG) {
- Slog.d(TAG, " entering split mode with minimized=true");
- }
- updateVisibility(true /* visible */);
- }
- }
-
- public void ensureNormalSplit() {
- setHomeMinimized(false /* minimized */);
- if (mView != null && !isDividerVisible()) {
- // Wasn't in split-mode, so enter now.
- if (DEBUG) {
- Slog.d(TAG, " enter split mode unminimized ");
- }
- updateVisibility(true /* visible */);
- }
- }
-
- public LegacySplitDisplayLayout getSplitLayout() {
- return mSplitLayout;
- }
-
- public WindowManagerProxy getWmProxy() {
- return mWindowManagerProxy;
- }
-
- public WindowContainerToken getSecondaryRoot() {
- if (mSplits == null || mSplits.mSecondary == null) {
- return null;
- }
- return mSplits.mSecondary.token;
- }
-
- private class SplitScreenImpl implements LegacySplitScreen {
- @Override
- public boolean isMinimized() {
- return mMinimized;
- }
-
- @Override
- public boolean isHomeStackResizable() {
- return mHomeStackResizable;
- }
-
- /**
- * TODO: Remove usage from outside the shell.
- */
- @Override
- public DividerView getDividerView() {
- return LegacySplitScreenController.this.getDividerView();
- }
-
- @Override
- public boolean isDividerVisible() {
- boolean[] result = new boolean[1];
- try {
- mMainExecutor.executeBlocking(() -> {
- result[0] = LegacySplitScreenController.this.isDividerVisible();
- });
- } catch (InterruptedException e) {
- Slog.e(TAG, "Failed to get divider visible");
- }
- return result[0];
- }
-
- @Override
- public void onKeyguardVisibilityChanged(boolean isShowing) {
- mMainExecutor.execute(() -> {
- LegacySplitScreenController.this.onKeyguardVisibilityChanged(isShowing);
- });
- }
-
- @Override
- public void setMinimized(boolean minimized) {
- mMainExecutor.execute(() -> {
- LegacySplitScreenController.this.setMinimized(minimized);
- });
- }
-
- @Override
- public void onUndockingTask() {
- mMainExecutor.execute(() -> {
- LegacySplitScreenController.this.onUndockingTask();
- });
- }
-
- @Override
- public void onAppTransitionFinished() {
- mMainExecutor.execute(() -> {
- LegacySplitScreenController.this.onAppTransitionFinished();
- });
- }
-
- @Override
- public void registerInSplitScreenListener(Consumer<Boolean> listener) {
- mMainExecutor.execute(() -> {
- LegacySplitScreenController.this.registerInSplitScreenListener(listener);
- });
- }
-
- @Override
- public void unregisterInSplitScreenListener(Consumer<Boolean> listener) {
- mMainExecutor.execute(() -> {
- LegacySplitScreenController.this.unregisterInSplitScreenListener(listener);
- });
- }
-
- @Override
- public void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) {
- mMainExecutor.execute(() -> {
- LegacySplitScreenController.this.registerBoundsChangeListener(listener);
- });
- }
-
- @Override
- public WindowContainerToken getSecondaryRoot() {
- WindowContainerToken[] result = new WindowContainerToken[1];
- try {
- mMainExecutor.executeBlocking(() -> {
- result[0] = LegacySplitScreenController.this.getSecondaryRoot();
- });
- } catch (InterruptedException e) {
- Slog.e(TAG, "Failed to get secondary root");
- }
- return result[0];
- }
-
- @Override
- public boolean splitPrimaryTask() {
- boolean[] result = new boolean[1];
- try {
- mMainExecutor.executeBlocking(() -> {
- result[0] = LegacySplitScreenController.this.splitPrimaryTask();
- });
- } catch (InterruptedException e) {
- Slog.e(TAG, "Failed to split primary task");
- }
- return result[0];
- }
-
- @Override
- public void dismissSplitToPrimaryTask() {
- mMainExecutor.execute(() -> {
- LegacySplitScreenController.this.dismissSplitToPrimaryTask();
- });
- }
-
- @Override
- public void dump(PrintWriter pw) {
- try {
- mMainExecutor.executeBlocking(() -> {
- LegacySplitScreenController.this.dump(pw);
- });
- } catch (InterruptedException e) {
- Slog.e(TAG, "Failed to dump LegacySplitScreenController in 2s");
- }
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
deleted file mode 100644
index d2f42c39acd5..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.legacysplitscreen;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.window.TaskOrganizer;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.SurfaceUtils;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.transition.Transitions;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener {
- private static final String TAG = LegacySplitScreenTaskListener.class.getSimpleName();
- private static final boolean DEBUG = LegacySplitScreenController.DEBUG;
-
- private final ShellTaskOrganizer mTaskOrganizer;
- private final SyncTransactionQueue mSyncQueue;
- private final SparseArray<SurfaceControl> mLeashByTaskId = new SparseArray<>();
-
- // TODO(shell-transitions): Remove when switched to shell-transitions.
- private final SparseArray<Point> mPositionByTaskId = new SparseArray<>();
-
- RunningTaskInfo mPrimary;
- RunningTaskInfo mSecondary;
- SurfaceControl mPrimarySurface;
- SurfaceControl mSecondarySurface;
- SurfaceControl mPrimaryDim;
- SurfaceControl mSecondaryDim;
- Rect mHomeBounds = new Rect();
- final LegacySplitScreenController mSplitScreenController;
- private boolean mSplitScreenSupported = false;
-
- final SurfaceSession mSurfaceSession = new SurfaceSession();
-
- private final LegacySplitScreenTransitions mSplitTransitions;
-
- LegacySplitScreenTaskListener(LegacySplitScreenController splitScreenController,
- ShellTaskOrganizer shellTaskOrganizer,
- Transitions transitions,
- SyncTransactionQueue syncQueue) {
- mSplitScreenController = splitScreenController;
- mTaskOrganizer = shellTaskOrganizer;
- mSplitTransitions = new LegacySplitScreenTransitions(splitScreenController.mTransactionPool,
- transitions, mSplitScreenController, this);
- transitions.addHandler(mSplitTransitions);
- mSyncQueue = syncQueue;
- }
-
- void init() {
- synchronized (this) {
- try {
- mTaskOrganizer.createRootTask(
- DEFAULT_DISPLAY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, this);
- mTaskOrganizer.createRootTask(
- DEFAULT_DISPLAY, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, this);
- } catch (Exception e) {
- // teardown to prevent callbacks
- mTaskOrganizer.removeListener(this);
- throw e;
- }
- }
- }
-
- boolean isSplitScreenSupported() {
- return mSplitScreenSupported;
- }
-
- SurfaceControl.Transaction getTransaction() {
- return mSplitScreenController.mTransactionPool.acquire();
- }
-
- void releaseTransaction(SurfaceControl.Transaction t) {
- mSplitScreenController.mTransactionPool.release(t);
- }
-
- TaskOrganizer getTaskOrganizer() {
- return mTaskOrganizer;
- }
-
- LegacySplitScreenTransitions getSplitTransitions() {
- return mSplitTransitions;
- }
-
- @Override
- public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
- synchronized (this) {
- if (taskInfo.hasParentTask()) {
- handleChildTaskAppeared(taskInfo, leash);
- return;
- }
-
- final int winMode = taskInfo.getWindowingMode();
- if (winMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- ProtoLog.v(WM_SHELL_TASK_ORG,
- "%s onTaskAppeared Primary taskId=%d", TAG, taskInfo.taskId);
- mPrimary = taskInfo;
- mPrimarySurface = leash;
- } else if (winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- ProtoLog.v(WM_SHELL_TASK_ORG,
- "%s onTaskAppeared Secondary taskId=%d", TAG, taskInfo.taskId);
- mSecondary = taskInfo;
- mSecondarySurface = leash;
- } else {
- ProtoLog.v(WM_SHELL_TASK_ORG, "%s onTaskAppeared unknown taskId=%d winMode=%d",
- TAG, taskInfo.taskId, winMode);
- }
-
- if (!mSplitScreenSupported && mPrimarySurface != null && mSecondarySurface != null) {
- mSplitScreenSupported = true;
- mSplitScreenController.onSplitScreenSupported();
- ProtoLog.v(WM_SHELL_TASK_ORG, "%s onTaskAppeared Supported", TAG);
-
- // Initialize dim surfaces:
- SurfaceControl.Transaction t = getTransaction();
- mPrimaryDim = SurfaceUtils.makeDimLayer(
- t, mPrimarySurface, "Primary Divider Dim", mSurfaceSession);
- mSecondaryDim = SurfaceUtils.makeDimLayer(
- t, mSecondarySurface, "Secondary Divider Dim", mSurfaceSession);
- t.apply();
- releaseTransaction(t);
- }
- }
- }
-
- @Override
- public void onTaskVanished(RunningTaskInfo taskInfo) {
- synchronized (this) {
- mPositionByTaskId.remove(taskInfo.taskId);
- if (taskInfo.hasParentTask()) {
- mLeashByTaskId.remove(taskInfo.taskId);
- return;
- }
-
- final boolean isPrimaryTask = mPrimary != null
- && taskInfo.token.equals(mPrimary.token);
- final boolean isSecondaryTask = mSecondary != null
- && taskInfo.token.equals(mSecondary.token);
-
- if (mSplitScreenSupported && (isPrimaryTask || isSecondaryTask)) {
- mSplitScreenSupported = false;
-
- SurfaceControl.Transaction t = getTransaction();
- t.remove(mPrimaryDim);
- t.remove(mSecondaryDim);
- t.remove(mPrimarySurface);
- t.remove(mSecondarySurface);
- t.apply();
- releaseTransaction(t);
-
- mSplitScreenController.onTaskVanished();
- }
- }
- }
-
- @Override
- public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
- if (taskInfo.displayId != DEFAULT_DISPLAY) {
- return;
- }
- synchronized (this) {
- if (!taskInfo.supportsMultiWindow) {
- if (mSplitScreenController.isDividerVisible()) {
- // Dismiss the split screen if the task no longer supports multi window.
- if (taskInfo.taskId == mPrimary.taskId
- || taskInfo.parentTaskId == mPrimary.taskId) {
- // If the primary is focused, dismiss to primary.
- mSplitScreenController
- .startDismissSplit(taskInfo.isFocused /* toPrimaryTask */);
- } else {
- // If the secondary is not focused, dismiss to primary.
- mSplitScreenController
- .startDismissSplit(!taskInfo.isFocused /* toPrimaryTask */);
- }
- }
- return;
- }
- if (taskInfo.hasParentTask()) {
- // changed messages are noisy since it reports on every ensureVisibility. This
- // conflicts with legacy app-transitions which "swaps" the position to a
- // leash. For now, only update when position actually changes to avoid
- // poorly-timed duplicate calls.
- if (taskInfo.positionInParent.equals(mPositionByTaskId.get(taskInfo.taskId))) {
- return;
- }
- handleChildTaskChanged(taskInfo);
- } else {
- handleTaskInfoChanged(taskInfo);
- }
- mPositionByTaskId.put(taskInfo.taskId, new Point(taskInfo.positionInParent));
- }
- }
-
- private void handleChildTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
- mLeashByTaskId.put(taskInfo.taskId, leash);
- mPositionByTaskId.put(taskInfo.taskId, new Point(taskInfo.positionInParent));
- if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
- updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
- }
-
- private void handleChildTaskChanged(RunningTaskInfo taskInfo) {
- if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
- final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId);
- updateChildTaskSurface(taskInfo, leash, false /* firstAppeared */);
- }
-
- private void updateChildTaskSurface(
- RunningTaskInfo taskInfo, SurfaceControl leash, boolean firstAppeared) {
- final Point taskPositionInParent = taskInfo.positionInParent;
- mSyncQueue.runInSync(t -> {
- t.setWindowCrop(leash, null);
- t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y);
- if (firstAppeared && !Transitions.ENABLE_SHELL_TRANSITIONS) {
- t.setAlpha(leash, 1f);
- t.setMatrix(leash, 1, 0, 0, 1);
- t.show(leash);
- }
- });
- }
-
- /**
- * This is effectively a finite state machine which moves between the various split-screen
- * presentations based on the contents of the split regions.
- */
- private void handleTaskInfoChanged(RunningTaskInfo info) {
- if (!mSplitScreenSupported) {
- // This shouldn't happen; but apparently there is a chance that SysUI crashes without
- // system server receiving binder-death (or maybe it receives binder-death too late?).
- // In this situation, when sys-ui restarts, the split root-tasks will still exist so
- // there is a small window of time during init() where WM might send messages here
- // before init() fails. So, avoid a cycle of crashes by returning early.
- Log.e(TAG, "Got handleTaskInfoChanged when not initialized: " + info);
- return;
- }
- final boolean secondaryImpliedMinimize = mSecondary.topActivityType == ACTIVITY_TYPE_HOME
- || (mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS
- && mSplitScreenController.isHomeStackResizable());
- final boolean primaryWasEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
- final boolean secondaryWasEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
- if (info.token.asBinder() == mPrimary.token.asBinder()) {
- mPrimary = info;
- } else if (info.token.asBinder() == mSecondary.token.asBinder()) {
- mSecondary = info;
- }
- if (DEBUG) {
- Log.d(TAG, "onTaskInfoChanged " + mPrimary + " " + mSecondary);
- }
- if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
- final boolean primaryIsEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
- final boolean secondaryIsEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
- final boolean secondaryImpliesMinimize = mSecondary.topActivityType == ACTIVITY_TYPE_HOME
- || (mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS
- && mSplitScreenController.isHomeStackResizable());
- if (primaryIsEmpty == primaryWasEmpty && secondaryWasEmpty == secondaryIsEmpty
- && secondaryImpliedMinimize == secondaryImpliesMinimize) {
- // No relevant changes
- return;
- }
- if (primaryIsEmpty || secondaryIsEmpty) {
- // At-least one of the splits is empty which means we are currently transitioning
- // into or out-of split-screen mode.
- if (DEBUG) {
- Log.d(TAG, " at-least one split empty " + mPrimary.topActivityType
- + " " + mSecondary.topActivityType);
- }
- if (mSplitScreenController.isDividerVisible()) {
- // Was in split-mode, which means we are leaving split, so continue that.
- // This happens when the stack in the primary-split is dismissed.
- if (DEBUG) {
- Log.d(TAG, " was in split, so this means leave it "
- + mPrimary.topActivityType + " " + mSecondary.topActivityType);
- }
- mSplitScreenController.startDismissSplit(false /* toPrimaryTask */);
- } else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) {
- // Wasn't in split-mode (both were empty), but now that the primary split is
- // populated, we should fully enter split by moving everything else into secondary.
- // This just tells window-manager to reparent things, the UI will respond
- // when it gets new task info for the secondary split.
- if (DEBUG) {
- Log.d(TAG, " was not in split, but primary is populated, so enter it");
- }
- mSplitScreenController.startEnterSplit();
- }
- } else if (secondaryImpliesMinimize) {
- // Workaround for b/172686383, we can't rely on the sync bounds change transaction for
- // the home task to finish before the last updateChildTaskSurface() call even if it's
- // queued on the sync transaction queue, so ensure that the home task surface is updated
- // again before we minimize
- final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
- mSplitScreenController.getWmProxy().getHomeAndRecentsTasks(tasks,
- mSplitScreenController.getSecondaryRoot());
- for (int i = 0; i < tasks.size(); i++) {
- final RunningTaskInfo taskInfo = tasks.get(i);
- final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId);
- if (leash != null) {
- updateChildTaskSurface(taskInfo, leash, false /* firstAppeared */);
- }
- }
-
- // Both splits are populated but the secondary split has a home/recents stack on top,
- // so enter minimized mode.
- mSplitScreenController.ensureMinimizedSplit();
- } else {
- // Both splits are populated by normal activities, so make sure we aren't minimized.
- mSplitScreenController.ensureNormalSplit();
- }
- }
-
- @Override
- public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
- b.setParent(findTaskSurface(taskId));
- }
-
- @Override
- public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
- SurfaceControl.Transaction t) {
- t.reparent(sc, findTaskSurface(taskId));
- }
-
- private SurfaceControl findTaskSurface(int taskId) {
- if (!mLeashByTaskId.contains(taskId)) {
- throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
- }
- return mLeashByTaskId.get(taskId);
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw, String prefix) {
- final String innerPrefix = prefix + " ";
- final String childPrefix = innerPrefix + " ";
- pw.println(prefix + this);
- pw.println(innerPrefix + "mSplitScreenSupported=" + mSplitScreenSupported);
- if (mPrimary != null) pw.println(innerPrefix + "mPrimary.taskId=" + mPrimary.taskId);
- if (mSecondary != null) pw.println(innerPrefix + "mSecondary.taskId=" + mSecondary.taskId);
- }
-
- @Override
- public String toString() {
- return TAG;
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
deleted file mode 100644
index b1fa2ac25fe7..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.legacysplitscreen;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
-import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.view.WindowManager.TRANSIT_TO_BACK;
-import static android.view.WindowManager.TRANSIT_TO_FRONT;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.WindowConfiguration;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.window.TransitionInfo;
-import android.window.TransitionRequestInfo;
-import android.window.WindowContainerTransaction;
-
-import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.transition.Transitions;
-
-import java.util.ArrayList;
-
-/** Plays transition animations for split-screen */
-public class LegacySplitScreenTransitions implements Transitions.TransitionHandler {
- private static final String TAG = "SplitScreenTransitions";
-
- public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 10;
-
- private final TransactionPool mTransactionPool;
- private final Transitions mTransitions;
- private final LegacySplitScreenController mSplitScreen;
- private final LegacySplitScreenTaskListener mListener;
-
- private IBinder mPendingDismiss = null;
- private boolean mDismissFromSnap = false;
- private IBinder mPendingEnter = null;
- private IBinder mAnimatingTransition = null;
-
- /** Keeps track of currently running animations */
- private final ArrayList<Animator> mAnimations = new ArrayList<>();
-
- private Transitions.TransitionFinishCallback mFinishCallback = null;
- private SurfaceControl.Transaction mFinishTransaction;
-
- LegacySplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions,
- @NonNull LegacySplitScreenController splitScreen,
- @NonNull LegacySplitScreenTaskListener listener) {
- mTransactionPool = pool;
- mTransitions = transitions;
- mSplitScreen = splitScreen;
- mListener = listener;
- }
-
- @Override
- public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
- @Nullable TransitionRequestInfo request) {
- WindowContainerTransaction out = null;
- final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
- final @WindowManager.TransitionType int type = request.getType();
- if (mSplitScreen.isDividerVisible()) {
- // try to handle everything while in split-screen
- out = new WindowContainerTransaction();
- if (triggerTask != null) {
- final boolean shouldDismiss =
- // if we close the primary-docked task, then leave split-screen since there
- // is nothing behind it.
- ((type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK)
- && triggerTask.parentTaskId == mListener.mPrimary.taskId)
- // if an activity that is not supported in multi window mode is launched,
- // we also need to leave split-screen.
- || ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)
- && !triggerTask.supportsMultiWindow);
- // In both cases, dismiss the primary
- if (shouldDismiss) {
- WindowManagerProxy.buildDismissSplit(out, mListener,
- mSplitScreen.getSplitLayout(), true /* dismiss */);
- if (type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT) {
- out.reorder(triggerTask.token, true /* onTop */);
- }
- mPendingDismiss = transition;
- }
- }
- } else if (triggerTask != null) {
- // Not in split mode, so look for an open with a trigger task.
- if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)
- && triggerTask.configuration.windowConfiguration.getWindowingMode()
- == WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- out = new WindowContainerTransaction();
- mSplitScreen.prepareEnterSplitTransition(out);
- mPendingEnter = transition;
- }
- }
- return out;
- }
-
- // TODO(shell-transitions): real animations
- private void startExampleAnimation(@NonNull SurfaceControl leash, boolean show) {
- final float end = show ? 1.f : 0.f;
- final float start = 1.f - end;
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- final ValueAnimator va = ValueAnimator.ofFloat(start, end);
- va.setDuration(500);
- va.addUpdateListener(animation -> {
- float fraction = animation.getAnimatedFraction();
- transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction);
- transaction.apply();
- });
- final Runnable finisher = () -> {
- transaction.setAlpha(leash, end);
- transaction.apply();
- mTransactionPool.release(transaction);
- mTransitions.getMainExecutor().execute(() -> {
- mAnimations.remove(va);
- onFinish();
- });
- };
- va.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) { }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- finisher.run();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- finisher.run();
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) { }
- });
- mAnimations.add(va);
- mTransitions.getAnimExecutor().execute(va::start);
- }
-
- // TODO(shell-transitions): real animations
- private void startExampleResizeAnimation(@NonNull SurfaceControl leash,
- @NonNull Rect startBounds, @NonNull Rect endBounds) {
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- final ValueAnimator va = ValueAnimator.ofFloat(0.f, 1.f);
- va.setDuration(500);
- va.addUpdateListener(animation -> {
- float fraction = animation.getAnimatedFraction();
- transaction.setWindowCrop(leash,
- (int) (startBounds.width() * (1.f - fraction) + endBounds.width() * fraction),
- (int) (startBounds.height() * (1.f - fraction)
- + endBounds.height() * fraction));
- transaction.setPosition(leash,
- startBounds.left * (1.f - fraction) + endBounds.left * fraction,
- startBounds.top * (1.f - fraction) + endBounds.top * fraction);
- transaction.apply();
- });
- final Runnable finisher = () -> {
- transaction.setWindowCrop(leash, 0, 0);
- transaction.setPosition(leash, endBounds.left, endBounds.top);
- transaction.apply();
- mTransactionPool.release(transaction);
- mTransitions.getMainExecutor().execute(() -> {
- mAnimations.remove(va);
- onFinish();
- });
- };
- va.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- finisher.run();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- finisher.run();
- }
- });
- mAnimations.add(va);
- mTransitions.getAnimExecutor().execute(va::start);
- }
-
- @Override
- public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (transition != mPendingDismiss && transition != mPendingEnter) {
- // If we're not in split-mode, just abort
- if (!mSplitScreen.isDividerVisible()) return false;
- // Check to see if HOME is involved
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- if (change.getTaskInfo() == null
- || change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME) continue;
- if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
- mSplitScreen.ensureMinimizedSplit();
- } else if (change.getMode() == TRANSIT_CLOSE
- || change.getMode() == TRANSIT_TO_BACK) {
- mSplitScreen.ensureNormalSplit();
- }
- }
- // Use normal animations.
- return false;
- }
-
- mFinishCallback = finishCallback;
- mFinishTransaction = mTransactionPool.acquire();
- mAnimatingTransition = transition;
-
- // Play fade animations
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- final SurfaceControl leash = change.getLeash();
- final int mode = info.getChanges().get(i).getMode();
-
- if (mode == TRANSIT_CHANGE) {
- if (change.getParent() != null) {
- // This is probably reparented, so we want the parent to be immediately visible
- final TransitionInfo.Change parentChange = info.getChange(change.getParent());
- startTransaction.show(parentChange.getLeash());
- startTransaction.setAlpha(parentChange.getLeash(), 1.f);
- // and then animate this layer outside the parent (since, for example, this is
- // the home task animating from fullscreen to part-screen).
- startTransaction.reparent(leash, info.getRootLeash());
- startTransaction.setLayer(leash, info.getChanges().size() - i);
- // build the finish reparent/reposition
- mFinishTransaction.reparent(leash, parentChange.getLeash());
- mFinishTransaction.setPosition(leash,
- change.getEndRelOffset().x, change.getEndRelOffset().y);
- }
- // TODO(shell-transitions): screenshot here
- final Rect startBounds = new Rect(change.getStartAbsBounds());
- final boolean isHome = change.getTaskInfo() != null
- && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME;
- if (mPendingDismiss == transition && mDismissFromSnap && !isHome) {
- // Home is special since it doesn't move during fling. Everything else, though,
- // when dismissing from snap, the top/left is at 0,0.
- startBounds.offsetTo(0, 0);
- }
- final Rect endBounds = new Rect(change.getEndAbsBounds());
- startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
- endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
- startExampleResizeAnimation(leash, startBounds, endBounds);
- }
- if (change.getParent() != null) {
- continue;
- }
-
- if (transition == mPendingEnter
- && mListener.mPrimary.token.equals(change.getContainer())
- || mListener.mSecondary.token.equals(change.getContainer())) {
- startTransaction.setWindowCrop(leash, change.getStartAbsBounds().width(),
- change.getStartAbsBounds().height());
- if (mListener.mPrimary.token.equals(change.getContainer())) {
- // Move layer to top since we want it above the oversized home task during
- // animation even though home task is on top in hierarchy.
- startTransaction.setLayer(leash, info.getChanges().size() + 1);
- }
- }
- boolean isOpening = Transitions.isOpeningType(info.getType());
- if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
- // fade in
- startExampleAnimation(leash, true /* show */);
- } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) {
- // fade out
- if (transition == mPendingDismiss && mDismissFromSnap) {
- // Dismissing via snap-to-top/bottom means that the dismissed task is already
- // not-visible (usually cropped to oblivion) so immediately set its alpha to 0
- // and don't animate it so it doesn't pop-in when reparented.
- startTransaction.setAlpha(leash, 0.f);
- } else {
- startExampleAnimation(leash, false /* show */);
- }
- }
- }
- if (transition == mPendingEnter) {
- // If entering, check if we should enter into minimized or normal split
- boolean homeIsVisible = false;
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- if (change.getTaskInfo() == null
- || change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME) {
- continue;
- }
- homeIsVisible = change.getMode() == TRANSIT_OPEN
- || change.getMode() == TRANSIT_TO_FRONT
- || change.getMode() == TRANSIT_CHANGE;
- break;
- }
- mSplitScreen.finishEnterSplitTransition(homeIsVisible);
- }
- startTransaction.apply();
- onFinish();
- return true;
- }
-
- @ExternalThread
- void dismissSplit(LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout,
- boolean dismissOrMaximize, boolean snapped) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- WindowManagerProxy.buildDismissSplit(wct, tiles, layout, dismissOrMaximize);
- mTransitions.getMainExecutor().execute(() -> {
- mDismissFromSnap = snapped;
- mPendingDismiss = mTransitions.startTransition(TRANSIT_SPLIT_DISMISS_SNAP, wct, this);
- });
- }
-
- private void onFinish() {
- if (!mAnimations.isEmpty()) return;
- mFinishTransaction.apply();
- mTransactionPool.release(mFinishTransaction);
- mFinishTransaction = null;
- mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
- mFinishCallback = null;
- if (mAnimatingTransition == mPendingEnter) {
- mPendingEnter = null;
- }
- if (mAnimatingTransition == mPendingDismiss) {
- mSplitScreen.onDismissSplit();
- mPendingDismiss = null;
- }
- mDismissFromSnap = false;
- mAnimatingTransition = null;
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/MinimizedDockShadow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/MinimizedDockShadow.java
deleted file mode 100644
index 1e9223cbe3e2..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/MinimizedDockShadow.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.legacysplitscreen;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.LinearGradient;
-import android.graphics.Paint;
-import android.graphics.Shader;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.WindowManager;
-
-import com.android.wm.shell.R;
-
-/**
- * Shadow for the minimized dock state on homescreen.
- */
-public class MinimizedDockShadow extends View {
-
- private final Paint mShadowPaint = new Paint();
-
- private int mDockSide = WindowManager.DOCKED_INVALID;
-
- public MinimizedDockShadow(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- void setDockSide(int dockSide) {
- if (dockSide != mDockSide) {
- mDockSide = dockSide;
- updatePaint(getLeft(), getTop(), getRight(), getBottom());
- invalidate();
- }
- }
-
- private void updatePaint(int left, int top, int right, int bottom) {
- int startColor = mContext.getResources().getColor(
- R.color.minimize_dock_shadow_start, null);
- int endColor = mContext.getResources().getColor(
- R.color.minimize_dock_shadow_end, null);
- final int middleColor = Color.argb(
- (Color.alpha(startColor) + Color.alpha(endColor)) / 2, 0, 0, 0);
- final int quarter = Color.argb(
- (int) (Color.alpha(startColor) * 0.25f + Color.alpha(endColor) * 0.75f),
- 0, 0, 0);
- if (mDockSide == WindowManager.DOCKED_TOP) {
- mShadowPaint.setShader(new LinearGradient(
- 0, 0, 0, bottom - top,
- new int[] { startColor, middleColor, quarter, endColor },
- new float[] { 0f, 0.35f, 0.6f, 1f }, Shader.TileMode.CLAMP));
- } else if (mDockSide == WindowManager.DOCKED_LEFT) {
- mShadowPaint.setShader(new LinearGradient(
- 0, 0, right - left, 0,
- new int[] { startColor, middleColor, quarter, endColor },
- new float[] { 0f, 0.35f, 0.6f, 1f }, Shader.TileMode.CLAMP));
- } else if (mDockSide == WindowManager.DOCKED_RIGHT) {
- mShadowPaint.setShader(new LinearGradient(
- right - left, 0, 0, 0,
- new int[] { startColor, middleColor, quarter, endColor },
- new float[] { 0f, 0.35f, 0.6f, 1f }, Shader.TileMode.CLAMP));
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (changed) {
- updatePaint(left, top, right, bottom);
- invalidate();
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- canvas.drawRect(0, 0, getWidth(), getHeight(), mShadowPaint);
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
deleted file mode 100644
index e42e43bbc2be..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * 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.wm.shell.legacysplitscreen;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import android.annotation.NonNull;
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.Display;
-import android.view.SurfaceControl;
-import android.view.WindowManagerGlobal;
-import android.window.TaskOrganizer;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.ArrayUtils;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.transition.Transitions;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Proxy to simplify calls into window manager/activity manager
- */
-class WindowManagerProxy {
-
- private static final String TAG = "WindowManagerProxy";
- private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS};
- private static final int[] CONTROLLED_ACTIVITY_TYPES = {
- ACTIVITY_TYPE_STANDARD,
- ACTIVITY_TYPE_HOME,
- ACTIVITY_TYPE_RECENTS,
- ACTIVITY_TYPE_UNDEFINED
- };
- private static final int[] CONTROLLED_WINDOWING_MODES = {
- WINDOWING_MODE_FULLSCREEN,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
- WINDOWING_MODE_UNDEFINED
- };
-
- @GuardedBy("mDockedRect")
- private final Rect mDockedRect = new Rect();
-
- private final Rect mTmpRect1 = new Rect();
-
- @GuardedBy("mDockedRect")
- private final Rect mTouchableRegion = new Rect();
-
- private final SyncTransactionQueue mSyncTransactionQueue;
- private final TaskOrganizer mTaskOrganizer;
-
- WindowManagerProxy(SyncTransactionQueue syncQueue, TaskOrganizer taskOrganizer) {
- mSyncTransactionQueue = syncQueue;
- mTaskOrganizer = taskOrganizer;
- }
-
- void dismissOrMaximizeDocked(final LegacySplitScreenTaskListener tiles,
- LegacySplitDisplayLayout layout, final boolean dismissOrMaximize) {
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- tiles.mSplitScreenController.startDismissSplit(!dismissOrMaximize, true /* snapped */);
- } else {
- applyDismissSplit(tiles, layout, dismissOrMaximize);
- }
- }
-
- public void setResizing(final boolean resizing) {
- try {
- ActivityTaskManager.getService().setSplitScreenResizing(resizing);
- } catch (RemoteException e) {
- Log.w(TAG, "Error calling setDockedStackResizing: " + e);
- }
- }
-
- /** Sets a touch region */
- public void setTouchRegion(Rect region) {
- try {
- synchronized (mDockedRect) {
- mTouchableRegion.set(region);
- }
- WindowManagerGlobal.getWindowManagerService().setDockedTaskDividerTouchRegion(
- mTouchableRegion);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to set touchable region: " + e);
- }
- }
-
- void applyResizeSplits(int position, LegacySplitDisplayLayout splitLayout) {
- WindowContainerTransaction t = new WindowContainerTransaction();
- splitLayout.resizeSplits(position, t);
- applySyncTransaction(t);
- }
-
- boolean getHomeAndRecentsTasks(List<ActivityManager.RunningTaskInfo> out,
- WindowContainerToken parent) {
- boolean resizable = false;
- List<ActivityManager.RunningTaskInfo> rootTasks = parent == null
- ? mTaskOrganizer.getRootTasks(Display.DEFAULT_DISPLAY, HOME_AND_RECENTS)
- : mTaskOrganizer.getChildTasks(parent, HOME_AND_RECENTS);
- for (int i = 0, n = rootTasks.size(); i < n; ++i) {
- final ActivityManager.RunningTaskInfo ti = rootTasks.get(i);
- out.add(ti);
- if (ti.topActivityType == ACTIVITY_TYPE_HOME) {
- resizable = ti.isResizeable;
- }
- }
- return resizable;
- }
-
- /**
- * Assign a fixed override-bounds to home tasks that reflect their geometry while the primary
- * split is minimized. This actually "sticks out" of the secondary split area, but when in
- * minimized mode, the secondary split gets a 'negative' crop to expose it.
- */
- boolean applyHomeTasksMinimized(LegacySplitDisplayLayout layout, WindowContainerToken parent,
- @NonNull WindowContainerTransaction wct) {
- // Resize the home/recents stacks to the larger minimized-state size
- final Rect homeBounds;
- final ArrayList<ActivityManager.RunningTaskInfo> homeStacks = new ArrayList<>();
- boolean isHomeResizable = getHomeAndRecentsTasks(homeStacks, parent);
- if (isHomeResizable) {
- homeBounds = layout.calcResizableMinimizedHomeStackBounds();
- } else {
- // home is not resizable, so lock it to its inherent orientation size.
- homeBounds = new Rect(0, 0, 0, 0);
- for (int i = homeStacks.size() - 1; i >= 0; --i) {
- if (homeStacks.get(i).topActivityType == ACTIVITY_TYPE_HOME) {
- final int orient = homeStacks.get(i).configuration.orientation;
- final boolean displayLandscape = layout.mDisplayLayout.isLandscape();
- final boolean isLandscape = orient == ORIENTATION_LANDSCAPE
- || (orient == ORIENTATION_UNDEFINED && displayLandscape);
- homeBounds.right = isLandscape == displayLandscape
- ? layout.mDisplayLayout.width() : layout.mDisplayLayout.height();
- homeBounds.bottom = isLandscape == displayLandscape
- ? layout.mDisplayLayout.height() : layout.mDisplayLayout.width();
- break;
- }
- }
- }
- for (int i = homeStacks.size() - 1; i >= 0; --i) {
- // For non-resizable homes, the minimized size is actually the fullscreen-size. As a
- // result, we don't minimize for recents since it only shows half-size screenshots.
- if (!isHomeResizable) {
- if (homeStacks.get(i).topActivityType == ACTIVITY_TYPE_RECENTS) {
- continue;
- }
- wct.setWindowingMode(homeStacks.get(i).token, WINDOWING_MODE_FULLSCREEN);
- }
- wct.setBounds(homeStacks.get(i).token, homeBounds);
- }
- layout.mTiles.mHomeBounds.set(homeBounds);
- return isHomeResizable;
- }
-
- /** @see #buildEnterSplit */
- boolean applyEnterSplit(LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout) {
- // Set launchtile first so that any stack created after
- // getAllRootTaskInfos and before reparent (even if unlikely) are placed
- // correctly.
- WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setLaunchRoot(tiles.mSecondary.token, CONTROLLED_WINDOWING_MODES,
- CONTROLLED_ACTIVITY_TYPES);
- final boolean isHomeResizable = buildEnterSplit(wct, tiles, layout);
- applySyncTransaction(wct);
- return isHomeResizable;
- }
-
- /**
- * Finishes entering split-screen by reparenting all FULLSCREEN tasks into the secondary split.
- * This assumes there is already something in the primary split since that is usually what
- * triggers a call to this. In the same transaction, this overrides the home task bounds via
- * {@link #applyHomeTasksMinimized}.
- *
- * @return whether the home stack is resizable
- */
- boolean buildEnterSplit(WindowContainerTransaction outWct, LegacySplitScreenTaskListener tiles,
- LegacySplitDisplayLayout layout) {
- List<ActivityManager.RunningTaskInfo> rootTasks =
- mTaskOrganizer.getRootTasks(DEFAULT_DISPLAY, null /* activityTypes */);
- if (rootTasks.isEmpty()) {
- return false;
- }
- ActivityManager.RunningTaskInfo topHomeTask = null;
- for (int i = rootTasks.size() - 1; i >= 0; --i) {
- final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
- // Check whether the task can be moved to split secondary.
- if (!rootTask.supportsMultiWindow && rootTask.topActivityType != ACTIVITY_TYPE_HOME) {
- continue;
- }
- // Only move split controlling tasks to split secondary.
- final int windowingMode = rootTask.getWindowingMode();
- if (!ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, windowingMode)
- || !ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, rootTask.getActivityType())
- // Excludes split screen secondary due to it's the root we're reparenting to.
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- continue;
- }
- // Since this iterates from bottom to top, update topHomeTask for every fullscreen task
- // so it will be left with the status of the top one.
- topHomeTask = isHomeOrRecentTask(rootTask) ? rootTask : null;
- outWct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */);
- }
- // Move the secondary split-forward.
- outWct.reorder(tiles.mSecondary.token, true /* onTop */);
- boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */,
- outWct);
- if (topHomeTask != null && !Transitions.ENABLE_SHELL_TRANSITIONS) {
- // Translate/update-crop of secondary out-of-band with sync transaction -- Until BALST
- // is enabled, this temporarily syncs the home surface position with offset until
- // sync transaction finishes.
- outWct.setBoundsChangeTransaction(topHomeTask.token, tiles.mHomeBounds);
- }
- return isHomeResizable;
- }
-
- static boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) {
- final int atype = ti.getActivityType();
- return atype == ACTIVITY_TYPE_HOME || atype == ACTIVITY_TYPE_RECENTS;
- }
-
- /** @see #buildDismissSplit */
- void applyDismissSplit(LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout,
- boolean dismissOrMaximize) {
- // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished
- // plus specific APIs to clean this up.
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- // Set launch root first so that any task created after getChildContainers and
- // before reparent (pretty unlikely) are put into fullscreen.
- wct.setLaunchRoot(tiles.mSecondary.token, null, null);
- buildDismissSplit(wct, tiles, layout, dismissOrMaximize);
- applySyncTransaction(wct);
- }
-
- /**
- * Reparents all tile members back to their display and resets home task override bounds.
- * @param dismissOrMaximize When {@code true} this resolves the split by closing the primary
- * split (thus resulting in the top of the secondary split becoming
- * fullscreen. {@code false} resolves the other way.
- */
- static void buildDismissSplit(WindowContainerTransaction outWct,
- LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout,
- boolean dismissOrMaximize) {
- // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished
- // plus specific APIs to clean this up.
- final TaskOrganizer taskOrg = tiles.getTaskOrganizer();
- List<ActivityManager.RunningTaskInfo> primaryChildren =
- taskOrg.getChildTasks(tiles.mPrimary.token, null /* activityTypes */);
- List<ActivityManager.RunningTaskInfo> secondaryChildren =
- taskOrg.getChildTasks(tiles.mSecondary.token, null /* activityTypes */);
- // In some cases (eg. non-resizable is launched), system-server will leave split-screen.
- // as a result, the above will not capture any tasks; yet, we need to clean-up the
- // home task bounds.
- List<ActivityManager.RunningTaskInfo> freeHomeAndRecents =
- taskOrg.getRootTasks(DEFAULT_DISPLAY, HOME_AND_RECENTS);
- // Filter out the root split tasks
- freeHomeAndRecents.removeIf(p -> p.token.equals(tiles.mSecondary.token)
- || p.token.equals(tiles.mPrimary.token));
-
- if (primaryChildren.isEmpty() && secondaryChildren.isEmpty()
- && freeHomeAndRecents.isEmpty()) {
- return;
- }
- if (dismissOrMaximize) {
- // Dismissing, so move all primary split tasks first
- for (int i = primaryChildren.size() - 1; i >= 0; --i) {
- outWct.reparent(primaryChildren.get(i).token, null /* parent */,
- true /* onTop */);
- }
- boolean homeOnTop = false;
- // Don't need to worry about home tasks because they are already in the "proper"
- // order within the secondary split.
- for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
- final ActivityManager.RunningTaskInfo ti = secondaryChildren.get(i);
- outWct.reparent(ti.token, null /* parent */, true /* onTop */);
- if (isHomeOrRecentTask(ti)) {
- outWct.setBounds(ti.token, null);
- outWct.setWindowingMode(ti.token, WINDOWING_MODE_UNDEFINED);
- if (i == 0) {
- homeOnTop = true;
- }
- }
- }
- if (homeOnTop && !Transitions.ENABLE_SHELL_TRANSITIONS) {
- // Translate/update-crop of secondary out-of-band with sync transaction -- instead
- // play this in sync with new home-app frame because until BALST is enabled this
- // shows up on screen before the syncTransaction returns.
- // We only have access to the secondary root surface, though, so in order to
- // position things properly, we have to take into account the existing negative
- // offset/crop of the minimized-home task.
- final boolean landscape = layout.mDisplayLayout.isLandscape();
- final int posX = landscape ? layout.mSecondary.left - tiles.mHomeBounds.left
- : layout.mSecondary.left;
- final int posY = landscape ? layout.mSecondary.top
- : layout.mSecondary.top - tiles.mHomeBounds.top;
- final SurfaceControl.Transaction sft = new SurfaceControl.Transaction();
- sft.setPosition(tiles.mSecondarySurface, posX, posY);
- final Rect crop = new Rect(0, 0, layout.mDisplayLayout.width(),
- layout.mDisplayLayout.height());
- crop.offset(-posX, -posY);
- sft.setWindowCrop(tiles.mSecondarySurface, crop);
- outWct.setBoundsChangeTransaction(tiles.mSecondary.token, sft);
- }
- } else {
- // Maximize, so move non-home secondary split first
- for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
- if (isHomeOrRecentTask(secondaryChildren.get(i))) {
- continue;
- }
- outWct.reparent(secondaryChildren.get(i).token, null /* parent */,
- true /* onTop */);
- }
- // Find and place home tasks in-between. This simulates the fact that there was
- // nothing behind the primary split's tasks.
- for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
- final ActivityManager.RunningTaskInfo ti = secondaryChildren.get(i);
- if (isHomeOrRecentTask(ti)) {
- outWct.reparent(ti.token, null /* parent */, true /* onTop */);
- // reset bounds and mode too
- outWct.setBounds(ti.token, null);
- outWct.setWindowingMode(ti.token, WINDOWING_MODE_UNDEFINED);
- }
- }
- for (int i = primaryChildren.size() - 1; i >= 0; --i) {
- outWct.reparent(primaryChildren.get(i).token, null /* parent */,
- true /* onTop */);
- }
- }
- for (int i = freeHomeAndRecents.size() - 1; i >= 0; --i) {
- outWct.setBounds(freeHomeAndRecents.get(i).token, null);
- outWct.setWindowingMode(freeHomeAndRecents.get(i).token, WINDOWING_MODE_UNDEFINED);
- }
- // Reset focusable to true
- outWct.setFocusable(tiles.mPrimary.token, true /* focusable */);
- }
-
- /**
- * Utility to apply a sync transaction serially with other sync transactions.
- *
- * @see SyncTransactionQueue#queue
- */
- void applySyncTransaction(WindowContainerTransaction wct) {
- mSyncTransactionQueue.queue(wct);
- }
-
- /**
- * @see SyncTransactionQueue#queueIfWaiting
- */
- boolean queueSyncTransactionIfWaiting(WindowContainerTransaction wct) {
- return mSyncTransactionQueue.queueIfWaiting(wct);
- }
-
- /**
- * @see SyncTransactionQueue#runInSync
- */
- void runInSync(SyncTransactionQueue.TransactionRunnable runnable) {
- mSyncTransactionQueue.runInSync(runnable);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index b00182f36cc8..76c0f41997ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.onehanded;
-import android.content.res.Configuration;
import android.os.SystemProperties;
import com.android.wm.shell.common.annotations.ExternalThread;
@@ -83,17 +82,7 @@ public interface OneHanded {
void registerTransitionCallback(OneHandedTransitionCallback callback);
/**
- * Receive onConfigurationChanged() events
- */
- void onConfigChanged(Configuration newConfig);
-
- /**
* Notifies when user switch complete
*/
void onUserSwitch(int userId);
-
- /**
- * Notifies when keyguard visibility changed
- */
- void onKeyguardVisibilityChanged(boolean showing);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 179b725ab210..24f02ac1a6cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -28,17 +28,16 @@ import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
import android.annotation.BinderThread;
import android.content.ComponentName;
import android.content.Context;
-import android.content.om.IOverlayManager;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.Rect;
import android.os.Handler;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Slog;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import android.window.DisplayAreaInfo;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
@@ -55,6 +54,9 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellController;
import java.io.PrintWriter;
@@ -62,7 +64,8 @@ import java.io.PrintWriter;
* Manages and manipulates the one handed states, transitions, and gesture for phones.
*/
public class OneHandedController implements RemoteCallable<OneHandedController>,
- DisplayChangeController.OnDisplayChangingListener {
+ DisplayChangeController.OnDisplayChangingListener, ConfigurationChangeListener,
+ KeyguardChangeListener {
private static final String TAG = "OneHandedController";
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
@@ -82,6 +85,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
private Context mContext;
+ private final ShellController mShellController;
private final AccessibilityManager mAccessibilityManager;
private final DisplayController mDisplayController;
private final OneHandedSettingsUtil mOneHandedSettingsUtil;
@@ -91,7 +95,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
private final OneHandedState mState;
private final OneHandedTutorialHandler mTutorialHandler;
private final TaskStackListenerImpl mTaskStackListener;
- private final IOverlayManager mOverlayManager;
private final ShellExecutor mMainExecutor;
private final Handler mMainHandler;
private final OneHandedImpl mImpl = new OneHandedImpl();
@@ -190,8 +193,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
* Creates {@link OneHandedController}, returns {@code null} if the feature is not supported.
*/
public static OneHandedController create(
- Context context, WindowManager windowManager, DisplayController displayController,
- DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
+ Context context, ShellController shellController, WindowManager windowManager,
+ DisplayController displayController, DisplayLayout displayLayout,
+ TaskStackListenerImpl taskStackListener,
InteractionJankMonitor jankMonitor, UiEventLogger uiEventLogger,
ShellExecutor mainExecutor, Handler mainHandler) {
OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil();
@@ -209,16 +213,15 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
context, displayLayout, settingsUtil, animationController, tutorialHandler,
jankMonitor, mainExecutor);
OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
- IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
- ServiceManager.getService(Context.OVERLAY_SERVICE));
- return new OneHandedController(context, displayController, organizer, touchHandler,
- tutorialHandler, settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState,
- jankMonitor, oneHandedUiEventsLogger, overlayManager, taskStackListener,
+ return new OneHandedController(context, shellController, displayController, organizer,
+ touchHandler, tutorialHandler, settingsUtil, accessibilityUtil, timeoutHandler,
+ oneHandedState, oneHandedUiEventsLogger, taskStackListener,
mainExecutor, mainHandler);
}
@VisibleForTesting
OneHandedController(Context context,
+ ShellController shellController,
DisplayController displayController,
OneHandedDisplayAreaOrganizer displayAreaOrganizer,
OneHandedTouchHandler touchHandler,
@@ -227,13 +230,12 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
OneHandedAccessibilityUtil oneHandedAccessibilityUtil,
OneHandedTimeoutHandler timeoutHandler,
OneHandedState state,
- InteractionJankMonitor jankMonitor,
OneHandedUiEventLogger uiEventsLogger,
- IOverlayManager overlayManager,
TaskStackListenerImpl taskStackListener,
ShellExecutor mainExecutor,
Handler mainHandler) {
mContext = context;
+ mShellController = shellController;
mOneHandedSettingsUtil = settingsUtil;
mOneHandedAccessibilityUtil = oneHandedAccessibilityUtil;
mDisplayAreaOrganizer = displayAreaOrganizer;
@@ -241,7 +243,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mTouchHandler = touchHandler;
mState = state;
mTutorialHandler = tutorialHandler;
- mOverlayManager = overlayManager;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
mOneHandedUiEventLogger = uiEventsLogger;
@@ -279,6 +280,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mAccessibilityStateChangeListener);
mState.addSListeners(mTutorialHandler);
+ mShellController.addConfigurationChangeListener(this);
+ mShellController.addKeyguardChangeListener(this);
}
public OneHanded asOneHanded() {
@@ -594,7 +597,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mLockedDisabled = locked && !enabled;
}
- private void onConfigChanged(Configuration newConfig) {
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
if (mTutorialHandler == null) {
return;
}
@@ -604,8 +608,11 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
mTutorialHandler.onConfigurationChanged();
}
- private void onKeyguardVisibilityChanged(boolean showing) {
- mKeyguardShowing = showing;
+ @Override
+ public void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
+ boolean animatingDismiss) {
+ mKeyguardShowing = visible;
+ stopOneHanded();
}
private void onUserSwitch(int newUserId) {
@@ -659,11 +666,11 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
}
/**
- * Handles rotation based on OnDisplayChangingListener callback
+ * Handles display change based on OnDisplayChangingListener callback
*/
@Override
- public void onRotateDisplay(int displayId, int fromRotation, int toRotation,
- WindowContainerTransaction wct) {
+ public void onDisplayChange(int displayId, int fromRotation, int toRotation,
+ DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
if (!isInitialized()) {
return;
}
@@ -674,9 +681,12 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
return;
}
+ if (mState.getState() == STATE_ACTIVE) {
+ mOneHandedUiEventLogger.writeEvent(
+ OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT);
+ }
+
mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation, wct);
- mOneHandedUiEventLogger.writeEvent(
- OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT);
}
/**
@@ -750,25 +760,11 @@ public class OneHandedController implements RemoteCallable<OneHandedController>,
}
@Override
- public void onConfigChanged(Configuration newConfig) {
- mMainExecutor.execute(() -> {
- OneHandedController.this.onConfigChanged(newConfig);
- });
- }
-
- @Override
public void onUserSwitch(int userId) {
mMainExecutor.execute(() -> {
OneHandedController.this.onUserSwitch(userId);
});
}
-
- @Override
- public void onKeyguardVisibilityChanged(boolean showing) {
- mMainExecutor.execute(() -> {
- OneHandedController.this.onKeyguardVisibilityChanged(showing);
- });
- }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index f61d1b95bd85..451afa08040c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -159,6 +159,10 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
@Override
public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) {
+ final SurfaceControl leash = mDisplayAreaTokenMap.get(displayAreaInfo.token);
+ if (leash != null) {
+ leash.release();
+ }
mDisplayAreaTokenMap.remove(displayAreaInfo.token);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 3b3091a9caf3..38631ce26cd1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.pip;
-import android.content.res.Configuration;
import android.graphics.Rect;
import com.android.wm.shell.common.annotations.ExternalThread;
@@ -44,24 +43,6 @@ public interface Pip {
}
/**
- * Called when configuration is changed.
- */
- default void onConfigurationChanged(Configuration newConfig) {
- }
-
- /**
- * Called when display size or font size of settings changed
- */
- default void onDensityOrFontScaleChanged() {
- }
-
- /**
- * Called when overlay package change invoked.
- */
- default void onOverlayChanged() {
- }
-
- /**
* Called when SysUI state changed.
*
* @param isSysUiStateValid Is SysUI state valid or not.
@@ -86,12 +67,12 @@ public interface Pip {
}
/**
- * Registers the pinned stack animation listener.
+ * Set the callback when {@link PipTaskOrganizer#isInPip()} state is changed.
*
- * @param callback The callback of pinned stack animation.
+ * @param callback The callback accepts the result of {@link PipTaskOrganizer#isInPip()}
+ * when it's changed.
*/
- default void setPinnedStackAnimationListener(Consumer<Boolean> callback) {
- }
+ default void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) {}
/**
* Set the pinned stack with {@link PipAnimationController.AnimationType}
@@ -120,23 +101,6 @@ public interface Pip {
default void removePipExclusionBoundsChangeListener(Consumer<Rect> listener) { }
/**
- * Called when the visibility of keyguard is changed.
- * @param showing {@code true} if keyguard is now showing, {@code false} otherwise.
- * @param animating {@code true} if system is animating between keyguard and surface behind,
- * this only makes sense when showing is {@code false}.
- */
- default void onKeyguardVisibilityChanged(boolean showing, boolean animating) { }
-
- /**
- * Called when the dismissing animation keyguard and surfaces behind is finished.
- * See also {@link #onKeyguardVisibilityChanged(boolean, boolean)}.
- *
- * TODO(b/206741900) deprecate this path once we're able to animate the PiP window as part of
- * keyguard dismiss animation.
- */
- default void onKeyguardDismissAnimationFinished() { }
-
- /**
* Dump the current state and information if need.
*
* @param pw The stream to dump information to.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 4eba1697b595..cf2734c375f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -591,7 +591,7 @@ public class PipAnimationController {
final Rect insets = computeInsets(fraction);
getSurfaceTransactionHelper().scaleAndCrop(tx, leash,
sourceHintRect, initialSourceValue, bounds, insets,
- isInPipDirection);
+ isInPipDirection, fraction);
if (shouldApplyCornerRadius()) {
final Rect sourceBounds = new Rect(initialContainerRect);
sourceBounds.inset(insets);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index a017a2674359..c0bc108baada 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -104,7 +104,7 @@ public class PipSurfaceTransactionHelper {
public PipSurfaceTransactionHelper scaleAndCrop(SurfaceControl.Transaction tx,
SurfaceControl leash, Rect sourceRectHint,
Rect sourceBounds, Rect destinationBounds, Rect insets,
- boolean isInPipDirection) {
+ boolean isInPipDirection, float fraction) {
mTmpDestinationRect.set(sourceBounds);
// Similar to {@link #scale}, we want to position the surface relative to the screen
// coordinates so offset the bounds to 0,0
@@ -116,9 +116,13 @@ public class PipSurfaceTransactionHelper {
if (isInPipDirection
&& sourceRectHint != null && sourceRectHint.width() < sourceBounds.width()) {
// scale by sourceRectHint if it's not edge-to-edge, for entering PiP transition only.
- scale = sourceBounds.width() <= sourceBounds.height()
+ final float endScale = sourceBounds.width() <= sourceBounds.height()
? (float) destinationBounds.width() / sourceRectHint.width()
: (float) destinationBounds.height() / sourceRectHint.height();
+ final float startScale = sourceBounds.width() <= sourceBounds.height()
+ ? (float) destinationBounds.width() / sourceBounds.width()
+ : (float) destinationBounds.height() / sourceBounds.height();
+ scale = (1 - fraction) * startScale + fraction * endScale;
} else {
scale = sourceBounds.width() <= sourceBounds.height()
? (float) destinationBounds.width() / sourceBounds.width()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index e624de661737..da88c2de6c01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -297,6 +297,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
displayController.addDisplayWindowListener(this);
}
+ public PipTransitionController getTransitionController() {
+ return mPipTransitionController;
+ }
+
public Rect getCurrentOrAnimatingBounds() {
PipAnimationController.PipTransitionAnimator animator =
mPipAnimationController.getCurrentAnimator();
@@ -458,7 +462,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
// Cancel the existing animator if there is any.
- cancelCurrentAnimator();
+ // TODO(b/232439933): this is disabled temporarily to unblock b/234502692.
+ // cancelCurrentAnimator();
// Set the exiting state first so if there is fixed rotation later, the running animation
// won't be interrupted by alpha animation for existing PiP.
@@ -937,7 +942,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
// Re-set the PIP bounds to none.
mPipBoundsState.setBounds(new Rect());
mPipUiEventLoggerLogger.setTaskInfo(null);
- mPipMenuController.detach();
+ mMainExecutor.executeDelayed(() -> mPipMenuController.detach(), 0);
+ mLeash = null;
if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) {
mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY);
@@ -1467,6 +1473,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
"%s: Abort animation, invalid leash", TAG);
return null;
}
+ if (isInPipDirection(direction)
+ && !isSourceRectHintValidForEnterPip(sourceHintRect, destinationBounds)) {
+ // The given source rect hint is too small for enter PiP animation, reset it to null.
+ sourceHintRect = null;
+ }
final int rotationDelta = mWaitForFixedRotation
? deltaRotation(mCurrentRotation, mNextRotation)
: Surface.ROTATION_0;
@@ -1541,6 +1552,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
/**
+ * This is a situation in which the source rect hint on at least one axis is smaller
+ * than the destination bounds, which represents a problem because we would have to scale
+ * up that axis to fit the bounds. So instead, just fallback to the non-source hint
+ * animation in this case.
+ *
+ * @return {@code false} if the given source is too small to use for the entering animation.
+ */
+ private boolean isSourceRectHintValidForEnterPip(Rect sourceRectHint, Rect destinationBounds) {
+ return sourceRectHint != null
+ && sourceRectHint.width() > destinationBounds.width()
+ && sourceRectHint.height() > destinationBounds.height();
+ }
+
+ /**
* Sync with {@link SplitScreenController} on destination bounds if PiP is going to
* split screen.
*
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 36e712459863..51be2a534dd7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -28,7 +28,6 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.transitTypeToString;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
-import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
@@ -43,6 +42,7 @@ import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SP
import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
import static com.android.wm.shell.transition.Transitions.isOpeningType;
+import android.animation.Animator;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.Context;
@@ -52,6 +52,7 @@ import android.graphics.Rect;
import android.os.IBinder;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerToken;
@@ -145,6 +146,11 @@ public class PipTransition extends PipTransitionController {
if (destinationBounds != null) {
mExitDestinationBounds.set(destinationBounds);
}
+ final PipAnimationController.PipTransitionAnimator animator =
+ mPipAnimationController.getCurrentAnimator();
+ if (animator != null && animator.isRunning()) {
+ animator.cancel();
+ }
mExitTransition = mTransitions.startTransition(type, out, this);
}
@@ -217,13 +223,20 @@ public class PipTransition extends PipTransitionController {
}
// Entering PIP.
- if (isEnteringPip(info, mCurrentPipTaskToken)) {
- return startEnterAnimation(info, startTransaction, finishTransaction, finishCallback);
+ if (isEnteringPip(info)) {
+ startEnterAnimation(info, startTransaction, finishTransaction, finishCallback);
+ return true;
}
// For transition that we don't animate, but contains the PIP leash, we need to update the
// PIP surface, otherwise it will be reset after the transition.
if (currentPipTaskChange != null) {
+ // Set the "end" bounds of pip. The default setup uses the start bounds. Since this is
+ // changing the *finish*Transaction, we need to use the end bounds. This will also
+ // make sure that the fade-in animation (below) uses the end bounds as well.
+ if (!currentPipTaskChange.getEndAbsBounds().isEmpty()) {
+ mPipBoundsState.setBounds(currentPipTaskChange.getEndAbsBounds());
+ }
updatePipForUnhandledTransition(currentPipTaskChange, startTransaction,
finishTransaction);
}
@@ -236,6 +249,13 @@ public class PipTransition extends PipTransitionController {
return false;
}
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ end();
+ }
+
/** Helper to identify whether this handler is currently the one playing an animation */
private boolean isAnimatingLocally() {
return mFinishTransaction != null;
@@ -245,16 +265,9 @@ public class PipTransition extends PipTransitionController {
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
- if (request.getType() == TRANSIT_PIP) {
+ if (requestHasPipEnter(request)) {
WindowContainerTransaction wct = new WindowContainerTransaction();
- if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
- mRequestedEnterTransition = transition;
- mRequestedEnterTask = request.getTriggerTask().token;
- wct.setActivityWindowingMode(request.getTriggerTask().token,
- WINDOWING_MODE_UNDEFINED);
- final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
- wct.setBounds(request.getTriggerTask().token, destinationBounds);
- }
+ augmentRequest(transition, request, wct);
return wct;
} else {
return null;
@@ -262,6 +275,29 @@ public class PipTransition extends PipTransitionController {
}
@Override
+ public void augmentRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) {
+ if (!requestHasPipEnter(request)) {
+ throw new IllegalStateException("Called PiP augmentRequest when request has no PiP");
+ }
+ if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ mRequestedEnterTransition = transition;
+ mRequestedEnterTask = request.getTriggerTask().token;
+ outWCT.setActivityWindowingMode(request.getTriggerTask().token,
+ WINDOWING_MODE_UNDEFINED);
+ final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
+ outWCT.setBounds(request.getTriggerTask().token, destinationBounds);
+ }
+ }
+
+ @Override
+ public void end() {
+ Animator animator = mPipAnimationController.getCurrentAnimator();
+ if (animator == null) return;
+ animator.end();
+ }
+
+ @Override
public boolean handleRotateDisplay(int startRotation, int endRotation,
WindowContainerTransaction wct) {
if (mRequestedEnterTransition != null && mOneShotAnimationType == ANIM_TYPE_ALPHA) {
@@ -279,7 +315,7 @@ public class PipTransition extends PipTransitionController {
}
@Override
- public void onTransitionMerged(@NonNull IBinder transition) {
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
if (transition != mExitTransition) {
return;
}
@@ -292,7 +328,7 @@ public class PipTransition extends PipTransitionController {
}
// Unset exitTransition AFTER cancel so that finishResize knows we are merging.
mExitTransition = null;
- if (!cancelled) return;
+ if (!cancelled || aborted) return;
final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo();
if (taskInfo != null) {
startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(),
@@ -315,11 +351,27 @@ public class PipTransition extends PipTransitionController {
// (likely a remote like launcher), so don't fire the finish-callback here -- wait until
// the exit transition is merged.
if ((mExitTransition == null || isAnimatingLocally()) && mFinishCallback != null) {
- WindowContainerTransaction wct = new WindowContainerTransaction();
- prepareFinishResizeTransaction(taskInfo, destinationBounds,
- direction, wct);
- if (tx != null) {
- wct.setBoundsChangeTransaction(taskInfo.token, tx);
+ WindowContainerTransaction wct = null;
+ if (isOutPipDirection(direction)) {
+ // Only need to reset surface properties. The server-side operations were already
+ // done at the start.
+ if (tx != null) {
+ mFinishTransaction.merge(tx);
+ }
+ } else {
+ wct = new WindowContainerTransaction();
+ if (isInPipDirection(direction)) {
+ // If we are animating from fullscreen using a bounds animation, then reset the
+ // activity windowing mode, and set the task bounds to the final bounds
+ wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
+ wct.scheduleFinishEnterPip(taskInfo.token, destinationBounds);
+ wct.setBounds(taskInfo.token, destinationBounds);
+ } else {
+ wct.setBounds(taskInfo.token, null /* bounds */);
+ }
+ if (tx != null) {
+ wct.setBoundsChangeTransaction(taskInfo.token, tx);
+ }
}
final SurfaceControl leash = mPipOrganizer.getSurfaceControl();
final int displayRotation = taskInfo.getConfiguration().windowConfiguration
@@ -559,92 +611,94 @@ public class PipTransition extends PipTransitionController {
}
/** Whether we should handle the given {@link TransitionInfo} animation as entering PIP. */
- private static boolean isEnteringPip(@NonNull TransitionInfo info,
- @Nullable WindowContainerToken currentPipTaskToken) {
+ private boolean isEnteringPip(@NonNull TransitionInfo info) {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- if (change.getTaskInfo() != null
- && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED
- && !change.getContainer().equals(currentPipTaskToken)) {
- // We support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
- // that enter PiP instantly on opening, mostly from CTS/Flicker tests)
- if (info.getType() == TRANSIT_PIP || info.getType() == TRANSIT_OPEN) {
- return true;
- }
- // This can happen if the request to enter PIP happens when we are collecting for
- // another transition, such as TRANSIT_CHANGE (display rotation).
- if (info.getType() == TRANSIT_CHANGE) {
- return true;
- }
+ if (isEnteringPip(change, info.getType())) return true;
+ }
+ return false;
+ }
- // Please file a bug to handle the unexpected transition type.
- throw new IllegalStateException("Entering PIP with unexpected transition type="
- + transitTypeToString(info.getType()));
+ /** Whether a particular change is a window that is entering pip. */
+ @Override
+ public boolean isEnteringPip(@NonNull TransitionInfo.Change change,
+ @WindowManager.TransitionType int transitType) {
+ if (change.getTaskInfo() != null
+ && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED
+ && !change.getContainer().equals(mCurrentPipTaskToken)) {
+ // We support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
+ // that enter PiP instantly on opening, mostly from CTS/Flicker tests)
+ if (transitType == TRANSIT_PIP || transitType == TRANSIT_OPEN) {
+ return true;
+ }
+ // This can happen if the request to enter PIP happens when we are collecting for
+ // another transition, such as TRANSIT_CHANGE (display rotation).
+ if (transitType == TRANSIT_CHANGE) {
+ return true;
}
+
+ // Please file a bug to handle the unexpected transition type.
+ throw new IllegalStateException("Entering PIP with unexpected transition type="
+ + transitTypeToString(transitType));
}
return false;
}
- private boolean startEnterAnimation(@NonNull TransitionInfo info,
+ private void startEnterAnimation(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- // Search for an Enter PiP transition (along with a show wallpaper one)
+ // Search for an Enter PiP transition
TransitionInfo.Change enterPip = null;
- TransitionInfo.Change wallpaper = null;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getTaskInfo() != null
&& change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED) {
enterPip = change;
- } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
- wallpaper = change;
}
}
if (enterPip == null) {
- return false;
+ throw new IllegalStateException("Trying to start PiP animation without a pip"
+ + "participant");
}
- // Keep track of the PIP task.
- mCurrentPipTaskToken = enterPip.getContainer();
- mHasFadeOut = false;
- if (mFinishCallback != null) {
- callFinishCallback(null /* wct */);
- mFinishTransaction = null;
- throw new RuntimeException("Previous callback not called, aborting entering PIP.");
- }
-
- // Show the wallpaper if there is a wallpaper change.
- if (wallpaper != null) {
- startTransaction.show(wallpaper.getLeash());
- startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
- }
// Make sure other open changes are visible as entering PIP. Some may be hidden in
// Transitions#setupStartState because the transition type is OPEN (such as auto-enter).
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- if (change == enterPip || change == wallpaper) {
- continue;
- }
+ if (change == enterPip) continue;
if (isOpeningType(change.getMode())) {
final SurfaceControl leash = change.getLeash();
startTransaction.show(leash).setAlpha(leash, 1.f);
}
}
+ startEnterAnimation(enterPip, startTransaction, finishTransaction, finishCallback);
+ }
+
+ @Override
+ public void startEnterAnimation(@NonNull final TransitionInfo.Change pipChange,
+ @NonNull final SurfaceControl.Transaction startTransaction,
+ @NonNull final SurfaceControl.Transaction finishTransaction,
+ @NonNull final Transitions.TransitionFinishCallback finishCallback) {
+ if (mFinishCallback != null) {
+ callFinishCallback(null /* wct */);
+ mFinishTransaction = null;
+ throw new RuntimeException("Previous callback not called, aborting entering PIP.");
+ }
+
+ // Keep track of the PIP task and animation.
+ mCurrentPipTaskToken = pipChange.getContainer();
+ mHasFadeOut = false;
mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
mFinishCallback = finishCallback;
mFinishTransaction = finishTransaction;
- final int endRotation = mInFixedRotation ? mEndFixedRotation : enterPip.getEndRotation();
- return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
- startTransaction, finishTransaction, enterPip.getStartRotation(),
- endRotation);
- }
- private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
- final SurfaceControl.Transaction startTransaction,
- final SurfaceControl.Transaction finishTransaction,
- final int startRotation, final int endRotation) {
+ final ActivityManager.RunningTaskInfo taskInfo = pipChange.getTaskInfo();
+ final SurfaceControl leash = pipChange.getLeash();
+ final int startRotation = pipChange.getStartRotation();
+ final int endRotation = mInFixedRotation ? mEndFixedRotation : pipChange.getEndRotation();
+
setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams,
taskInfo.topActivityInfo);
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
@@ -657,12 +711,11 @@ public class PipTransition extends PipTransitionController {
computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo,
destinationBounds, sourceHintRect);
}
- PipAnimationController.PipTransitionAnimator animator;
// Set corner radius for entering pip.
mSurfaceTransactionHelper
.crop(finishTransaction, leash, destinationBounds)
.round(finishTransaction, leash, true /* applyCornerRadius */);
- mPipMenuController.attach(leash);
+ mTransitions.getMainExecutor().executeDelayed(() -> mPipMenuController.attach(leash), 0);
if (taskInfo.pictureInPictureParams != null
&& taskInfo.pictureInPictureParams.isAutoEnterEnabled()
@@ -694,7 +747,7 @@ public class PipTransition extends PipTransitionController {
null /* callback */, false /* withStartDelay */);
}
mPipTransitionState.setInSwipePipToHomeTransition(false);
- return true;
+ return;
}
if (rotationDelta != Surface.ROTATION_0) {
@@ -702,6 +755,12 @@ public class PipTransition extends PipTransitionController {
tmpTransform.postRotate(rotationDelta);
startTransaction.setMatrix(leash, tmpTransform, new float[9]);
}
+ if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ startTransaction.setAlpha(leash, 0f);
+ }
+ startTransaction.apply();
+
+ PipAnimationController.PipTransitionAnimator animator;
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
@@ -712,7 +771,6 @@ public class PipTransition extends PipTransitionController {
animator.setColorContentOverlay(mContext);
}
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
- startTransaction.setAlpha(leash, 0f);
animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
0f, 1f);
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
@@ -720,7 +778,6 @@ public class PipTransition extends PipTransitionController {
throw new RuntimeException("Unrecognized animation type: "
+ mOneShotAnimationType);
}
- startTransaction.apply();
animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration);
@@ -731,8 +788,6 @@ public class PipTransition extends PipTransitionController {
animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds());
}
animator.start();
-
- return true;
}
/** Computes destination bounds in old rotation and updates source hint rect if available. */
@@ -852,27 +907,4 @@ public class PipTransition extends PipTransitionController {
mPipMenuController.movePipMenu(null, null, destinationBounds);
mPipMenuController.updateMenuBounds(destinationBounds);
}
-
- private void prepareFinishResizeTransaction(TaskInfo taskInfo, Rect destinationBounds,
- @PipAnimationController.TransitionDirection int direction,
- WindowContainerTransaction wct) {
- Rect taskBounds = null;
- if (isInPipDirection(direction)) {
- // If we are animating from fullscreen using a bounds animation, then reset the
- // activity windowing mode set by WM, and set the task bounds to the final bounds
- taskBounds = destinationBounds;
- wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
- wct.scheduleFinishEnterPip(taskInfo.token, destinationBounds);
- } else if (isOutPipDirection(direction)) {
- // If we are animating to fullscreen, then we need to reset the override bounds
- // on the task to ensure that the task "matches" the parent's bounds.
- taskBounds = (direction == TRANSITION_DIRECTION_LEAVE_PIP)
- ? null : destinationBounds;
- wct.setWindowingMode(taskInfo.token, getOutPipWindowingMode());
- // Simply reset the activity mode set prior to the animation running.
- wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
- }
-
- wct.setBounds(taskInfo.token, taskBounds);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 54f46e0c9938..90a2695bdf90 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.pip;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.WindowManager.TRANSIT_PIP;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
@@ -27,11 +28,15 @@ import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
+import android.os.IBinder;
import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
+import androidx.annotation.NonNull;
+
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.transition.Transitions;
@@ -49,7 +54,6 @@ public abstract class PipTransitionController implements Transitions.TransitionH
protected final ShellTaskOrganizer mShellTaskOrganizer;
protected final PipMenuController mPipMenuController;
protected final Transitions mTransitions;
- private final Handler mMainHandler;
private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
protected PipTaskOrganizer mPipOrganizer;
@@ -137,7 +141,6 @@ public abstract class PipTransitionController implements Transitions.TransitionH
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipAnimationController = pipAnimationController;
mTransitions = transitions;
- mMainHandler = new Handler(Looper.getMainLooper());
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
transitions.addHandler(this);
}
@@ -206,6 +209,34 @@ public abstract class PipTransitionController implements Transitions.TransitionH
return false;
}
+ /** @return whether the transition-request represents a pip-entry. */
+ public boolean requestHasPipEnter(@NonNull TransitionRequestInfo request) {
+ return request.getType() == TRANSIT_PIP;
+ }
+
+ /** Whether a particular change is a window that is entering pip. */
+ public boolean isEnteringPip(@NonNull TransitionInfo.Change change,
+ @WindowManager.TransitionType int transitType) {
+ return false;
+ }
+
+ /** Add PiP-related changes to `outWCT` for the given request. */
+ public void augmentRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) {
+ throw new IllegalStateException("Request isn't entering PiP");
+ }
+
+ /** Play a transition animation for entering PiP on a specific PiP change. */
+ public void startEnterAnimation(@NonNull final TransitionInfo.Change pipChange,
+ @NonNull final SurfaceControl.Transaction startTransaction,
+ @NonNull final SurfaceControl.Transaction finishTransaction,
+ @NonNull final Transitions.TransitionFinishCallback finishCallback) {
+ }
+
+ /** End the currently-playing PiP animation. */
+ public void end() {
+ }
+
/**
* Callback interface for PiP transitions (both from and to PiP mode)
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
index 85e56b7dd99f..1a4be3b41911 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
@@ -17,12 +17,15 @@
package com.android.wm.shell.pip;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
/**
* Used to keep track of PiP leash state as it appears and animates by {@link PipTaskOrganizer} and
@@ -37,6 +40,9 @@ public class PipTransitionState {
public static final int ENTERED_PIP = 4;
public static final int EXITING_PIP = 5;
+ private final List<OnPipTransitionStateChangedListener> mOnPipTransitionStateChangedListeners =
+ new ArrayList<>();
+
/**
* If set to {@code true}, no entering PiP transition would be kicked off and most likely
* it's due to the fact that Launcher is handling the transition directly when swiping
@@ -65,7 +71,13 @@ public class PipTransitionState {
}
public void setTransitionState(@TransitionState int state) {
- mState = state;
+ if (mState != state) {
+ for (int i = 0; i < mOnPipTransitionStateChangedListeners.size(); i++) {
+ mOnPipTransitionStateChangedListeners.get(i).onPipTransitionStateChanged(
+ mState, state);
+ }
+ mState = state;
+ }
}
public @TransitionState int getTransitionState() {
@@ -73,8 +85,7 @@ public class PipTransitionState {
}
public boolean isInPip() {
- return mState >= TASK_APPEARED
- && mState != EXITING_PIP;
+ return isInPip(mState);
}
public void setInSwipePipToHomeTransition(boolean inSwipePipToHomeTransition) {
@@ -94,4 +105,23 @@ public class PipTransitionState {
return mState < ENTERING_PIP
|| mState == EXITING_PIP;
}
+
+ public void addOnPipTransitionStateChangedListener(
+ @NonNull OnPipTransitionStateChangedListener listener) {
+ mOnPipTransitionStateChangedListeners.add(listener);
+ }
+
+ public void removeOnPipTransitionStateChangedListener(
+ @NonNull OnPipTransitionStateChangedListener listener) {
+ mOnPipTransitionStateChangedListeners.remove(listener);
+ }
+
+ public static boolean isInPip(@TransitionState int state) {
+ return state >= TASK_APPEARED && state != EXITING_PIP;
+ }
+
+ public interface OnPipTransitionStateChangedListener {
+ void onPipTransitionStateChanged(@TransitionState int oldState,
+ @TransitionState int newState);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
index dc60bcf742ce..29434f73e84b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
@@ -116,7 +116,7 @@ public class PipUtils {
if (taskId <= 0) return null;
try {
return ActivityTaskManager.getService().getTaskSnapshot(
- taskId, isLowResolution);
+ taskId, isLowResolution, false /* takeSnapshotIfNeeded */);
} catch (RemoteException e) {
Log.e(TAG, "Failed to get task snapshot, taskId=" + taskId, e);
return null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 4b1e5f8c0d7c..420d6067f420 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -84,8 +84,12 @@ import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
@@ -99,7 +103,7 @@ import java.util.function.Consumer;
* Manages the picture-in-picture (PIP) UI and states for Phones.
*/
public class PipController implements PipTransitionController.PipTransitionCallback,
- RemoteCallable<PipController> {
+ RemoteCallable<PipController>, ConfigurationChangeListener, KeyguardChangeListener {
private static final String TAG = "PipController";
private Context mContext;
@@ -110,12 +114,15 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private PipAppOpsListener mAppOpsListener;
private PipMediaController mMediaController;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
+ private PipKeepClearAlgorithm mPipKeepClearAlgorithm;
private PipBoundsState mPipBoundsState;
+ private PipMotionHelper mPipMotionHelper;
private PipTouchHandler mTouchHandler;
private PipTransitionController mPipTransitionController;
private TaskStackListenerImpl mTaskStackListener;
private PipParamsChangedForwarder mPipParamsChangedForwarder;
private Optional<OneHandedController> mOneHandedController;
+ private final ShellController mShellController;
protected final PipImpl mImpl;
private final Rect mTmpInsetBounds = new Rect();
@@ -126,11 +133,14 @@ public class PipController implements PipTransitionController.PipTransitionCallb
protected PhonePipMenuController mMenuController;
protected PipTaskOrganizer mPipTaskOrganizer;
+ private PipTransitionState mPipTransitionState;
protected PinnedStackListenerForwarder.PinnedTaskListener mPinnedTaskListener =
new PipControllerPinnedTaskListener();
private boolean mIsKeyguardShowingOrAnimating;
+ private Consumer<Boolean> mOnIsInPipStateChangedListener;
+
private interface PipAnimationListener {
/**
* Notifies the listener that the Pip animation is started.
@@ -156,7 +166,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
* Handler for display rotation changes.
*/
private final DisplayChangeController.OnDisplayChangingListener mRotationController = (
- int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> {
+ displayId, fromRotation, toRotation, newDisplayAreaInfo, t) -> {
if (mPipTransitionController.handleRotateDisplay(fromRotation, toRotation, t)) {
return;
}
@@ -284,11 +294,20 @@ public class PipController implements PipTransitionController.PipTransitionCallb
* Instantiates {@link PipController}, returns {@code null} if the feature not supported.
*/
@Nullable
- public static Pip create(Context context, DisplayController displayController,
- PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipBoundsState pipBoundsState, PipMediaController pipMediaController,
- PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
- PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController,
+ public static Pip create(Context context,
+ ShellController shellController,
+ DisplayController displayController,
+ PipAppOpsListener pipAppOpsListener,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipKeepClearAlgorithm pipKeepClearAlgorithm,
+ PipBoundsState pipBoundsState,
+ PipMotionHelper pipMotionHelper,
+ PipMediaController pipMediaController,
+ PhonePipMenuController phonePipMenuController,
+ PipTaskOrganizer pipTaskOrganizer,
+ PipTransitionState pipTransitionState,
+ PipTouchHandler pipTouchHandler,
+ PipTransitionController pipTransitionController,
WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder,
@@ -300,22 +319,27 @@ public class PipController implements PipTransitionController.PipTransitionCallb
return null;
}
- return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm,
- pipBoundsState, pipMediaController,
- phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController,
+ return new PipController(context, shellController, displayController, pipAppOpsListener,
+ pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper,
+ pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
+ pipTouchHandler, pipTransitionController,
windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
oneHandedController, mainExecutor)
.mImpl;
}
protected PipController(Context context,
+ ShellController shellController,
DisplayController displayController,
PipAppOpsListener pipAppOpsListener,
PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipKeepClearAlgorithm pipKeepClearAlgorithm,
@NonNull PipBoundsState pipBoundsState,
+ PipMotionHelper pipMotionHelper,
PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController,
PipTaskOrganizer pipTaskOrganizer,
+ PipTransitionState pipTransitionState,
PipTouchHandler pipTouchHandler,
PipTransitionController pipTransitionController,
WindowManagerShellWrapper windowManagerShellWrapper,
@@ -331,12 +355,16 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
mContext = context;
+ mShellController = shellController;
mImpl = new PipImpl();
mWindowManagerShellWrapper = windowManagerShellWrapper;
mDisplayController = displayController;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
+ mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
mPipBoundsState = pipBoundsState;
+ mPipMotionHelper = pipMotionHelper;
mPipTaskOrganizer = pipTaskOrganizer;
+ mPipTransitionState = pipTransitionState;
mMainExecutor = mainExecutor;
mMediaController = pipMediaController;
mMenuController = phonePipMenuController;
@@ -363,6 +391,15 @@ public class PipController implements PipTransitionController.PipTransitionCallb
onDisplayChanged(mDisplayController.getDisplayLayout(displayId),
false /* saveRestoreSnapFraction */);
});
+ mPipTransitionState.addOnPipTransitionStateChangedListener((oldState, newState) -> {
+ if (mOnIsInPipStateChangedListener != null) {
+ final boolean wasInPip = PipTransitionState.isInPip(oldState);
+ final boolean nowInPip = PipTransitionState.isInPip(newState);
+ if (nowInPip != wasInPip) {
+ mOnIsInPipStateChangedListener.accept(nowInPip);
+ }
+ }
+ });
mPipBoundsState.setOnMinimalSizeChangeCallback(
() -> {
// The minimal size drives the normal bounds, so they need to be recalculated.
@@ -489,6 +526,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
});
});
+
+ mShellController.addConfigurationChangeListener(this);
+ mShellController.addKeyguardChangeListener(this);
}
@Override
@@ -501,18 +541,21 @@ public class PipController implements PipTransitionController.PipTransitionCallb
return mMainExecutor;
}
- private void onConfigurationChanged(Configuration newConfig) {
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
mPipBoundsAlgorithm.onConfigurationChanged(mContext);
mTouchHandler.onConfigurationChanged();
mPipBoundsState.onConfigurationChanged();
}
- private void onDensityOrFontScaleChanged() {
+ @Override
+ public void onDensityOrFontScaleChanged() {
mPipTaskOrganizer.onDensityOrFontScaleChanged(mContext);
onPipResourceDimensionsChanged();
}
- private void onOverlayChanged() {
+ @Override
+ public void onThemeChanged() {
mTouchHandler.onOverlayChanged();
onDisplayChanged(new DisplayLayout(mContext, mContext.getDisplay()),
false /* saveRestoreSnapFraction */);
@@ -620,21 +663,24 @@ public class PipController implements PipTransitionController.PipTransitionCallb
* finished first to reset the visibility of PiP window.
* See also {@link #onKeyguardDismissAnimationFinished()}
*/
- private void onKeyguardVisibilityChanged(boolean keyguardShowing, boolean animating) {
+ @Override
+ public void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
+ boolean animatingDismiss) {
if (!mPipTaskOrganizer.isInPip()) {
return;
}
- if (keyguardShowing) {
+ if (visible) {
mIsKeyguardShowingOrAnimating = true;
hidePipMenu(null /* onStartCallback */, null /* onEndCallback */);
mPipTaskOrganizer.setPipVisibility(false);
- } else if (!animating) {
+ } else if (!animatingDismiss) {
mIsKeyguardShowingOrAnimating = false;
mPipTaskOrganizer.setPipVisibility(true);
}
}
- private void onKeyguardDismissAnimationFinished() {
+ @Override
+ public void onKeyguardDismissAnimationFinished() {
if (mPipTaskOrganizer.isInPip()) {
mIsKeyguardShowingOrAnimating = false;
mPipTaskOrganizer.setPipVisibility(true);
@@ -657,6 +703,13 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
}
+ private void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) {
+ mOnIsInPipStateChangedListener = callback;
+ if (mOnIsInPipStateChangedListener != null) {
+ callback.accept(mPipTransitionState.isInPip());
+ }
+ }
+
private void setShelfHeightLocked(boolean visible, int height) {
final int shelfHeight = visible ? height : 0;
mPipBoundsState.setShelfVisibility(visible, shelfHeight);
@@ -892,27 +945,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
- mMainExecutor.execute(() -> {
- PipController.this.onConfigurationChanged(newConfig);
- });
- }
-
- @Override
- public void onDensityOrFontScaleChanged() {
- mMainExecutor.execute(() -> {
- PipController.this.onDensityOrFontScaleChanged();
- });
- }
-
- @Override
- public void onOverlayChanged() {
- mMainExecutor.execute(() -> {
- PipController.this.onOverlayChanged();
- });
- }
-
- @Override
public void onSystemUiStateChanged(boolean isSysUiStateValid, int flag) {
mMainExecutor.execute(() -> {
PipController.this.onSystemUiStateChanged(isSysUiStateValid, flag);
@@ -934,6 +966,13 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
@Override
+ public void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) {
+ mMainExecutor.execute(() -> {
+ PipController.this.setOnIsInPipStateChangedListener(callback);
+ });
+ }
+
+ @Override
public void setPinnedStackAnimationType(int animationType) {
mMainExecutor.execute(() -> {
PipController.this.setPinnedStackAnimationType(animationType);
@@ -962,18 +1001,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
@Override
- public void onKeyguardVisibilityChanged(boolean showing, boolean animating) {
- mMainExecutor.execute(() -> {
- PipController.this.onKeyguardVisibilityChanged(showing, animating);
- });
- }
-
- @Override
- public void onKeyguardDismissAnimationFinished() {
- mMainExecutor.execute(PipController.this::onKeyguardDismissAnimationFinished);
- }
-
- @Override
public void dump(PrintWriter pw) {
try {
mMainExecutor.executeBlocking(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java
new file mode 100644
index 000000000000..78084fafe197
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.pip.phone;
+
+import android.graphics.Rect;
+import android.util.ArraySet;
+
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
+
+import java.util.Set;
+
+/**
+ * Calculates the adjusted position that does not occlude keep clear areas.
+ */
+public class PipKeepClearAlgorithm {
+
+ /**
+ * Adjusts the current position of PiP to avoid occluding keep clear areas. If the user has
+ * moved PiP manually, the unmodified current position will be returned instead.
+ */
+ public Rect adjust(PipBoundsState boundsState, PipBoundsAlgorithm boundsAlgorithm) {
+ if (boundsState.hasUserResizedPip()) {
+ return boundsState.getBounds();
+ }
+ return adjust(boundsAlgorithm.getEntryDestinationBounds(),
+ boundsState.getRestrictedKeepClearAreas(),
+ boundsState.getUnrestrictedKeepClearAreas(), boundsState.getDisplayBounds());
+ }
+
+ /** Returns a new {@code Rect} that does not occlude the provided keep clear areas. */
+ public Rect adjust(Rect defaultBounds, Set<Rect> restrictedKeepClearAreas,
+ Set<Rect> unrestrictedKeepClearAreas, Rect displayBounds) {
+ if (restrictedKeepClearAreas.isEmpty()) {
+ return defaultBounds;
+ }
+ Set<Rect> keepClearAreas = new ArraySet<>();
+ if (!restrictedKeepClearAreas.isEmpty()) {
+ keepClearAreas.addAll(restrictedKeepClearAreas);
+ }
+ Rect outBounds = new Rect(defaultBounds);
+ for (Rect r : keepClearAreas) {
+ if (Rect.intersects(r, outBounds)) {
+ if (tryOffsetUp(outBounds, r, displayBounds)) continue;
+ if (tryOffsetLeft(outBounds, r, displayBounds)) continue;
+ if (tryOffsetDown(outBounds, r, displayBounds)) continue;
+ if (tryOffsetRight(outBounds, r, displayBounds)) continue;
+ }
+ }
+ return outBounds;
+ }
+
+ private boolean tryOffsetLeft(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) {
+ return tryOffset(rectToMove, rectToAvoid, displayBounds,
+ rectToAvoid.left - rectToMove.right, 0);
+ }
+
+ private boolean tryOffsetRight(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) {
+ return tryOffset(rectToMove, rectToAvoid, displayBounds,
+ rectToAvoid.right - rectToMove.left, 0);
+ }
+
+ private boolean tryOffsetUp(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) {
+ return tryOffset(rectToMove, rectToAvoid, displayBounds,
+ 0, rectToAvoid.top - rectToMove.bottom);
+ }
+
+ private boolean tryOffsetDown(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) {
+ return tryOffset(rectToMove, rectToAvoid, displayBounds,
+ 0, rectToAvoid.bottom - rectToMove.top);
+ }
+
+ private boolean tryOffset(Rect rectToMove, Rect rectToAvoid, Rect displayBounds,
+ int dx, int dy) {
+ Rect tmp = new Rect(rectToMove);
+ tmp.offset(dx, dy);
+ if (!Rect.intersects(rectToAvoid, tmp) && displayBounds.contains(tmp)) {
+ rectToMove.offsetTo(tmp.left, tmp.top);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 6390c8984dac..1958157fc319 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -282,7 +282,7 @@ public class PipMenuView extends FrameLayout {
&& mSplitScreenControllerOptional.get().isTaskInSplitScreen(taskInfo.taskId);
mFocusedTaskAllowSplitScreen = isSplitScreen
|| (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
- && taskInfo.supportsSplitScreenMultiWindow
+ && taskInfo.supportsMultiWindow
&& taskInfo.topActivityType != WindowConfiguration.ACTIVITY_TYPE_HOME);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index ac7b9033b2b9..a2ff97247189 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -54,6 +54,7 @@ import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -250,6 +251,10 @@ public class PipTouchHandler {
});
}
+ public PipTransitionController getTransitionHandler() {
+ return mPipTaskOrganizer.getTransitionController();
+ }
+
private void reloadResources() {
final Resources res = mContext.getResources();
mBottomOffsetBufferPx = res.getDimensionPixelSize(R.dimen.pip_bottom_offset_buffer);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index fa48def9c7d7..a24d9618032d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -49,6 +49,8 @@ import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.ShellController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -61,7 +63,8 @@ import java.util.Set;
*/
public class TvPipController implements PipTransitionController.PipTransitionCallback,
TvPipBoundsController.PipBoundsListener, TvPipMenuController.Delegate,
- TvPipNotificationController.Delegate, DisplayController.OnDisplaysChangedListener {
+ TvPipNotificationController.Delegate, DisplayController.OnDisplaysChangedListener,
+ ConfigurationChangeListener {
private static final String TAG = "TvPipController";
static final boolean DEBUG = false;
@@ -93,6 +96,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private final Context mContext;
+ private final ShellController mShellController;
private final TvPipBoundsState mTvPipBoundsState;
private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
private final TvPipBoundsController mTvPipBoundsController;
@@ -117,6 +121,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
public static Pip create(
Context context,
+ ShellController shellController,
TvPipBoundsState tvPipBoundsState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
@@ -133,6 +138,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
ShellExecutor mainExecutor) {
return new TvPipController(
context,
+ shellController,
tvPipBoundsState,
tvPipBoundsAlgorithm,
tvPipBoundsController,
@@ -151,6 +157,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private TvPipController(
Context context,
+ ShellController shellController,
TvPipBoundsState tvPipBoundsState,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
@@ -167,6 +174,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
ShellExecutor mainExecutor) {
mContext = context;
mMainExecutor = mainExecutor;
+ mShellController = shellController;
mTvPipBoundsState = tvPipBoundsState;
mTvPipBoundsState.setDisplayId(context.getDisplayId());
@@ -193,9 +201,12 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
registerTaskStackListenerCallback(taskStackListener);
registerWmShellPinnedStackListener(wmShell);
displayController.addDisplayWindowListener(this);
+
+ mShellController.addConfigurationChangeListener(this);
}
- private void onConfigurationChanged(Configuration newConfig) {
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
if (DEBUG) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: onConfigurationChanged(), state=%s", TAG, stateToName(mState));
@@ -669,13 +680,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private class TvPipImpl implements Pip {
@Override
- public void onConfigurationChanged(Configuration newConfig) {
- mMainExecutor.execute(() -> {
- TvPipController.this.onConfigurationChanged(newConfig);
- });
- }
-
- @Override
public void registerSessionListenerForCurrentUser() {
mMainExecutor.execute(() -> {
TvPipController.this.registerSessionListenerForCurrentUser();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index d04c34916256..b2961518b66a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -26,6 +26,8 @@ import com.android.internal.protolog.common.IProtoLogGroup;
public enum ShellProtoLogGroup implements IProtoLogGroup {
// NOTE: Since we enable these from the same WM ShellCommand, these names should not conflict
// with those in the framework ProtoLogGroup
+ WM_SHELL_INIT(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+ Consts.TAG_WM_SHELL),
WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
@@ -38,10 +40,12 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
"ShellBackPreview"),
WM_SHELL_RECENT_TASKS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
- WM_SHELL_PICTURE_IN_PICTURE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG,
- false, Consts.TAG_WM_SHELL),
+ WM_SHELL_PICTURE_IN_PICTURE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM_SHELL),
WM_SHELL_SPLIT_SCREEN(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
+ WM_SHELL_SYSUI_EVENTS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM_SHELL),
TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
private final boolean mEnabled;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index c166178e9bbd..0f7a4daf6d08 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -44,7 +44,7 @@ import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
-import com.android.wm.shell.util.StagedSplitBounds;
+import com.android.wm.shell.util.SplitBounds;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -69,12 +69,12 @@ public class RecentTasksController implements TaskStackListenerCallback,
// pair, then mSplitTasks[t1] = t2, and mSplitTasks[t2] = t1)
private final SparseIntArray mSplitTasks = new SparseIntArray();
/**
- * Maps taskId to {@link StagedSplitBounds} for both taskIDs.
+ * Maps taskId to {@link SplitBounds} for both taskIDs.
* Meaning there will be two taskId integers mapping to the same object.
* If there's any ordering to the pairing than we can probably just get away with only one
* taskID mapping to it, leaving both for consistency with {@link #mSplitTasks} for now.
*/
- private final Map<Integer, StagedSplitBounds> mTaskSplitBoundsMap = new HashMap<>();
+ private final Map<Integer, SplitBounds> mTaskSplitBoundsMap = new HashMap<>();
/**
* Creates {@link RecentTasksController}, returns {@code null} if the feature is not
@@ -110,7 +110,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
/**
* Adds a split pair. This call does not validate the taskIds, only that they are not the same.
*/
- public void addSplitPair(int taskId1, int taskId2, StagedSplitBounds splitBounds) {
+ public void addSplitPair(int taskId1, int taskId2, SplitBounds splitBounds) {
if (taskId1 == taskId2) {
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index ae5e075c4d3f..b0080b24c609 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -16,7 +16,9 @@
package com.android.wm.shell.splitscreen;
-import android.annotation.Nullable;
+import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
+import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
+
import android.content.Context;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
@@ -38,10 +40,14 @@ class MainStage extends StageTaskListener {
MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider,
- @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
- super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider,
- stageTaskUnfoldController);
+ SurfaceSession surfaceSession, IconProvider iconProvider) {
+ super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
+ iconProvider);
+ }
+
+ @Override
+ void dismiss(WindowContainerTransaction wct, boolean toTop) {
+ deactivate(wct, toTop);
}
boolean isActive() {
@@ -76,10 +82,10 @@ class MainStage extends StageTaskListener {
if (mRootTaskInfo == null) return;
final WindowContainerToken rootToken = mRootTaskInfo.token;
wct.reparentTasks(
- rootToken,
- null /* newParent */,
- CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
- CONTROLLED_ACTIVITY_TYPES,
- toTop);
+ rootToken,
+ null /* newParent */,
+ null /* windowingModes */,
+ null /* activityTypes */,
+ toTop);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index d55619f5e5ed..86efbe0af79c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.splitscreen;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.view.SurfaceSession;
@@ -38,10 +37,14 @@ class SideStage extends StageTaskListener {
SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider,
- @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
- super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider,
- stageTaskUnfoldController);
+ SurfaceSession surfaceSession, IconProvider iconProvider) {
+ super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
+ iconProvider);
+ }
+
+ @Override
+ void dismiss(WindowContainerTransaction wct, boolean toTop) {
+ removeAllTasks(wct, toTop);
}
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
@@ -49,8 +52,8 @@ class SideStage extends StageTaskListener {
wct.reparentTasks(
mRootTaskInfo.token,
null /* newParent */,
- CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
- CONTROLLED_ACTIVITY_TYPES,
+ null /* windowingModes */,
+ null /* activityTypes */,
toTop);
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 448773ae9ea2..e73b799b7a3d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.graphics.Rect;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
@@ -58,6 +59,7 @@ public interface SplitScreen {
interface SplitScreenListener {
default void onStagePositionChanged(@StageType int stage, @SplitPosition int position) {}
default void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {}
+ default void onSplitBoundsChanged(Rect rootBounds, Rect mainBounds, Rect sideBounds) {}
default void onSplitVisibilityChanged(boolean visible) {}
}
@@ -75,12 +77,6 @@ public interface SplitScreen {
return null;
}
- /**
- * Called when the visibility of the keyguard changes.
- * @param showing Indicates if the keyguard is now visible.
- */
- void onKeyguardVisibilityChanged(boolean showing);
-
/** Called when device waking up finished. */
void onFinishedWakingUp();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 31b510c38457..53ec39d954c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
@@ -58,7 +59,9 @@ import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -74,8 +77,11 @@ import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
+import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
@@ -83,11 +89,10 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Executor;
-import javax.inject.Provider;
-
/**
* Class manages split-screen multitasking mode and implements the main interface
* {@link SplitScreen}.
@@ -96,19 +101,19 @@ import javax.inject.Provider;
*/
// TODO(b/198577848): Implement split screen flicker test to consolidate CUJ of split screen.
public class SplitScreenController implements DragAndDropPolicy.Starter,
- RemoteCallable<SplitScreenController> {
+ RemoteCallable<SplitScreenController>, KeyguardChangeListener {
private static final String TAG = SplitScreenController.class.getSimpleName();
- static final int EXIT_REASON_UNKNOWN = 0;
- static final int EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW = 1;
- static final int EXIT_REASON_APP_FINISHED = 2;
- static final int EXIT_REASON_DEVICE_FOLDED = 3;
- static final int EXIT_REASON_DRAG_DIVIDER = 4;
- static final int EXIT_REASON_RETURN_HOME = 5;
- static final int EXIT_REASON_ROOT_TASK_VANISHED = 6;
- static final int EXIT_REASON_SCREEN_LOCKED = 7;
- static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8;
- static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
+ public static final int EXIT_REASON_UNKNOWN = 0;
+ public static final int EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW = 1;
+ public static final int EXIT_REASON_APP_FINISHED = 2;
+ public static final int EXIT_REASON_DEVICE_FOLDED = 3;
+ public static final int EXIT_REASON_DRAG_DIVIDER = 4;
+ public static final int EXIT_REASON_RETURN_HOME = 5;
+ public static final int EXIT_REASON_ROOT_TASK_VANISHED = 6;
+ public static final int EXIT_REASON_SCREEN_LOCKED = 7;
+ public static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8;
+ public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
@IntDef(value = {
EXIT_REASON_UNKNOWN,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
@@ -124,6 +129,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@Retention(RetentionPolicy.SOURCE)
@interface ExitReason{}
+ private final ShellController mShellController;
private final ShellTaskOrganizer mTaskOrganizer;
private final SyncTransactionQueue mSyncQueue;
private final Context mContext;
@@ -138,22 +144,22 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private final SplitscreenEventLogger mLogger;
private final IconProvider mIconProvider;
private final Optional<RecentTasksController> mRecentTasksOptional;
- private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider;
private StageCoordinator mStageCoordinator;
// Only used for the legacy recents animation from splitscreen to allow the tasks to be animated
// outside the bounds of the roots by being reparented into a higher level fullscreen container
private SurfaceControl mSplitTasksContainerLayer;
- public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
+ public SplitScreenController(ShellController shellController,
+ ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, Context context,
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
ShellExecutor mainExecutor, DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider,
- Optional<RecentTasksController> recentTasks,
- Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
+ Optional<RecentTasksController> recentTasks) {
+ mShellController = shellController;
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
mContext = context;
@@ -164,7 +170,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mDisplayInsetsController = displayInsetsController;
mTransitions = transitions;
mTransactionPool = transactionPool;
- mUnfoldControllerProvider = unfoldControllerProvider;
mLogger = new SplitscreenEventLogger();
mIconProvider = iconProvider;
mRecentTasksOptional = recentTasks;
@@ -185,12 +190,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
public void onOrganizerRegistered() {
+ mShellController.addKeyguardChangeListener(this);
if (mStageCoordinator == null) {
// TODO: Multi-display
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
- mIconProvider, mMainExecutor, mRecentTasksOptional, mUnfoldControllerProvider);
+ mIconProvider, mMainExecutor, mRecentTasksOptional);
}
}
@@ -198,6 +204,18 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return mStageCoordinator.isSplitScreenVisible();
}
+ public StageCoordinator getTransitionHandler() {
+ return mStageCoordinator;
+ }
+
+ public ActivityManager.RunningTaskInfo getFocusingTaskInfo() {
+ return mStageCoordinator.getFocusingTaskInfo();
+ }
+
+ public boolean isValidToEnterSplitScreen(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
+ return mStageCoordinator.isValidToEnterSplitScreen(taskInfo);
+ }
+
@Nullable
public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) {
@@ -222,6 +240,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
new WindowContainerTransaction());
}
+ /**
+ * Update surfaces of the split screen layout based on the current state
+ * @param transaction to write the updates to
+ */
+ public void updateSplitScreenSurfaces(SurfaceControl.Transaction transaction) {
+ mStageCoordinator.updateSurfaces(transaction);
+ }
+
private boolean moveToStage(int taskId, @StageType int stageType,
@SplitPosition int stagePosition, WindowContainerTransaction wct) {
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
@@ -263,8 +289,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason);
}
- public void onKeyguardVisibilityChanged(boolean showing) {
- mStageCoordinator.onKeyguardVisibilityChanged(showing);
+ @Override
+ public void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
+ boolean animatingDismiss) {
+ mStageCoordinator.onKeyguardVisibilityChanged(visible);
}
public void onFinishedWakingUp() {
@@ -333,14 +361,21 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
null /* wct */);
- // Flag this as a no-user-action launch to prevent sending user leaving event to the
- // current top activity since it's going to be put into another side of the split. This
- // prevents the current top activity from going into pip mode due to user leaving event.
if (fillInIntent == null) {
fillInIntent = new Intent();
}
+ // Flag this as a no-user-action launch to prevent sending user leaving event to the
+ // current top activity since it's going to be put into another side of the split. This
+ // prevents the current top activity from going into pip mode due to user leaving event.
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
+ // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
+ // split.
+ if (isLaunchingAdjacently(intent.getIntent(), position)) {
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
+ }
+
intent.send(mContext, 0, fillInIntent, null /* onFinished */, null /* handler */,
null /* requiredPermission */, options);
} catch (PendingIntent.CanceledException e) {
@@ -350,6 +385,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private void startIntentLegacy(PendingIntent intent, @Nullable Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options) {
+ boolean startSameActivityAdjacently = isLaunchingAdjacently(intent.getIntent(), position);
+
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
mStageCoordinator.prepareEvictChildTasks(position, evictWct);
@@ -360,13 +397,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
IRemoteAnimationFinishedCallback finishedCallback,
SurfaceControl.Transaction t) {
if (apps == null || apps.length == 0) {
- final ActivityManager.RunningTaskInfo pairedTaskInfo =
- getTaskInfo(SplitLayout.reversePosition(position));
- final ComponentName pairedActivity =
- pairedTaskInfo != null ? pairedTaskInfo.baseActivity : null;
- final ComponentName intentActivity =
- intent.getIntent() != null ? intent.getIntent().getComponent() : null;
- if (pairedActivity != null && pairedActivity.equals(intentActivity)) {
+ if (startSameActivityAdjacently) {
// Switch split position if dragging the same activity to another side.
setSideStagePosition(SplitLayout.reversePosition(
mStageCoordinator.getSideStagePosition()));
@@ -408,11 +439,44 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
fillInIntent = new Intent();
}
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
+ if (startSameActivityAdjacently) {
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
+ }
wct.sendPendingIntent(intent, fillInIntent, options);
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
}
+ /** Returns {@code true} if it's launching the same component on both sides of the split. */
+ @VisibleForTesting
+ boolean isLaunchingAdjacently(@Nullable Intent startIntent,
+ @SplitPosition int position) {
+ if (startIntent == null) {
+ return false;
+ }
+
+ final ComponentName launchingActivity = startIntent.getComponent();
+ if (launchingActivity == null) {
+ return false;
+ }
+
+ if (isSplitScreenVisible()) {
+ final ActivityManager.RunningTaskInfo pairedTaskInfo =
+ getTaskInfo(SplitLayout.reversePosition(position));
+ final ComponentName pairedActivity = pairedTaskInfo != null
+ ? pairedTaskInfo.baseIntent.getComponent() : null;
+ return Objects.equals(launchingActivity, pairedActivity);
+ }
+
+ final ActivityManager.RunningTaskInfo taskInfo = getFocusingTaskInfo();
+ if (taskInfo != null && isValidToEnterSplitScreen(taskInfo)) {
+ return Objects.equals(taskInfo.baseIntent.getComponent(), launchingActivity);
+ }
+
+ return false;
+ }
+
RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
if (isSplitScreenVisible()) {
// Evict child tasks except the top visible one under split root to ensure it could be
@@ -425,7 +489,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
- return reparentSplitTasksForAnimation(apps, false /*splitExpectedToBeVisible*/);
+ try {
+ return reparentSplitTasksForAnimation(apps, false /*splitExpectedToBeVisible*/);
+ } finally {
+ for (RemoteAnimationTarget appTarget : apps) {
+ if (appTarget.leash != null) {
+ appTarget.leash.release();
+ }
+ }
+ }
}
private RemoteAnimationTarget[] reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
@@ -535,6 +607,17 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
@Override
+ public void onSplitBoundsChanged(Rect rootBounds, Rect mainBounds, Rect sideBounds) {
+ for (int i = 0; i < mExecutors.size(); i++) {
+ final int index = i;
+ mExecutors.valueAt(index).execute(() -> {
+ mExecutors.keyAt(index).onSplitBoundsChanged(rootBounds, mainBounds,
+ sideBounds);
+ });
+ }
+ }
+
+ @Override
public void onSplitVisibilityChanged(boolean visible) {
for (int i = 0; i < mExecutors.size(); i++) {
final int index = i;
@@ -583,13 +666,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
@Override
- public void onKeyguardVisibilityChanged(boolean showing) {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.onKeyguardVisibilityChanged(showing);
- });
- }
-
- @Override
public void onFinishedWakingUp() {
mMainExecutor.execute(() -> {
SplitScreenController.this.onFinishedWakingUp();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index cd121ed41fdd..056cd5813861 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -21,7 +21,6 @@ import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
-import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
@@ -58,9 +57,6 @@ import java.util.ArrayList;
class SplitScreenTransitions {
private static final String TAG = "SplitScreenTransitions";
- /** Flag applied to a transition change to identify it as a divider bar for animation. */
- public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM;
-
private final TransactionPool mTransactionPool;
private final Transitions mTransitions;
private final Runnable mOnFinish;
@@ -70,8 +66,9 @@ class SplitScreenTransitions {
IBinder mPendingRecent = null;
private IBinder mAnimatingTransition = null;
- private OneShotRemoteHandler mPendingRemoteHandler = null;
+ OneShotRemoteHandler mPendingRemoteHandler = null;
private OneShotRemoteHandler mActiveRemoteHandler = null;
+ private boolean mEnterTransitionMerged;
private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish;
@@ -94,7 +91,8 @@ class SplitScreenTransitions {
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback,
- @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) {
+ @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
+ @NonNull WindowContainerToken topRoot) {
mFinishCallback = finishCallback;
mAnimatingTransition = transition;
if (mPendingRemoteHandler != null) {
@@ -104,12 +102,12 @@ class SplitScreenTransitions {
mPendingRemoteHandler = null;
return;
}
- playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot);
+ playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot, topRoot);
}
private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
- @NonNull WindowContainerToken sideRoot) {
+ @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) {
mFinishTransaction = mTransactionPool.acquire();
// Play some place-holder fade animations
@@ -140,7 +138,10 @@ class SplitScreenTransitions {
endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
startExampleResizeAnimation(leash, startBounds, endBounds);
}
- if (change.getParent() != null) {
+ boolean isRootOrSplitSideRoot = change.getParent() == null
+ || topRoot.equals(change.getParent());
+ // For enter or exit, we only want to animate the side roots but not the top-root.
+ if (!isRootOrSplitSideRoot || topRoot.equals(change.getContainer())) {
continue;
}
@@ -187,27 +188,28 @@ class SplitScreenTransitions {
}
/** Starts a transition to dismiss split. */
- IBinder startDismissTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
+ IBinder startDismissTransition(WindowContainerTransaction wct,
Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
@SplitScreenController.ExitReason int reason) {
final int type = reason == EXIT_REASON_DRAG_DIVIDER
? TRANSIT_SPLIT_DISMISS_SNAP : TRANSIT_SPLIT_DISMISS;
- if (transition == null) {
- transition = mTransitions.startTransition(type, wct, handler);
- }
+ IBinder transition = mTransitions.startTransition(type, wct, handler);
+ setDismissTransition(transition, dismissTop, reason);
+ return transition;
+ }
+
+ /** Sets a transition to dismiss split. */
+ void setDismissTransition(@NonNull IBinder transition, @SplitScreen.StageType int dismissTop,
+ @SplitScreenController.ExitReason int reason) {
mPendingDismiss = new DismissTransition(transition, reason, dismissTop);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ " deduced Dismiss due to %s. toTop=%s",
exitReasonToString(reason), stageTypeToString(dismissTop));
- return transition;
}
- IBinder startRecentTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
- Transitions.TransitionHandler handler, @Nullable RemoteTransition remoteTransition) {
- if (transition == null) {
- transition = mTransitions.startTransition(TRANSIT_OPEN, wct, handler);
- }
+ void setRecentTransition(@NonNull IBinder transition,
+ @Nullable RemoteTransition remoteTransition) {
mPendingRecent = transition;
if (remoteTransition != null) {
@@ -219,13 +221,42 @@ class SplitScreenTransitions {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ " deduced Enter recent panel");
- return transition;
}
void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
- if (mergeTarget == mAnimatingTransition && mActiveRemoteHandler != null) {
+ if (mergeTarget != mAnimatingTransition) return;
+ if (mActiveRemoteHandler != null) {
mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
+ } else {
+ for (int i = mAnimations.size() - 1; i >= 0; --i) {
+ final Animator anim = mAnimations.get(i);
+ mTransitions.getAnimExecutor().execute(anim::end);
+ }
+ }
+ }
+
+ boolean end() {
+ // If its remote, there's nothing we can do right now.
+ if (mActiveRemoteHandler != null) return false;
+ for (int i = mAnimations.size() - 1; i >= 0; --i) {
+ final Animator anim = mAnimations.get(i);
+ mTransitions.getAnimExecutor().execute(anim::end);
+ }
+ return true;
+ }
+
+ void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+ if (aborted) return;
+
+ // Once a pending enter transition got merged, make sure to append the reset of finishing
+ // operations to the finish transition.
+ if (transition == mPendingEnter) {
+ mFinishTransaction = mTransactionPool.acquire();
+ mStageCoordinator.finishEnterSplitScreen(mFinishTransaction);
+ mPendingEnter = null;
+ mPendingRemoteHandler = null;
+ mEnterTransitionMerged = true;
}
}
@@ -238,18 +269,16 @@ class SplitScreenTransitions {
mPendingDismiss = null;
}
if (mAnimatingTransition == mPendingRecent) {
- // If the clean-up wct is null when finishing recent transition, it indicates it's
- // returning to home and thus no need to reorder tasks.
- final boolean returnToHome = wct == null;
- if (returnToHome) {
- wct = new WindowContainerTransaction();
+ if (!mEnterTransitionMerged) {
+ if (wct == null) wct = new WindowContainerTransaction();
+ mStageCoordinator.onRecentTransitionFinished(wct, mFinishTransaction);
}
- mStageCoordinator.onRecentTransitionFinished(returnToHome, wct, mFinishTransaction);
mPendingRecent = null;
}
mPendingRemoteHandler = null;
mActiveRemoteHandler = null;
mAnimatingTransition = null;
+ mEnterTransitionMerged = false;
mOnFinish.run();
if (mFinishTransaction != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 30f316efb2b3..2229e26b9343 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -18,6 +18,8 @@ package com.android.wm.shell.splitscreen;
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED;
+import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -28,9 +30,13 @@ import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
+import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
+import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -47,7 +53,6 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
-import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
@@ -84,6 +89,8 @@ import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowManager;
+import android.widget.Toast;
+import android.window.DisplayAreaInfo;
import android.window.RemoteTransition;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -93,7 +100,9 @@ import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.ArrayUtils;
import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
@@ -110,15 +119,13 @@ import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
import com.android.wm.shell.transition.Transitions;
-import com.android.wm.shell.util.StagedSplitBounds;
+import com.android.wm.shell.util.SplitBounds;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
-import javax.inject.Provider;
-
/**
* Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
* {@link SideStage} stages.
@@ -132,20 +139,21 @@ import javax.inject.Provider;
* This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
* {@link #onStageHasChildrenChanged(StageListenerImpl).}
*/
-class StageCoordinator implements SplitLayout.SplitLayoutHandler,
+public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
DisplayController.OnDisplaysChangedListener, Transitions.TransitionHandler,
- ShellTaskOrganizer.TaskListener {
+ ShellTaskOrganizer.TaskListener, ShellTaskOrganizer.FocusListener {
private static final String TAG = StageCoordinator.class.getSimpleName();
+ /** Flag applied to a transition change to identify it as a divider bar for animation. */
+ public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM;
+
private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final MainStage mMainStage;
private final StageListenerImpl mMainStageListener = new StageListenerImpl();
- private final StageTaskUnfoldController mMainUnfoldController;
private final SideStage mSideStage;
private final StageListenerImpl mSideStageListener = new StageListenerImpl();
- private final StageTaskUnfoldController mSideUnfoldController;
private final DisplayLayout mDisplayLayout;
@SplitPosition
private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -168,6 +176,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private final ShellExecutor mMainExecutor;
private final Optional<RecentTasksController> mRecentTasks;
+ private final Rect mTempRect1 = new Rect();
+ private final Rect mTempRect2 = new Rect();
+
+ private ActivityManager.RunningTaskInfo mFocusingTaskInfo;
+
/**
* A single-top root task which the split divider attached to.
*/
@@ -181,6 +194,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private boolean mShouldUpdateRecents;
private boolean mExitSplitScreenOnHide;
private boolean mIsDividerRemoteAnimating;
+ private boolean mIsExiting;
private boolean mResizingSplits;
/** The target stage to dismiss to when unlock after folded. */
@@ -206,8 +220,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool, SplitscreenEventLogger logger,
IconProvider iconProvider, ShellExecutor mainExecutor,
- Optional<RecentTasksController> recentTasks,
- Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
+ Optional<RecentTasksController> recentTasks) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -215,8 +228,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mLogger = logger;
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
- mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
- mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
+
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
mMainStage = new MainStage(
@@ -226,8 +238,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mMainStageListener,
mSyncQueue,
mSurfaceSession,
- iconProvider,
- mMainUnfoldController);
+ iconProvider);
mSideStage = new SideStage(
mContext,
mTaskOrganizer,
@@ -235,8 +246,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSideStageListener,
mSyncQueue,
mSurfaceSession,
- iconProvider,
- mSideUnfoldController);
+ iconProvider);
mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
@@ -250,6 +260,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mDisplayController.addDisplayWindowListener(this);
mDisplayLayout = new DisplayLayout(displayController.getDisplayLayout(displayId));
transitions.addHandler(this);
+ mTaskOrganizer.addFocusListener(this);
}
@VisibleForTesting
@@ -259,8 +270,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
SplitscreenEventLogger logger, ShellExecutor mainExecutor,
- Optional<RecentTasksController> recentTasks,
- Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
+ Optional<RecentTasksController> recentTasks) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -274,8 +284,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout = splitLayout;
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
this::onTransitionAnimationComplete, this);
- mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
- mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
mLogger = logger;
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
@@ -366,10 +374,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
sideOptions = sideOptions != null ? sideOptions : new Bundle();
setSideStagePosition(sidePosition, wct);
+ if (mMainStage.isActive()) {
+ mMainStage.evictAllChildren(wct);
+ mSideStage.evictAllChildren(wct);
+ } else {
+ // Build a request WCT that will launch both apps such that task 0 is on the main stage
+ // while task 1 is on the side stage.
+ mMainStage.activate(wct, false /* reparent */);
+ }
mSplitLayout.setDivideRatio(splitRatio);
- // Build a request WCT that will launch both apps such that task 0 is on the main stage
- // while task 1 is on the side stage.
- mMainStage.activate(wct, false /* reparent */);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
@@ -436,7 +449,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
new IRemoteAnimationFinishedCallback.Stub() {
@Override
public void onAnimationFinished() throws RemoteException {
- onRemoteAnimationFinishedOrCancelled(evictWct);
+ onRemoteAnimationFinishedOrCancelled(false /* cancel */, evictWct);
finishedCallback.onAnimationFinished();
}
};
@@ -457,7 +470,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onAnimationCancelled(boolean isKeyguardOccluded) {
- onRemoteAnimationFinishedOrCancelled(evictWct);
+ onRemoteAnimationFinishedOrCancelled(true /* cancel */, evictWct);
try {
adapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
} catch (RemoteException e) {
@@ -507,13 +520,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
});
}
- private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) {
+ private void onRemoteAnimationFinishedOrCancelled(boolean cancel,
+ WindowContainerTransaction evictWct) {
mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
// If any stage has no child after animation finished, it means that split will display
// nothing, such status will happen if task and intent is same app but not support
// multi-instagce, we should exit split and expand that app as full screen.
- if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
+ if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
mMainExecutor.execute(() ->
exitSplitScreen(mMainStage.getChildCount() == 0
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
@@ -610,6 +624,21 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
+ void setSideStagePositionAnimated(@SplitPosition int sideStagePosition) {
+ if (mSideStagePosition == sideStagePosition) return;
+ SurfaceControl.Transaction t = mTransactionPool.acquire();
+ final StageTaskListener topLeftStage =
+ mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
+ final StageTaskListener bottomRightStage =
+ mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
+ mSplitLayout.splitSwitching(t, topLeftStage.mRootLeash, bottomRightStage.mRootLeash,
+ () -> {
+ setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition),
+ null /* wct */);
+ mTransactionPool.release(t);
+ });
+ }
+
void setSideStagePosition(@SplitPosition int sideStagePosition,
@Nullable WindowContainerTransaction wct) {
setSideStagePosition(sideStagePosition, true /* updateBounds */, wct);
@@ -627,7 +656,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
onLayoutSizeChanged(mSplitLayout);
} else {
updateWindowBounds(mSplitLayout, wct);
- updateUnfoldBounds();
+ sendOnBoundsChanged();
}
}
}
@@ -642,7 +671,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (ENABLE_SHELL_TRANSITIONS) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct);
- mSplitTransitions.startDismissTransition(null /* transition */, wct, this,
+ mSplitTransitions.startDismissTransition(wct, this,
mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED);
} else {
exitSplitScreen(
@@ -675,8 +704,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final int dismissTop = mainStageVisible ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareExitSplitScreen(dismissTop, wct);
- mSplitTransitions.startDismissTransition(null /* transition */, wct, this,
- dismissTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
+ mSplitTransitions.startDismissTransition(wct, this, dismissTop,
+ EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
}
}
}
@@ -712,7 +741,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void applyExitSplitScreen(@Nullable StageTaskListener childrenToTop,
WindowContainerTransaction wct, @ExitReason int exitReason) {
- if (!mMainStage.isActive()) return;
+ if (!mMainStage.isActive() || mIsExiting) return;
mRecentTasks.ifPresent(recentTasks -> {
// Notify recents if we are exiting in a way that breaks the pair, and disable further
@@ -724,25 +753,45 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
});
mShouldUpdateRecents = false;
- // When the exit split-screen is caused by one of the task enters auto pip,
- // we want the tasks to be put to bottom instead of top, otherwise it will end up
- // a fullscreen plus a pinned task instead of pinned only at the end of the transition.
- final boolean fromEnteringPip = exitReason == EXIT_REASON_CHILD_TASK_ENTER_PIP;
- mSideStage.removeAllTasks(wct, !fromEnteringPip && mSideStage == childrenToTop);
- mMainStage.deactivate(wct, !fromEnteringPip && mMainStage == childrenToTop);
- wct.reorder(mRootTaskInfo.token, false /* onTop */);
- mTaskOrganizer.applyTransaction(wct);
+ if (childrenToTop == null) {
+ mSideStage.removeAllTasks(wct, false /* toTop */);
+ mMainStage.deactivate(wct, false /* toTop */);
+ wct.reorder(mRootTaskInfo.token, false /* onTop */);
+ onTransitionAnimationComplete();
+ } else {
+ // Expand to top side split as full screen for fading out decor animation and dismiss
+ // another side split(Moving its children to bottom).
+ mIsExiting = true;
+ final StageTaskListener tempFullStage = childrenToTop;
+ final StageTaskListener dismissStage = mMainStage == childrenToTop
+ ? mSideStage : mMainStage;
+ tempFullStage.resetBounds(wct);
+ wct.setSmallestScreenWidthDp(tempFullStage.mRootTaskInfo.token,
+ mRootTaskInfo.configuration.smallestScreenWidthDp);
+ dismissStage.dismiss(wct, false /* toTop */);
+ }
+ mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
- setResizingSplits(false /* resizing */);
t.setWindowCrop(mMainStage.mRootLeash, null)
.setWindowCrop(mSideStage.mRootLeash, null);
+ t.setPosition(mMainStage.mRootLeash, 0, 0)
+ .setPosition(mSideStage.mRootLeash, 0, 0);
setDividerVisibility(false, t);
+
+ // In this case, exit still under progress, fade out the split decor after first WCT
+ // done and do remaining WCT after animation finished.
+ if (childrenToTop != null) {
+ childrenToTop.fadeOutDecor(() -> {
+ WindowContainerTransaction finishedWCT = new WindowContainerTransaction();
+ mIsExiting = false;
+ childrenToTop.dismiss(finishedWCT, true /* toTop */);
+ wct.reorder(mRootTaskInfo.token, false /* toTop */);
+ mTaskOrganizer.applyTransaction(finishedWCT);
+ onTransitionAnimationComplete();
+ });
+ }
});
- // Hide divider and reset its position.
- mSplitLayout.resetDividerPosition();
- mSplitLayout.release();
- mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason));
// Log the exit
if (childrenToTop != null) {
@@ -840,6 +889,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void addActivityOptions(Bundle opts, StageTaskListener stage) {
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
+ // Put BAL flags to avoid activity start aborted.
+ opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
+ opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
}
void updateActivityOptions(Bundle opts, @SplitPosition int position) {
@@ -860,6 +912,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
listener.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition());
listener.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition());
listener.onSplitVisibilityChanged(isSplitScreenVisible());
+ if (mSplitLayout != null) {
+ listener.onSplitBoundsChanged(mSplitLayout.getRootBounds(), getMainStageBounds(),
+ getSideStageBounds());
+ }
mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE);
mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN);
}
@@ -872,6 +928,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
+ private void sendOnBoundsChanged() {
+ if (mSplitLayout == null) return;
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ mListeners.get(i).onSplitBoundsChanged(mSplitLayout.getRootBounds(),
+ getMainStageBounds(), getSideStageBounds());
+ }
+ }
+
private void onStageChildTaskStatusChanged(StageListenerImpl stageListener, int taskId,
boolean present, boolean visible) {
int stage;
@@ -897,9 +961,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- private void onStageChildTaskEnterPip(StageListenerImpl stageListener, int taskId) {
- exitSplitScreen(stageListener == mMainStageListener ? mMainStage : mSideStage,
- EXIT_REASON_CHILD_TASK_ENTER_PIP);
+ private void onStageChildTaskEnterPip() {
+ // When the exit split-screen is caused by one of the task enters auto pip,
+ // we want both tasks to be put to bottom instead of top, otherwise it will end up
+ // a fullscreen plus a pinned task instead of pinned only at the end of the transition.
+ exitSplitScreen(null, EXIT_REASON_CHILD_TASK_ENTER_PIP);
}
private void updateRecentTasksSplitPair() {
@@ -921,7 +987,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
leftTopTaskId = mainStageTopTaskId;
rightBottomTaskId = sideStageTopTaskId;
}
- StagedSplitBounds splitBounds = new StagedSplitBounds(topLeftBounds, bottomRightBounds,
+ SplitBounds splitBounds = new SplitBounds(topLeftBounds, bottomRightBounds,
leftTopTaskId, rightBottomTaskId);
if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
// Update the pair for the top tasks
@@ -935,12 +1001,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final SplitScreen.SplitScreenListener l = mListeners.get(i);
l.onSplitVisibilityChanged(mDividerVisible);
}
-
- if (mMainUnfoldController != null && mSideUnfoldController != null) {
- mMainUnfoldController.onSplitVisibilityChanged(mDividerVisible);
- mSideUnfoldController.onSplitVisibilityChanged(mDividerVisible);
- updateUnfoldBounds();
- }
+ sendOnBoundsChanged();
}
@Override
@@ -961,11 +1022,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
}
- if (mMainUnfoldController != null && mSideUnfoldController != null) {
- mMainUnfoldController.init();
- mSideUnfoldController.init();
- }
-
onRootTaskAppeared();
}
@@ -979,13 +1035,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mRootTaskInfo = taskInfo;
if (mSplitLayout != null
&& mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)
- && mMainStage.isActive()) {
- // TODO(b/204925795): With Shell transition, We are handling split bounds rotation at
- // onRotateDisplay. But still need to handle unfold case.
- if (ENABLE_SHELL_TRANSITIONS) {
- updateUnfoldBounds();
- return;
- }
+ && mMainStage.isActive()
+ && !ENABLE_SHELL_TRANSITIONS) {
// Clear the divider remote animating flag as the divider will be re-rendered to apply
// the new rotation config.
mIsDividerRemoteAnimating = false;
@@ -1009,6 +1060,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
mRootTaskInfo = null;
+ mRootTaskLeash = null;
}
@@ -1025,8 +1077,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
wct.reparent(mMainStage.mRootTaskInfo.token, mRootTaskInfo.token, true);
wct.reparent(mSideStage.mRootTaskInfo.token, mRootTaskInfo.token, true);
// Make the stages adjacent to each other so they occlude what's behind them.
- wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token,
- true /* moveTogether */);
+ wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
mTaskOrganizer.applyTransaction(wct);
}
@@ -1136,12 +1187,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mDividerFadeInAnimator.cancel();
return;
}
+ mSplitLayout.getRefDividerBounds(mTempRect1);
transaction.show(dividerLeash);
transaction.setAlpha(dividerLeash, 0);
transaction.setLayer(dividerLeash, Integer.MAX_VALUE);
- transaction.setPosition(dividerLeash,
- mSplitLayout.getRefDividerBounds().left,
- mSplitLayout.getRefDividerBounds().top);
+ transaction.setPosition(dividerLeash, mTempRect1.left, mTempRect1.top);
transaction.apply();
}
@@ -1161,21 +1211,42 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
final boolean hasChildren = stageListener.mHasChildren;
final boolean isSideStage = stageListener == mSideStageListener;
- if (!hasChildren) {
+ if (!hasChildren && !mIsExiting) {
if (isSideStage && mMainStageListener.mVisible) {
// Exit to main stage if side stage no longer has children.
- exitSplitScreen(mMainStage, EXIT_REASON_APP_FINISHED);
+ if (ENABLE_SHELL_TRANSITIONS) {
+ exitSplitScreen(mMainStage, EXIT_REASON_APP_FINISHED);
+ } else {
+ mSplitLayout.flingDividerToDismiss(
+ mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ EXIT_REASON_APP_FINISHED);
+ }
} else if (!isSideStage && mSideStageListener.mVisible) {
// Exit to side stage if main stage no longer has children.
- exitSplitScreen(mSideStage, EXIT_REASON_APP_FINISHED);
+ if (ENABLE_SHELL_TRANSITIONS) {
+ exitSplitScreen(mSideStage, EXIT_REASON_APP_FINISHED);
+ } else {
+ mSplitLayout.flingDividerToDismiss(
+ mSideStagePosition != SPLIT_POSITION_BOTTOM_OR_RIGHT,
+ EXIT_REASON_APP_FINISHED);
+ }
}
} else if (isSideStage && !mMainStage.isActive()) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- mSplitLayout.init();
- prepareEnterSplitScreen(wct);
- mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t ->
- updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */));
+ if (mFocusingTaskInfo != null && !isValidToEnterSplitScreen(mFocusingTaskInfo)) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ mSideStage.removeAllTasks(wct, true);
+ wct.reorder(mRootTaskInfo.token, false /* onTop */);
+ mTaskOrganizer.applyTransaction(wct);
+ Slog.i(TAG, "cancel entering split screen, reason = "
+ + exitReasonToString(EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW));
+ } else {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ mSplitLayout.init();
+ prepareEnterSplitScreen(wct);
+ mSyncQueue.queue(wct);
+ mSyncQueue.runInSync(t ->
+ updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */));
+ }
}
if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
mShouldUpdateRecents = true;
@@ -1190,27 +1261,43 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
+ boolean isValidToEnterSplitScreen(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
+ return taskInfo.supportsMultiWindow
+ && ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
+ && ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode());
+ }
+
+ ActivityManager.RunningTaskInfo getFocusingTaskInfo() {
+ return mFocusingTaskInfo;
+ }
+
+ @Override
+ public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ mFocusingTaskInfo = taskInfo;
+ }
+
@Override
- public void onSnappedToDismiss(boolean bottomOrRight) {
+ public void onSnappedToDismiss(boolean bottomOrRight, int reason) {
final boolean mainStageToTop =
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
if (!ENABLE_SHELL_TRANSITIONS) {
- exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER);
+ exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, reason);
return;
}
- setResizingSplits(false /* resizing */);
final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareExitSplitScreen(dismissTop, wct);
- mSplitTransitions.startDismissTransition(
- null /* transition */, wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER);
+ if (mRootTaskInfo != null) {
+ wct.setDoNotPip(mRootTaskInfo.token);
+ }
+ mSplitTransitions.startDismissTransition(wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER);
}
@Override
public void onDoubleTappedDivider() {
- setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition), null /* wct */);
+ setSideStagePositionAnimated(SplitLayout.reversePosition(mSideStagePosition));
mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLandscape());
@@ -1229,10 +1316,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
public void onLayoutSizeChanging(SplitLayout layout) {
final SurfaceControl.Transaction t = mTransactionPool.acquire();
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
- setResizingSplits(true /* resizing */);
updateSurfaceBounds(layout, t, true /* applyResizingOffset */);
- mMainStage.onResizing(getMainStageBounds(), t);
- mSideStage.onResizing(getSideStageBounds(), t);
+ getMainStageBounds(mTempRect1);
+ getSideStageBounds(mTempRect2);
+ mMainStage.onResizing(mTempRect1, mTempRect2, t);
+ mSideStage.onResizing(mTempRect2, mTempRect1, t);
t.apply();
mTransactionPool.release(t);
}
@@ -1241,10 +1329,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
public void onLayoutSizeChanged(SplitLayout layout) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
updateWindowBounds(layout, wct);
- updateUnfoldBounds();
+ sendOnBoundsChanged();
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
- setResizingSplits(false /* resizing */);
updateSurfaceBounds(layout, t, false /* applyResizingOffset */);
mMainStage.onResized(t);
mSideStage.onResized(t);
@@ -1252,15 +1339,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
}
- private void updateUnfoldBounds() {
- if (mMainUnfoldController != null && mSideUnfoldController != null) {
- mMainUnfoldController.onLayoutChanged(getMainStageBounds(), getMainStagePosition(),
- isLandscape());
- mSideUnfoldController.onLayoutChanged(getSideStageBounds(), getSideStagePosition(),
- isLandscape());
- }
- }
-
private boolean isLandscape() {
return mSplitLayout.isLandscape();
}
@@ -1288,16 +1366,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
applyResizingOffset);
}
- void setResizingSplits(boolean resizing) {
- if (resizing == mResizingSplits) return;
- try {
- ActivityTaskManager.getService().setSplitScreenResizing(resizing);
- mResizingSplits = resizing;
- } catch (RemoteException e) {
- Slog.w(TAG, "Error calling setSplitScreenResizing", e);
- }
- }
-
@Override
public int getSplitItemPosition(WindowContainerToken token) {
if (token == null) {
@@ -1329,7 +1397,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (displayId != DEFAULT_DISPLAY) {
return;
}
- mDisplayController.addDisplayChangingController(this::onRotateDisplay);
+ mDisplayController.addDisplayChangingController(this::onDisplayChange);
}
@Override
@@ -1340,16 +1408,22 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mDisplayLayout.set(mDisplayController.getDisplayLayout(displayId));
}
- private void onRotateDisplay(int displayId, int fromRotation, int toRotation,
- WindowContainerTransaction wct) {
+ void updateSurfaces(SurfaceControl.Transaction transaction) {
+ updateSurfaceBounds(mSplitLayout, transaction, /* applyResizingOffset */ false);
+ mSplitLayout.update(transaction);
+ }
+
+ private void onDisplayChange(int displayId, int fromRotation, int toRotation,
+ @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
if (!mMainStage.isActive()) return;
- // Only do this when shell transition
- if (!ENABLE_SHELL_TRANSITIONS) return;
mDisplayLayout.rotateTo(mContext.getResources(), toRotation);
mSplitLayout.rotateTo(toRotation, mDisplayLayout.stableInsets());
+ if (newDisplayAreaInfo != null) {
+ mSplitLayout.updateConfiguration(newDisplayAreaInfo.configuration);
+ }
updateWindowBounds(mSplitLayout, wct);
- updateUnfoldBounds();
+ sendOnBoundsChanged();
}
private void onFoldedStateChanged(boolean folded) {
@@ -1373,6 +1447,22 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
? mSplitLayout.getBounds2() : mSplitLayout.getBounds1();
}
+ private void getSideStageBounds(Rect rect) {
+ if (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT) {
+ mSplitLayout.getBounds1(rect);
+ } else {
+ mSplitLayout.getBounds2(rect);
+ }
+ }
+
+ private void getMainStageBounds(Rect rect) {
+ if (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT) {
+ mSplitLayout.getBounds2(rect);
+ } else {
+ mSplitLayout.getBounds1(rect);
+ }
+ }
+
/**
* Get the stage that should contain this `taskInfo`. The stage doesn't necessarily contain
* this task (yet) so this can also be used to identify which stage to put a task into.
@@ -1400,7 +1490,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Nullable TransitionRequestInfo request) {
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask == null) {
- if (mMainStage.isActive()) {
+ if (isSplitActive()) {
+ // Check if the display is rotating.
final TransitionRequestInfo.DisplayChange displayChange =
request.getDisplayChange();
if (request.getType() == TRANSIT_CHANGE && displayChange != null
@@ -1427,7 +1518,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
}
- if (mMainStage.isActive()) {
+ if (isSplitActive()) {
// Try to handle everything while in split-screen, so return a WCT even if it's empty.
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
+ "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
@@ -1442,7 +1533,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_SIDE
: STAGE_TYPE_MAIN;
prepareExitSplitScreen(dismissTop, out);
- mSplitTransitions.startDismissTransition(transition, out, this, dismissTop,
+ mSplitTransitions.setDismissTransition(transition, dismissTop,
EXIT_REASON_APP_FINISHED);
}
} else if (isOpening && inFullscreen) {
@@ -1452,12 +1543,13 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
} else if (activityType == ACTIVITY_TYPE_HOME
|| activityType == ACTIVITY_TYPE_RECENTS) {
// Enter overview panel, so start recent transition.
- mSplitTransitions.startRecentTransition(transition, out, this,
+ mSplitTransitions.setRecentTransition(transition,
request.getRemoteTransition());
- } else {
- // Occluded by the other fullscreen task, so dismiss both.
+ } else if (mSplitTransitions.mPendingRecent == null) {
+ // If split-task is not controlled by recents animation
+ // and occluded by the other fullscreen task, dismiss both.
prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
- mSplitTransitions.startDismissTransition(transition, out, this,
+ mSplitTransitions.setDismissTransition(transition,
STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN);
}
}
@@ -1472,6 +1564,33 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return out;
}
+ /**
+ * This is used for mixed scenarios. For such scenarios, just make sure to include exiting
+ * split or entering split when appropriate.
+ */
+ public void addEnterOrExitIfNeeded(@Nullable TransitionRequestInfo request,
+ @NonNull WindowContainerTransaction outWCT) {
+ final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
+ if (triggerTask != null && triggerTask.displayId != mDisplayId) {
+ // Skip handling task on the other display.
+ return;
+ }
+ final @WindowManager.TransitionType int type = request.getType();
+ if (isSplitActive() && !isOpeningType(type)
+ && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " One of the splits became "
+ + "empty during a mixed transition (one not handled by split),"
+ + " so make sure split-screen state is cleaned-up. "
+ + "mainStageCount=%d sideStageCount=%d", mMainStage.getChildCount(),
+ mSideStage.getChildCount());
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, outWCT);
+ }
+ }
+
+ public boolean isSplitActive() {
+ return mMainStage.isActive();
+ }
+
@Override
public void mergeAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t, IBinder mergeTarget,
@@ -1479,17 +1598,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitTransitions.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
}
+ /** Jump the current transition animation to the end. */
+ public boolean end() {
+ return mSplitTransitions.end();
+ }
+
@Override
- public void onTransitionMerged(@NonNull IBinder transition) {
- // Once the pending enter transition got merged, make sure to bring divider bar visible and
- // clear the pending transition from cache to prevent mess-up the following state.
- if (transition == mSplitTransitions.mPendingEnter) {
- final SurfaceControl.Transaction t = mTransactionPool.acquire();
- finishEnterSplitScreen(t);
- mSplitTransitions.mPendingEnter = null;
- t.apply();
- mTransactionPool.release(t);
- }
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+ mSplitTransitions.onTransitionConsumed(transition, aborted);
}
@Override
@@ -1560,13 +1676,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (!shouldAnimate) return false;
mSplitTransitions.playAnimation(transition, info, startTransaction, finishTransaction,
- finishCallback, mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
+ finishCallback, mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token,
+ mRootTaskInfo.token);
return true;
}
- void onTransitionAnimationComplete() {
+ /** Called to clean-up state and do house-keeping after the animation is done. */
+ public void onTransitionAnimationComplete() {
// If still playing, let it finish.
- if (!mMainStage.isActive()) {
+ if (!mMainStage.isActive() && !mIsExiting) {
// Update divider state after animation so that it is still around and positioned
// properly for the animation itself.
mSplitLayout.release();
@@ -1628,8 +1746,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return true;
}
- private boolean startPendingDismissAnimation(
- @NonNull SplitScreenTransitions.DismissTransition dismissTransition,
+ /** Synchronize split-screen state with transition and make appropriate preparations. */
+ public void prepareDismissAnimation(@StageType int toStage, @ExitReason int dismissReason,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
@NonNull SurfaceControl.Transaction finishT) {
// Make some noise if things aren't totally expected. These states shouldn't effect
@@ -1662,7 +1780,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mRecentTasks.ifPresent(recentTasks -> {
// Notify recents if we are exiting in a way that breaks the pair, and disable further
// updates to splits in the recents until we enter split again
- if (shouldBreakPairedTaskInRecents(dismissTransition.mReason) && mShouldUpdateRecents) {
+ if (shouldBreakPairedTaskInRecents(dismissReason) && mShouldUpdateRecents) {
for (TransitionInfo.Change change : info.getChanges()) {
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (taskInfo != null
@@ -1679,30 +1797,37 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Wait until after animation to update divider
// Reset crops so they don't interfere with subsequent launches
- t.setWindowCrop(mMainStage.mRootLeash, null);
- t.setWindowCrop(mSideStage.mRootLeash, null);
+ t.setCrop(mMainStage.mRootLeash, null);
+ t.setCrop(mSideStage.mRootLeash, null);
+
+ if (toStage == STAGE_TYPE_UNDEFINED) {
+ logExit(dismissReason);
+ } else {
+ logExitToStage(dismissReason, toStage == STAGE_TYPE_MAIN);
+ }
+ // Hide divider and dim layer on transition finished.
+ setDividerVisibility(false, finishT);
+ finishT.hide(mMainStage.mDimLayer);
+ finishT.hide(mSideStage.mDimLayer);
+ }
+
+ private boolean startPendingDismissAnimation(
+ @NonNull SplitScreenTransitions.DismissTransition dismissTransition,
+ @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction finishT) {
+ prepareDismissAnimation(dismissTransition.mDismissTop, dismissTransition.mReason, info,
+ t, finishT);
if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
- logExit(dismissTransition.mReason);
// TODO: Have a proper remote for this. Until then, though, reset state and use the
// normal animation stuff (which falls back to the normal launcher remote).
+ t.hide(mSplitLayout.getDividerLeash());
mSplitLayout.release(t);
mSplitTransitions.mPendingDismiss = null;
return false;
- } else {
- logExitToStage(dismissTransition.mReason,
- dismissTransition.mDismissTop == STAGE_TYPE_MAIN);
}
addDividerBarToTransition(info, t, false /* show */);
- // We're dismissing split by moving the other one to fullscreen.
- // Since we don't have any animations for this yet, just use the internal example
- // animations.
-
- // Hide divider and dim layer on transition finished.
- setDividerVisibility(false, finishT);
- finishT.hide(mMainStage.mDimLayer);
- finishT.hide(mSideStage.mDimLayer);
return true;
}
@@ -1712,26 +1837,26 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return true;
}
- void onRecentTransitionFinished(boolean returnToHome, WindowContainerTransaction wct,
+ void onRecentTransitionFinished(WindowContainerTransaction wct,
SurfaceControl.Transaction finishT) {
- // Exclude the case that the split screen has been dismissed already.
- if (!mMainStage.isActive()) {
- // The latest split dismissing transition might be a no-op transition and thus won't
- // callback startAnimation, update split visibility here to cover this kind of no-op
- // transition case.
- setSplitsVisible(false);
- return;
+ // Check if the recent transition is finished by returning to the current split so we can
+ // restore the divider bar.
+ for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
+ final WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
+ final IBinder container = op.getContainer();
+ if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop()
+ && (mMainStage.containsContainer(container)
+ || mSideStage.containsContainer(container))) {
+ setDividerVisibility(true, finishT);
+ return;
+ }
}
- if (returnToHome) {
- // When returning to home from recent apps, the splitting tasks are already hidden, so
- // append the reset of dismissing operations into the clean-up wct.
- prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
- setSplitsVisible(false);
- logExit(EXIT_REASON_RETURN_HOME);
- } else {
- setDividerVisibility(true, finishT);
- }
+ // Dismiss the split screen is it's not returning to split.
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+ setSplitsVisible(false);
+ setDividerVisibility(false, finishT);
+ logExit(EXIT_REASON_UNKNOWN);
}
private void addDividerBarToTransition(@NonNull TransitionInfo info,
@@ -1855,8 +1980,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
@Override
- public void onChildTaskEnterPip(int taskId) {
- StageCoordinator.this.onStageChildTaskEnterPip(this, taskId);
+ public void onChildTaskEnterPip() {
+ StageCoordinator.this.onStageChildTaskEnterPip();
}
@Override
@@ -1868,19 +1993,22 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onNoLongerSupportMultiWindow() {
if (mMainStage.isActive()) {
+ final Toast splitUnsupportedToast = Toast.makeText(mContext,
+ R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT);
final boolean isMainStage = mMainStageListener == this;
if (!ENABLE_SHELL_TRANSITIONS) {
StageCoordinator.this.exitSplitScreen(isMainStage ? mMainStage : mSideStage,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
+ splitUnsupportedToast.show();
return;
}
final int stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
final WindowContainerTransaction wct = new WindowContainerTransaction();
prepareExitSplitScreen(stageType, wct);
- mSplitTransitions.startDismissTransition(null /* transition */, wct,
- StageCoordinator.this, stageType,
+ mSplitTransitions.startDismissTransition(wct,StageCoordinator.this, stageType,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
+ splitUnsupportedToast.show();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 949bf5f55808..f6dc68be946a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -17,12 +17,12 @@
package com.android.wm.shell.splitscreen;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
+import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import android.annotation.CallSuper;
@@ -31,6 +31,7 @@ import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.IBinder;
import android.util.SparseArray;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -39,6 +40,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
+import com.android.internal.util.ArrayUtils;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SurfaceUtils;
@@ -47,6 +49,7 @@ import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import java.io.PrintWriter;
+import java.util.function.Predicate;
/**
* Base class that handle common task org. related for split-screen stages.
@@ -60,12 +63,6 @@ import java.io.PrintWriter;
class StageTaskListener implements ShellTaskOrganizer.TaskListener {
private static final String TAG = StageTaskListener.class.getSimpleName();
- protected static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD};
- protected static final int[] CONTROLLED_WINDOWING_MODES =
- {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
- protected static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE =
- {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW};
-
/** Callback interface for listening to changes in a split-screen stage. */
public interface StageListenerCallbacks {
void onRootTaskAppeared();
@@ -74,7 +71,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
- void onChildTaskEnterPip(int taskId);
+ void onChildTaskEnterPip();
void onRootTaskVanished();
@@ -95,21 +92,22 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
// TODO(b/204308910): Extracts SplitDecorManager related code to common package.
private SplitDecorManager mSplitDecorManager;
- private final StageTaskUnfoldController mStageTaskUnfoldController;
-
StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider,
- @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
+ SurfaceSession surfaceSession, IconProvider iconProvider) {
mContext = context;
mCallbacks = callbacks;
mSyncQueue = syncQueue;
mSurfaceSession = surfaceSession;
mIconProvider = iconProvider;
- mStageTaskUnfoldController = stageTaskUnfoldController;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
}
+ /**
+ * General function for dismiss this stage.
+ */
+ void dismiss(WindowContainerTransaction wct, boolean toTop) {}
+
int getChildCount() {
return mChildrenTaskInfo.size();
}
@@ -119,63 +117,53 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
boolean containsToken(WindowContainerToken token) {
- if (token.equals(mRootTaskInfo.token)) {
- return true;
- }
-
- for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
- if (token.equals(mChildrenTaskInfo.valueAt(i).token)) {
- return true;
- }
- }
+ return contains(t -> t.token.equals(token));
+ }
- return false;
+ boolean containsContainer(IBinder binder) {
+ return contains(t -> t.token.asBinder() == binder);
}
/**
* Returns the top visible child task's id.
*/
int getTopVisibleChildTaskId() {
- for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
- final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i);
- if (info.isVisible) {
- return info.taskId;
- }
- }
- return INVALID_TASK_ID;
+ final ActivityManager.RunningTaskInfo taskInfo = getChildTaskInfo(t -> t.isVisible);
+ return taskInfo != null ? taskInfo.taskId : INVALID_TASK_ID;
}
/**
* Returns the top activity uid for the top child task.
*/
int getTopChildTaskUid() {
- for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
- final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i);
- if (info.topActivityInfo == null) {
- continue;
- }
- return info.topActivityInfo.applicationInfo.uid;
- }
- return 0;
+ final ActivityManager.RunningTaskInfo taskInfo =
+ getChildTaskInfo(t -> t.topActivityInfo != null);
+ return taskInfo != null ? taskInfo.topActivityInfo.applicationInfo.uid : 0;
}
/** @return {@code true} if this listener contains the currently focused task. */
boolean isFocused() {
- if (mRootTaskInfo == null) {
- return false;
- }
+ return contains(t -> t.isFocused);
+ }
- if (mRootTaskInfo.isFocused) {
+ private boolean contains(Predicate<ActivityManager.RunningTaskInfo> predicate) {
+ if (mRootTaskInfo != null && predicate.test(mRootTaskInfo)) {
return true;
}
+ return getChildTaskInfo(predicate) != null;
+ }
+
+ @Nullable
+ private ActivityManager.RunningTaskInfo getChildTaskInfo(
+ Predicate<ActivityManager.RunningTaskInfo> predicate) {
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
- if (mChildrenTaskInfo.valueAt(i).isFocused) {
- return true;
+ final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
+ if (predicate.test(taskInfo)) {
+ return taskInfo;
}
}
-
- return false;
+ return null;
}
@Override
@@ -207,20 +195,11 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
}
-
- if (mStageTaskUnfoldController != null) {
- mStageTaskUnfoldController.onTaskAppeared(taskInfo, leash);
- }
}
@Override
@CallSuper
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- if (!taskInfo.supportsMultiWindow) {
- // Leave split screen if the task no longer supports multi window.
- mCallbacks.onNoLongerSupportMultiWindow();
- return;
- }
if (mRootTaskInfo.taskId == taskInfo.taskId) {
// Inflates split decor view only when the root task is visible.
if (mRootTaskInfo.isVisible != taskInfo.isVisible) {
@@ -233,6 +212,15 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
mRootTaskInfo = taskInfo;
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
+ if (!taskInfo.supportsMultiWindow
+ || !ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
+ || !ArrayUtils.contains(CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
+ taskInfo.getWindowingMode())) {
+ // Leave split screen if the task no longer supports multi window or have
+ // uncontrolled task.
+ mCallbacks.onNoLongerSupportMultiWindow();
+ return;
+ }
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */,
taskInfo.isVisible);
@@ -258,6 +246,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
if (mRootTaskInfo.taskId == taskId) {
mCallbacks.onRootTaskVanished();
mRootTaskInfo = null;
+ mRootLeash = null;
mSyncQueue.runInSync(t -> {
t.remove(mDimLayer);
mSplitDecorManager.release(t);
@@ -266,22 +255,18 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
mChildrenTaskInfo.remove(taskId);
mChildrenLeashes.remove(taskId);
mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
- if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
- mCallbacks.onChildTaskEnterPip(taskId);
- }
if (ENABLE_SHELL_TRANSITIONS) {
// Status is managed/synchronized by the transition lifecycle.
return;
}
+ if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
+ mCallbacks.onChildTaskEnterPip();
+ }
sendStatusChanged();
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
}
-
- if (mStageTaskUnfoldController != null) {
- mStageTaskUnfoldController.onTaskVanished(taskInfo);
- }
}
@Override
@@ -305,9 +290,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
}
- void onResizing(Rect newBounds, SurfaceControl.Transaction t) {
+ void onResizing(Rect newBounds, Rect sideBounds, SurfaceControl.Transaction t) {
if (mSplitDecorManager != null && mRootTaskInfo != null) {
- mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, t);
+ mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, sideBounds, t);
}
}
@@ -317,6 +302,14 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
}
+ void fadeOutDecor(Runnable finishedCallback) {
+ if (mSplitDecorManager != null) {
+ mSplitDecorManager.fadeOutDecor(finishedCallback);
+ } else {
+ finishedCallback.run();
+ }
+ }
+
void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
// Clear overridden bounds and windowing mode to make sure the child task can inherit
// windowing mode and bounds from split root.
@@ -350,6 +343,11 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
}
+ void resetBounds(WindowContainerTransaction wct) {
+ wct.setBounds(mRootTaskInfo.token, null);
+ wct.setAppBounds(mRootTaskInfo.token, null);
+ }
+
void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener,
@StageType int stage) {
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreen.aidl
deleted file mode 100644
index 45f6d3c8b154..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreen.aidl
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.stagesplit;
-
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationTarget;
-import android.window.RemoteTransition;
-
-import com.android.wm.shell.stagesplit.ISplitScreenListener;
-
-/**
- * Interface that is exposed to remote callers to manipulate the splitscreen feature.
- */
-interface ISplitScreen {
-
- /**
- * Registers a split screen listener.
- */
- oneway void registerSplitScreenListener(in ISplitScreenListener listener) = 1;
-
- /**
- * Unregisters a split screen listener.
- */
- oneway void unregisterSplitScreenListener(in ISplitScreenListener listener) = 2;
-
- /**
- * Hides the side-stage if it is currently visible.
- */
- oneway void setSideStageVisibility(boolean visible) = 3;
-
- /**
- * Removes a task from the side stage.
- */
- oneway void removeFromSideStage(int taskId) = 4;
-
- /**
- * Removes the split-screen stages and leaving indicated task to top. Passing INVALID_TASK_ID
- * to indicate leaving no top task after leaving split-screen.
- */
- oneway void exitSplitScreen(int toTopTaskId) = 5;
-
- /**
- * @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible.
- */
- oneway void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 6;
-
- /**
- * Starts a task in a stage.
- */
- oneway void startTask(int taskId, int stage, int position, in Bundle options) = 7;
-
- /**
- * Starts a shortcut in a stage.
- */
- oneway void startShortcut(String packageName, String shortcutId, int stage, int position,
- in Bundle options, in UserHandle user) = 8;
-
- /**
- * Starts an activity in a stage.
- */
- oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int stage,
- int position, in Bundle options) = 9;
-
- /**
- * Starts tasks simultaneously in one transition.
- */
- oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
- in Bundle sideOptions, int sidePosition, in RemoteTransition remoteTransition) = 10;
-
- /**
- * Version of startTasks using legacy transition system.
- */
- oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
- int sideTaskId, in Bundle sideOptions, int sidePosition,
- in RemoteAnimationAdapter adapter) = 11;
-
- /**
- * Blocking call that notifies and gets additional split-screen targets when entering
- * recents (for example: the dividerBar).
- * @param cancel is true if leaving recents back to split (eg. the gesture was cancelled).
- * @param appTargets apps that will be re-parented to display area
- */
- RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel,
- in RemoteAnimationTarget[] appTargets) = 12;
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/MainStage.java
deleted file mode 100644
index 83855be91e04..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/MainStage.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.stagesplit;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-
-import android.annotation.Nullable;
-import android.graphics.Rect;
-import android.view.SurfaceSession;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-/**
- * Main stage for split-screen mode. When split-screen is active all standard activity types launch
- * on the main stage, except for task that are explicitly pinned to the {@link SideStage}.
- * @see StageCoordinator
- */
-class MainStage extends StageTaskListener {
- private static final String TAG = MainStage.class.getSimpleName();
-
- private boolean mIsActive = false;
-
- MainStage(ShellTaskOrganizer taskOrganizer, int displayId,
- StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession,
- @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
- super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
- stageTaskUnfoldController);
- }
-
- boolean isActive() {
- return mIsActive;
- }
-
- void activate(Rect rootBounds, WindowContainerTransaction wct) {
- if (mIsActive) return;
-
- final WindowContainerToken rootToken = mRootTaskInfo.token;
- wct.setBounds(rootToken, rootBounds)
- .setWindowingMode(rootToken, WINDOWING_MODE_MULTI_WINDOW)
- .setLaunchRoot(
- rootToken,
- CONTROLLED_WINDOWING_MODES,
- CONTROLLED_ACTIVITY_TYPES)
- .reparentTasks(
- null /* currentParent */,
- rootToken,
- CONTROLLED_WINDOWING_MODES,
- CONTROLLED_ACTIVITY_TYPES,
- true /* onTop */)
- // Moving the root task to top after the child tasks were re-parented , or the root
- // task cannot be visible and focused.
- .reorder(rootToken, true /* onTop */);
-
- mIsActive = true;
- }
-
- void deactivate(WindowContainerTransaction wct) {
- deactivate(wct, false /* toTop */);
- }
-
- void deactivate(WindowContainerTransaction wct, boolean toTop) {
- if (!mIsActive) return;
- mIsActive = false;
-
- if (mRootTaskInfo == null) return;
- final WindowContainerToken rootToken = mRootTaskInfo.token;
- wct.setLaunchRoot(
- rootToken,
- null,
- null)
- .reparentTasks(
- rootToken,
- null /* newParent */,
- CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
- CONTROLLED_ACTIVITY_TYPES,
- toTop)
- // We want this re-order to the bottom regardless since we are re-parenting
- // all its tasks.
- .reorder(rootToken, false /* onTop */);
- }
-
- void updateConfiguration(int windowingMode, Rect bounds, WindowContainerTransaction wct) {
- wct.setBounds(mRootTaskInfo.token, bounds)
- .setWindowingMode(mRootTaskInfo.token, windowingMode);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS
deleted file mode 100644
index 264e88f32bff..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# WM shell sub-modules stagesplit owner
-chenghsiuchang@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineManager.java
deleted file mode 100644
index 8fbad52c630f..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineManager.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.stagesplit;
-
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.view.IWindow;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.WindowlessWindowManager;
-import android.widget.FrameLayout;
-
-import com.android.wm.shell.R;
-
-/**
- * Handles drawing outline of the bounds of provided root surface. The outline will be drown with
- * the consideration of display insets like status bar, navigation bar and display cutout.
- */
-class OutlineManager extends WindowlessWindowManager {
- private static final String WINDOW_NAME = "SplitOutlineLayer";
- private final Context mContext;
- private final Rect mRootBounds = new Rect();
- private final Rect mTempRect = new Rect();
- private final Rect mLastOutlineBounds = new Rect();
- private final InsetsState mInsetsState = new InsetsState();
- private final int mExpandedTaskBarHeight;
- private OutlineView mOutlineView;
- private SurfaceControlViewHost mViewHost;
- private SurfaceControl mHostLeash;
- private SurfaceControl mLeash;
-
- OutlineManager(Context context, Configuration configuration) {
- super(configuration, null /* rootSurface */, null /* hostInputToken */);
- mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
- null /* options */);
- mExpandedTaskBarHeight = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.taskbar_frame_height);
- }
-
- @Override
- protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
- b.setParent(mHostLeash);
- }
-
- void inflate(SurfaceControl rootLeash, Rect rootBounds) {
- if (mLeash != null || mViewHost != null) return;
-
- mHostLeash = rootLeash;
- mRootBounds.set(rootBounds);
- mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
-
- final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(mContext)
- .inflate(R.layout.split_outline, null);
- mOutlineView = rootLayout.findViewById(R.id.split_outline);
-
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
- FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
- lp.width = mRootBounds.width();
- lp.height = mRootBounds.height();
- lp.token = new Binder();
- lp.setTitle(WINDOW_NAME);
- lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
- // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
- // TRUSTED_OVERLAY for windowless window without input channel.
- mViewHost.setView(rootLayout, lp);
- mLeash = getSurfaceControl(mViewHost.getWindowToken());
-
- drawOutline();
- }
-
- void release() {
- if (mViewHost != null) {
- mViewHost.release();
- mViewHost = null;
- }
- mRootBounds.setEmpty();
- mLastOutlineBounds.setEmpty();
- mOutlineView = null;
- mHostLeash = null;
- mLeash = null;
- }
-
- @Nullable
- SurfaceControl getOutlineLeash() {
- return mLeash;
- }
-
- void setVisibility(boolean visible) {
- if (mOutlineView != null) {
- mOutlineView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- }
- }
-
- void setRootBounds(Rect rootBounds) {
- if (mViewHost == null || mViewHost.getView() == null) {
- return;
- }
-
- if (!mRootBounds.equals(rootBounds)) {
- WindowManager.LayoutParams lp =
- (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
- lp.width = rootBounds.width();
- lp.height = rootBounds.height();
- mViewHost.relayout(lp);
- mRootBounds.set(rootBounds);
- drawOutline();
- }
- }
-
- void onInsetsChanged(InsetsState insetsState) {
- if (!mInsetsState.equals(insetsState)) {
- mInsetsState.set(insetsState);
- drawOutline();
- }
- }
-
- private void computeOutlineBounds(Rect rootBounds, InsetsState insetsState, Rect outBounds) {
- outBounds.set(rootBounds);
- final InsetsSource taskBarInsetsSource =
- insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
- // Only insets the divider bar with task bar when it's expanded so that the rounded corners
- // will be drawn against task bar.
- if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
- outBounds.inset(taskBarInsetsSource.calculateVisibleInsets(outBounds));
- }
-
- // Offset the coordinate from screen based to surface based.
- outBounds.offset(-rootBounds.left, -rootBounds.top);
- }
-
- void drawOutline() {
- if (mOutlineView == null) {
- return;
- }
-
- computeOutlineBounds(mRootBounds, mInsetsState, mTempRect);
- if (mTempRect.equals(mLastOutlineBounds)) {
- return;
- }
-
- ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) mOutlineView.getLayoutParams();
- lp.leftMargin = mTempRect.left;
- lp.topMargin = mTempRect.top;
- lp.width = mTempRect.width();
- lp.height = mTempRect.height();
- mOutlineView.setLayoutParams(lp);
- mLastOutlineBounds.set(mTempRect);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineView.java
deleted file mode 100644
index 92b1381fc808..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineView.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.stagesplit;
-
-import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
-import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
-import static android.view.RoundedCorner.POSITION_TOP_LEFT;
-import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.util.AttributeSet;
-import android.view.RoundedCorner;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.R;
-
-/** View for drawing split outline. */
-public class OutlineView extends View {
- private final Paint mPaint = new Paint();
- private final Path mPath = new Path();
- private final float[] mRadii = new float[8];
-
- public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeWidth(
- getResources().getDimension(R.dimen.accessibility_focus_highlight_stroke_width));
- mPaint.setColor(getResources().getColor(R.color.system_accent1_100, null));
- }
-
- @Override
- protected void onAttachedToWindow() {
- // TODO(b/200850654): match the screen corners with the actual display decor.
- mRadii[0] = mRadii[1] = getCornerRadius(POSITION_TOP_LEFT);
- mRadii[2] = mRadii[3] = getCornerRadius(POSITION_TOP_RIGHT);
- mRadii[4] = mRadii[5] = getCornerRadius(POSITION_BOTTOM_RIGHT);
- mRadii[6] = mRadii[7] = getCornerRadius(POSITION_BOTTOM_LEFT);
- }
-
- private int getCornerRadius(@RoundedCorner.Position int position) {
- final RoundedCorner roundedCorner = getDisplay().getRoundedCorner(position);
- return roundedCorner == null ? 0 : roundedCorner.getRadius();
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (changed) {
- mPath.reset();
- mPath.addRoundRect(0, 0, getWidth(), getHeight(), mRadii, Path.Direction.CW);
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- canvas.drawPath(mPath, mPaint);
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SideStage.java
deleted file mode 100644
index 55c4f3aea19a..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SideStage.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.stagesplit;
-
-import android.annotation.CallSuper;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.content.Context;
-import android.graphics.Rect;
-import android.view.InsetsSourceControl;
-import android.view.InsetsState;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayInsetsController;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-/**
- * Side stage for split-screen mode. Only tasks that are explicitly pinned to this stage show up
- * here. All other task are launch in the {@link MainStage}.
- *
- * @see StageCoordinator
- */
-class SideStage extends StageTaskListener implements
- DisplayInsetsController.OnInsetsChangedListener {
- private static final String TAG = SideStage.class.getSimpleName();
- private final Context mContext;
- private OutlineManager mOutlineManager;
-
- SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
- StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession,
- @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
- super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
- stageTaskUnfoldController);
- mContext = context;
- }
-
- void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
- WindowContainerTransaction wct) {
- final WindowContainerToken rootToken = mRootTaskInfo.token;
- wct.setBounds(rootToken, rootBounds)
- .reparent(task.token, rootToken, true /* onTop*/)
- // Moving the root task to top after the child tasks were reparented , or the root
- // task cannot be visible and focused.
- .reorder(rootToken, true /* onTop */);
- }
-
- boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
- // No matter if the root task is empty or not, moving the root to bottom because it no
- // longer preserves visible child task.
- wct.reorder(mRootTaskInfo.token, false /* onTop */);
- if (mChildrenTaskInfo.size() == 0) return false;
- wct.reparentTasks(
- mRootTaskInfo.token,
- null /* newParent */,
- CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
- CONTROLLED_ACTIVITY_TYPES,
- toTop);
- return true;
- }
-
- boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
- final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
- if (task == null) return false;
- wct.reparent(task.token, newParent, false /* onTop */);
- return true;
- }
-
- @Nullable
- public SurfaceControl getOutlineLeash() {
- return mOutlineManager.getOutlineLeash();
- }
-
- @Override
- @CallSuper
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- super.onTaskAppeared(taskInfo, leash);
- if (isRootTask(taskInfo)) {
- mOutlineManager = new OutlineManager(mContext, taskInfo.configuration);
- enableOutline(true);
- }
- }
-
- @Override
- @CallSuper
- public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- super.onTaskInfoChanged(taskInfo);
- if (isRootTask(taskInfo)) {
- mOutlineManager.setRootBounds(taskInfo.configuration.windowConfiguration.getBounds());
- }
- }
-
- private boolean isRootTask(ActivityManager.RunningTaskInfo taskInfo) {
- return mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId;
- }
-
- void enableOutline(boolean enable) {
- if (mOutlineManager == null) {
- return;
- }
-
- if (enable) {
- if (mRootTaskInfo != null) {
- mOutlineManager.inflate(mRootLeash,
- mRootTaskInfo.configuration.windowConfiguration.getBounds());
- }
- } else {
- mOutlineManager.release();
- }
- }
-
- void setOutlineVisibility(boolean visible) {
- mOutlineManager.setVisibility(visible);
- }
-
- @Override
- public void insetsChanged(InsetsState insetsState) {
- mOutlineManager.onInsetsChanged(insetsState);
- }
-
- @Override
- public void insetsControlChanged(InsetsState insetsState,
- InsetsSourceControl[] activeControls) {
- insetsChanged(insetsState);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreen.java
deleted file mode 100644
index c5d231262cd2..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreen.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.stagesplit;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-
-import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
-
-import java.util.concurrent.Executor;
-
-/**
- * Interface to engage split-screen feature.
- * TODO: Figure out which of these are actually needed outside of the Shell
- */
-@ExternalThread
-public interface SplitScreen {
- /**
- * Stage type isn't specified normally meaning to use what ever the default is.
- * E.g. exit split-screen and launch the app in fullscreen.
- */
- int STAGE_TYPE_UNDEFINED = -1;
- /**
- * The main stage type.
- * @see MainStage
- */
- int STAGE_TYPE_MAIN = 0;
-
- /**
- * The side stage type.
- * @see SideStage
- */
- int STAGE_TYPE_SIDE = 1;
-
- @IntDef(prefix = { "STAGE_TYPE_" }, value = {
- STAGE_TYPE_UNDEFINED,
- STAGE_TYPE_MAIN,
- STAGE_TYPE_SIDE
- })
- @interface StageType {}
-
- /** Callback interface for listening to changes in a split-screen stage. */
- interface SplitScreenListener {
- default void onStagePositionChanged(@StageType int stage, @SplitPosition int position) {}
- default void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {}
- default void onSplitVisibilityChanged(boolean visible) {}
- }
-
- /** Registers listener that gets split screen callback. */
- void registerSplitScreenListener(@NonNull SplitScreenListener listener,
- @NonNull Executor executor);
-
- /** Unregisters listener that gets split screen callback. */
- void unregisterSplitScreenListener(@NonNull SplitScreenListener listener);
-
- /**
- * Returns a binder that can be passed to an external process to manipulate SplitScreen.
- */
- default ISplitScreen createExternalInterface() {
- return null;
- }
-
- /**
- * Called when the keyguard occluded state changes.
- * @param occluded Indicates if the keyguard is now occluded.
- */
- void onKeyguardOccludedChanged(boolean occluded);
-
- /**
- * Called when the visibility of the keyguard changes.
- * @param showing Indicates if the keyguard is now visible.
- */
- void onKeyguardVisibilityChanged(boolean showing);
-
- /** Get a string representation of a stage type */
- static String stageTypeToString(@StageType int stage) {
- switch (stage) {
- case STAGE_TYPE_UNDEFINED: return "UNDEFINED";
- case STAGE_TYPE_MAIN: return "MAIN";
- case STAGE_TYPE_SIDE: return "SIDE";
- default: return "UNKNOWN(" + stage + ")";
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
deleted file mode 100644
index 07174051a344..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java
+++ /dev/null
@@ -1,595 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.stagesplit;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.RemoteAnimationTarget.MODE_OPENING;
-
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
-
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.LauncherApps;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.Slog;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.view.WindowManager;
-import android.window.RemoteTransition;
-import android.window.WindowContainerTransaction;
-
-import androidx.annotation.BinderThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.logging.InstanceId;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.DisplayInsetsController;
-import com.android.wm.shell.common.RemoteCallable;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
-import com.android.wm.shell.draganddrop.DragAndDropPolicy;
-import com.android.wm.shell.transition.LegacyTransitions;
-import com.android.wm.shell.transition.Transitions;
-
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
-import javax.inject.Provider;
-
-/**
- * Class manages split-screen multitasking mode and implements the main interface
- * {@link SplitScreen}.
- * @see StageCoordinator
- */
-// TODO(b/198577848): Implement split screen flicker test to consolidate CUJ of split screen.
-public class SplitScreenController implements DragAndDropPolicy.Starter,
- RemoteCallable<SplitScreenController> {
- private static final String TAG = SplitScreenController.class.getSimpleName();
-
- private final ShellTaskOrganizer mTaskOrganizer;
- private final SyncTransactionQueue mSyncQueue;
- private final Context mContext;
- private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
- private final ShellExecutor mMainExecutor;
- private final SplitScreenImpl mImpl = new SplitScreenImpl();
- private final DisplayImeController mDisplayImeController;
- private final DisplayInsetsController mDisplayInsetsController;
- private final Transitions mTransitions;
- private final TransactionPool mTransactionPool;
- private final SplitscreenEventLogger mLogger;
- private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider;
-
- private StageCoordinator mStageCoordinator;
-
- public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
- SyncTransactionQueue syncQueue, Context context,
- RootTaskDisplayAreaOrganizer rootTDAOrganizer,
- ShellExecutor mainExecutor, DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController,
- Transitions transitions, TransactionPool transactionPool,
- Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
- mTaskOrganizer = shellTaskOrganizer;
- mSyncQueue = syncQueue;
- mContext = context;
- mRootTDAOrganizer = rootTDAOrganizer;
- mMainExecutor = mainExecutor;
- mDisplayImeController = displayImeController;
- mDisplayInsetsController = displayInsetsController;
- mTransitions = transitions;
- mTransactionPool = transactionPool;
- mUnfoldControllerProvider = unfoldControllerProvider;
- mLogger = new SplitscreenEventLogger();
- }
-
- public SplitScreen asSplitScreen() {
- return mImpl;
- }
-
- @Override
- public Context getContext() {
- return mContext;
- }
-
- @Override
- public ShellExecutor getRemoteCallExecutor() {
- return mMainExecutor;
- }
-
- public void onOrganizerRegistered() {
- if (mStageCoordinator == null) {
- // TODO: Multi-display
- mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
- mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
- mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
- mUnfoldControllerProvider);
- }
- }
-
- public boolean isSplitScreenVisible() {
- return mStageCoordinator.isSplitScreenVisible();
- }
-
- public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) {
- final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
- if (task == null) {
- throw new IllegalArgumentException("Unknown taskId" + taskId);
- }
- return moveToSideStage(task, sideStagePosition);
- }
-
- public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SplitPosition int sideStagePosition) {
- return mStageCoordinator.moveToSideStage(task, sideStagePosition);
- }
-
- public boolean removeFromSideStage(int taskId) {
- return mStageCoordinator.removeFromSideStage(taskId);
- }
-
- public void setSideStageOutline(boolean enable) {
- mStageCoordinator.setSideStageOutline(enable);
- }
-
- public void setSideStagePosition(@SplitPosition int sideStagePosition) {
- mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */);
- }
-
- public void setSideStageVisibility(boolean visible) {
- mStageCoordinator.setSideStageVisibility(visible);
- }
-
- public void enterSplitScreen(int taskId, boolean leftOrTop) {
- moveToSideStage(taskId,
- leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
- }
-
- public void exitSplitScreen(int toTopTaskId, int exitReason) {
- mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason);
- }
-
- public void onKeyguardOccludedChanged(boolean occluded) {
- mStageCoordinator.onKeyguardOccludedChanged(occluded);
- }
-
- public void onKeyguardVisibilityChanged(boolean showing) {
- mStageCoordinator.onKeyguardVisibilityChanged(showing);
- }
-
- public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
- mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
- }
-
- public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
- mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
- }
-
- public void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
- mStageCoordinator.registerSplitScreenListener(listener);
- }
-
- public void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) {
- mStageCoordinator.unregisterSplitScreenListener(listener);
- }
-
- public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
- options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
- null /* wct */);
-
- try {
- ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to launch task", e);
- }
- }
-
- public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
- @Nullable Bundle options, UserHandle user) {
- options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
- null /* wct */);
-
- try {
- LauncherApps launcherApps =
- mContext.getSystemService(LauncherApps.class);
- launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
- options, user);
- } catch (ActivityNotFoundException e) {
- Slog.e(TAG, "Failed to launch shortcut", e);
- }
- }
-
- public void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
- @Nullable Bundle options) {
- if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
- startIntentLegacy(intent, fillInIntent, position, options);
- return;
- }
- mStageCoordinator.startIntent(intent, fillInIntent, STAGE_TYPE_UNDEFINED, position, options,
- null /* remote */);
- }
-
- private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
- @SplitPosition int position, @Nullable Bundle options) {
- LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
- @Override
- public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
- IRemoteAnimationFinishedCallback finishedCallback,
- SurfaceControl.Transaction t) {
- mStageCoordinator.updateSurfaceBounds(null /* layout */, t,
- false /* applyResizingOffset */);
-
- if (apps != null) {
- for (int i = 0; i < apps.length; ++i) {
- if (apps[i].mode == MODE_OPENING) {
- t.show(apps[i].leash);
- }
- }
- }
-
- t.apply();
- if (finishedCallback != null) {
- try {
- finishedCallback.onAnimationFinished();
- } catch (RemoteException e) {
- Slog.e(TAG, "Error finishing legacy transition: ", e);
- }
- }
- }
- };
- WindowContainerTransaction wct = new WindowContainerTransaction();
- options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
- wct.sendPendingIntent(intent, fillInIntent, options);
- mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
- }
-
- RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, RemoteAnimationTarget[] apps) {
- if (!isSplitScreenVisible()) return null;
- final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
- .setContainerLayer()
- .setName("RecentsAnimationSplitTasks")
- .setHidden(false)
- .setCallsite("SplitScreenController#onGoingtoRecentsLegacy");
- mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
- SurfaceControl sc = builder.build();
- SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
-
- // Ensure that we order these in the parent in the right z-order as their previous order
- Arrays.sort(apps, (a1, a2) -> a1.prefixOrderIndex - a2.prefixOrderIndex);
- int layer = 1;
- for (RemoteAnimationTarget appTarget : apps) {
- transaction.reparent(appTarget.leash, sc);
- transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
- appTarget.screenSpaceBounds.top);
- transaction.setLayer(appTarget.leash, layer++);
- }
- transaction.apply();
- transaction.close();
- return new RemoteAnimationTarget[]{
- mStageCoordinator.getDividerBarLegacyTarget(),
- mStageCoordinator.getOutlineLegacyTarget()};
- }
-
- /**
- * Sets drag info to be logged when splitscreen is entered.
- */
- public void logOnDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) {
- mStageCoordinator.logOnDroppedToSplit(position, dragSessionId);
- }
-
- public void dump(@NonNull PrintWriter pw, String prefix) {
- pw.println(prefix + TAG);
- if (mStageCoordinator != null) {
- mStageCoordinator.dump(pw, prefix);
- }
- }
-
- /**
- * The interface for calls from outside the Shell, within the host process.
- */
- @ExternalThread
- private class SplitScreenImpl implements SplitScreen {
- private ISplitScreenImpl mISplitScreen;
- private final ArrayMap<SplitScreenListener, Executor> mExecutors = new ArrayMap<>();
- private final SplitScreenListener mListener = new SplitScreenListener() {
- @Override
- public void onStagePositionChanged(int stage, int position) {
- for (int i = 0; i < mExecutors.size(); i++) {
- final int index = i;
- mExecutors.valueAt(index).execute(() -> {
- mExecutors.keyAt(index).onStagePositionChanged(stage, position);
- });
- }
- }
-
- @Override
- public void onTaskStageChanged(int taskId, int stage, boolean visible) {
- for (int i = 0; i < mExecutors.size(); i++) {
- final int index = i;
- mExecutors.valueAt(index).execute(() -> {
- mExecutors.keyAt(index).onTaskStageChanged(taskId, stage, visible);
- });
- }
- }
-
- @Override
- public void onSplitVisibilityChanged(boolean visible) {
- for (int i = 0; i < mExecutors.size(); i++) {
- final int index = i;
- mExecutors.valueAt(index).execute(() -> {
- mExecutors.keyAt(index).onSplitVisibilityChanged(visible);
- });
- }
- }
- };
-
- @Override
- public ISplitScreen createExternalInterface() {
- if (mISplitScreen != null) {
- mISplitScreen.invalidate();
- }
- mISplitScreen = new ISplitScreenImpl(SplitScreenController.this);
- return mISplitScreen;
- }
-
- @Override
- public void onKeyguardOccludedChanged(boolean occluded) {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.onKeyguardOccludedChanged(occluded);
- });
- }
-
- @Override
- public void registerSplitScreenListener(SplitScreenListener listener, Executor executor) {
- if (mExecutors.containsKey(listener)) return;
-
- mMainExecutor.execute(() -> {
- if (mExecutors.size() == 0) {
- SplitScreenController.this.registerSplitScreenListener(mListener);
- }
-
- mExecutors.put(listener, executor);
- });
-
- executor.execute(() -> {
- mStageCoordinator.sendStatusToListener(listener);
- });
- }
-
- @Override
- public void unregisterSplitScreenListener(SplitScreenListener listener) {
- mMainExecutor.execute(() -> {
- mExecutors.remove(listener);
-
- if (mExecutors.size() == 0) {
- SplitScreenController.this.unregisterSplitScreenListener(mListener);
- }
- });
- }
-
- @Override
- public void onKeyguardVisibilityChanged(boolean showing) {
- mMainExecutor.execute(() -> {
- SplitScreenController.this.onKeyguardVisibilityChanged(showing);
- });
- }
- }
-
- /**
- * The interface for calls from outside the host process.
- */
- @BinderThread
- private static class ISplitScreenImpl extends ISplitScreen.Stub {
- private SplitScreenController mController;
- private ISplitScreenListener mListener;
- private final SplitScreen.SplitScreenListener mSplitScreenListener =
- new SplitScreen.SplitScreenListener() {
- @Override
- public void onStagePositionChanged(int stage, int position) {
- try {
- if (mListener != null) {
- mListener.onStagePositionChanged(stage, position);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "onStagePositionChanged", e);
- }
- }
-
- @Override
- public void onTaskStageChanged(int taskId, int stage, boolean visible) {
- try {
- if (mListener != null) {
- mListener.onTaskStageChanged(taskId, stage, visible);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "onTaskStageChanged", e);
- }
- }
- };
- private final IBinder.DeathRecipient mListenerDeathRecipient =
- new IBinder.DeathRecipient() {
- @Override
- @BinderThread
- public void binderDied() {
- final SplitScreenController controller = mController;
- controller.getRemoteCallExecutor().execute(() -> {
- mListener = null;
- controller.unregisterSplitScreenListener(mSplitScreenListener);
- });
- }
- };
-
- public ISplitScreenImpl(SplitScreenController controller) {
- mController = controller;
- }
-
- /**
- * Invalidates this instance, preventing future calls from updating the controller.
- */
- void invalidate() {
- mController = null;
- }
-
- @Override
- public void registerSplitScreenListener(ISplitScreenListener listener) {
- executeRemoteCallWithTaskPermission(mController, "registerSplitScreenListener",
- (controller) -> {
- if (mListener != null) {
- mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
- 0 /* flags */);
- }
- if (listener != null) {
- try {
- listener.asBinder().linkToDeath(mListenerDeathRecipient,
- 0 /* flags */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to link to death");
- return;
- }
- }
- mListener = listener;
- controller.registerSplitScreenListener(mSplitScreenListener);
- });
- }
-
- @Override
- public void unregisterSplitScreenListener(ISplitScreenListener listener) {
- executeRemoteCallWithTaskPermission(mController, "unregisterSplitScreenListener",
- (controller) -> {
- if (mListener != null) {
- mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
- 0 /* flags */);
- }
- mListener = null;
- controller.unregisterSplitScreenListener(mSplitScreenListener);
- });
- }
-
- @Override
- public void exitSplitScreen(int toTopTaskId) {
- executeRemoteCallWithTaskPermission(mController, "exitSplitScreen",
- (controller) -> {
- controller.exitSplitScreen(toTopTaskId,
- FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__UNKNOWN_EXIT);
- });
- }
-
- @Override
- public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
- executeRemoteCallWithTaskPermission(mController, "exitSplitScreenOnHide",
- (controller) -> {
- controller.exitSplitScreenOnHide(exitSplitScreenOnHide);
- });
- }
-
- @Override
- public void setSideStageVisibility(boolean visible) {
- executeRemoteCallWithTaskPermission(mController, "setSideStageVisibility",
- (controller) -> {
- controller.setSideStageVisibility(visible);
- });
- }
-
- @Override
- public void removeFromSideStage(int taskId) {
- executeRemoteCallWithTaskPermission(mController, "removeFromSideStage",
- (controller) -> {
- controller.removeFromSideStage(taskId);
- });
- }
-
- @Override
- public void startTask(int taskId, int stage, int position, @Nullable Bundle options) {
- executeRemoteCallWithTaskPermission(mController, "startTask",
- (controller) -> {
- controller.startTask(taskId, position, options);
- });
- }
-
- @Override
- public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
- int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- RemoteAnimationAdapter adapter) {
- executeRemoteCallWithTaskPermission(mController, "startTasks",
- (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
- mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,
- adapter));
- }
-
- @Override
- public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
- int sideTaskId, @Nullable Bundle sideOptions,
- @SplitPosition int sidePosition,
- @Nullable RemoteTransition remoteTransition) {
- executeRemoteCallWithTaskPermission(mController, "startTasks",
- (controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions,
- sideTaskId, sideOptions, sidePosition, remoteTransition));
- }
-
- @Override
- public void startShortcut(String packageName, String shortcutId, int stage, int position,
- @Nullable Bundle options, UserHandle user) {
- executeRemoteCallWithTaskPermission(mController, "startShortcut",
- (controller) -> {
- controller.startShortcut(packageName, shortcutId, position,
- options, user);
- });
- }
-
- @Override
- public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position,
- @Nullable Bundle options) {
- executeRemoteCallWithTaskPermission(mController, "startIntent",
- (controller) -> {
- controller.startIntent(intent, fillInIntent, position, options);
- });
- }
-
- @Override
- public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel,
- RemoteAnimationTarget[] apps) {
- final RemoteAnimationTarget[][] out = new RemoteAnimationTarget[][]{null};
- executeRemoteCallWithTaskPermission(mController, "onGoingToRecentsLegacy",
- (controller) -> out[0] = controller.onGoingToRecentsLegacy(cancel, apps),
- true /* blocking */);
- return out[0];
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java
deleted file mode 100644
index 018365420177..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.stagesplit;
-
-import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.view.WindowManager.TRANSIT_TO_BACK;
-import static android.view.WindowManager.TRANSIT_TO_FRONT;
-import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
-
-import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
-import static com.android.wm.shell.transition.Transitions.isOpeningType;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.window.RemoteTransition;
-import android.window.TransitionInfo;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.transition.OneShotRemoteHandler;
-import com.android.wm.shell.transition.Transitions;
-
-import java.util.ArrayList;
-
-/** Manages transition animations for split-screen. */
-class SplitScreenTransitions {
- private static final String TAG = "SplitScreenTransitions";
-
- /** Flag applied to a transition change to identify it as a divider bar for animation. */
- public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM;
-
- private final TransactionPool mTransactionPool;
- private final Transitions mTransitions;
- private final Runnable mOnFinish;
-
- IBinder mPendingDismiss = null;
- IBinder mPendingEnter = null;
-
- private IBinder mAnimatingTransition = null;
- private OneShotRemoteHandler mRemoteHandler = null;
-
- private Transitions.TransitionFinishCallback mRemoteFinishCB = (wct, wctCB) -> {
- if (wct != null || wctCB != null) {
- throw new UnsupportedOperationException("finish transactions not supported yet.");
- }
- onFinish();
- };
-
- /** Keeps track of currently running animations */
- private final ArrayList<Animator> mAnimations = new ArrayList<>();
-
- private Transitions.TransitionFinishCallback mFinishCallback = null;
- private SurfaceControl.Transaction mFinishTransaction;
-
- SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions,
- @NonNull Runnable onFinishCallback) {
- mTransactionPool = pool;
- mTransitions = transitions;
- mOnFinish = onFinishCallback;
- }
-
- void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback,
- @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) {
- mFinishCallback = finishCallback;
- mAnimatingTransition = transition;
- if (mRemoteHandler != null) {
- mRemoteHandler.startAnimation(transition, info, startTransaction, finishTransaction,
- mRemoteFinishCB);
- mRemoteHandler = null;
- return;
- }
- playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot);
- }
-
- private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
- @NonNull WindowContainerToken sideRoot) {
- mFinishTransaction = mTransactionPool.acquire();
-
- // Play some place-holder fade animations
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- final SurfaceControl leash = change.getLeash();
- final int mode = info.getChanges().get(i).getMode();
-
- if (mode == TRANSIT_CHANGE) {
- if (change.getParent() != null) {
- // This is probably reparented, so we want the parent to be immediately visible
- final TransitionInfo.Change parentChange = info.getChange(change.getParent());
- t.show(parentChange.getLeash());
- t.setAlpha(parentChange.getLeash(), 1.f);
- // and then animate this layer outside the parent (since, for example, this is
- // the home task animating from fullscreen to part-screen).
- t.reparent(leash, info.getRootLeash());
- t.setLayer(leash, info.getChanges().size() - i);
- // build the finish reparent/reposition
- mFinishTransaction.reparent(leash, parentChange.getLeash());
- mFinishTransaction.setPosition(leash,
- change.getEndRelOffset().x, change.getEndRelOffset().y);
- }
- // TODO(shell-transitions): screenshot here
- final Rect startBounds = new Rect(change.getStartAbsBounds());
- final Rect endBounds = new Rect(change.getEndAbsBounds());
- startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
- endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
- startExampleResizeAnimation(leash, startBounds, endBounds);
- }
- if (change.getParent() != null) {
- continue;
- }
-
- if (transition == mPendingEnter && (mainRoot.equals(change.getContainer())
- || sideRoot.equals(change.getContainer()))) {
- t.setWindowCrop(leash, change.getStartAbsBounds().width(),
- change.getStartAbsBounds().height());
- }
- boolean isOpening = isOpeningType(info.getType());
- if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
- // fade in
- startExampleAnimation(leash, true /* show */);
- } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) {
- // fade out
- if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
- // Dismissing via snap-to-top/bottom means that the dismissed task is already
- // not-visible (usually cropped to oblivion) so immediately set its alpha to 0
- // and don't animate it so it doesn't pop-in when reparented.
- t.setAlpha(leash, 0.f);
- } else {
- startExampleAnimation(leash, false /* show */);
- }
- }
- }
- t.apply();
- onFinish();
- }
-
- /** Starts a transition to enter split with a remote transition animator. */
- IBinder startEnterTransition(@WindowManager.TransitionType int transitType,
- @NonNull WindowContainerTransaction wct, @Nullable RemoteTransition remoteTransition,
- @NonNull Transitions.TransitionHandler handler) {
- if (remoteTransition != null) {
- // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff)
- mRemoteHandler = new OneShotRemoteHandler(
- mTransitions.getMainExecutor(), remoteTransition);
- }
- final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
- mPendingEnter = transition;
- if (mRemoteHandler != null) {
- mRemoteHandler.setTransition(transition);
- }
- return transition;
- }
-
- /** Starts a transition for dismissing split after dragging the divider to a screen edge */
- IBinder startSnapToDismiss(@NonNull WindowContainerTransaction wct,
- @NonNull Transitions.TransitionHandler handler) {
- final IBinder transition = mTransitions.startTransition(
- TRANSIT_SPLIT_DISMISS_SNAP, wct, handler);
- mPendingDismiss = transition;
- return transition;
- }
-
- void onFinish() {
- if (!mAnimations.isEmpty()) return;
- mOnFinish.run();
- if (mFinishTransaction != null) {
- mFinishTransaction.apply();
- mTransactionPool.release(mFinishTransaction);
- mFinishTransaction = null;
- }
- mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
- mFinishCallback = null;
- if (mAnimatingTransition == mPendingEnter) {
- mPendingEnter = null;
- }
- if (mAnimatingTransition == mPendingDismiss) {
- mPendingDismiss = null;
- }
- mAnimatingTransition = null;
- }
-
- // TODO(shell-transitions): real animations
- private void startExampleAnimation(@NonNull SurfaceControl leash, boolean show) {
- final float end = show ? 1.f : 0.f;
- final float start = 1.f - end;
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- final ValueAnimator va = ValueAnimator.ofFloat(start, end);
- va.setDuration(500);
- va.addUpdateListener(animation -> {
- float fraction = animation.getAnimatedFraction();
- transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction);
- transaction.apply();
- });
- final Runnable finisher = () -> {
- transaction.setAlpha(leash, end);
- transaction.apply();
- mTransactionPool.release(transaction);
- mTransitions.getMainExecutor().execute(() -> {
- mAnimations.remove(va);
- onFinish();
- });
- };
- va.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) { }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- finisher.run();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- finisher.run();
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) { }
- });
- mAnimations.add(va);
- mTransitions.getAnimExecutor().execute(va::start);
- }
-
- // TODO(shell-transitions): real animations
- private void startExampleResizeAnimation(@NonNull SurfaceControl leash,
- @NonNull Rect startBounds, @NonNull Rect endBounds) {
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- final ValueAnimator va = ValueAnimator.ofFloat(0.f, 1.f);
- va.setDuration(500);
- va.addUpdateListener(animation -> {
- float fraction = animation.getAnimatedFraction();
- transaction.setWindowCrop(leash,
- (int) (startBounds.width() * (1.f - fraction) + endBounds.width() * fraction),
- (int) (startBounds.height() * (1.f - fraction)
- + endBounds.height() * fraction));
- transaction.setPosition(leash,
- startBounds.left * (1.f - fraction) + endBounds.left * fraction,
- startBounds.top * (1.f - fraction) + endBounds.top * fraction);
- transaction.apply();
- });
- final Runnable finisher = () -> {
- transaction.setWindowCrop(leash, 0, 0);
- transaction.setPosition(leash, endBounds.left, endBounds.top);
- transaction.apply();
- mTransactionPool.release(transaction);
- mTransitions.getMainExecutor().execute(() -> {
- mAnimations.remove(va);
- onFinish();
- });
- };
- va.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- finisher.run();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- finisher.run();
- }
- });
- mAnimations.add(va);
- mTransitions.getAnimExecutor().execute(va::start);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitscreenEventLogger.java
deleted file mode 100644
index e1850396a5c0..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitscreenEventLogger.java
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.stagesplit;
-
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
-
-import com.android.internal.logging.InstanceId;
-import com.android.internal.logging.InstanceIdSequence;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
-
-/**
- * Helper class that to log Drag & Drop UIEvents for a single session, see also go/uievent
- */
-public class SplitscreenEventLogger {
-
- // Used to generate instance ids for this drag if one is not provided
- private final InstanceIdSequence mIdSequence;
-
- // The instance id for the current splitscreen session (from start to end)
- private InstanceId mLoggerSessionId;
-
- // Drag info
- private @SplitPosition int mDragEnterPosition;
- private InstanceId mDragEnterSessionId;
-
- // For deduping async events
- private int mLastMainStagePosition = -1;
- private int mLastMainStageUid = -1;
- private int mLastSideStagePosition = -1;
- private int mLastSideStageUid = -1;
- private float mLastSplitRatio = -1f;
-
- public SplitscreenEventLogger() {
- mIdSequence = new InstanceIdSequence(Integer.MAX_VALUE);
- }
-
- /**
- * Return whether a splitscreen session has started.
- */
- public boolean hasStartedSession() {
- return mLoggerSessionId != null;
- }
-
- /**
- * May be called before logEnter() to indicate that the session was started from a drag.
- */
- public void enterRequestedByDrag(@SplitPosition int position, InstanceId dragSessionId) {
- mDragEnterPosition = position;
- mDragEnterSessionId = dragSessionId;
- }
-
- /**
- * Logs when the user enters splitscreen.
- */
- public void logEnter(float splitRatio,
- @SplitPosition int mainStagePosition, int mainStageUid,
- @SplitPosition int sideStagePosition, int sideStageUid,
- boolean isLandscape) {
- mLoggerSessionId = mIdSequence.newInstanceId();
- int enterReason = mDragEnterPosition != SPLIT_POSITION_UNDEFINED
- ? getDragEnterReasonFromSplitPosition(mDragEnterPosition, isLandscape)
- : SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW;
- updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
- mainStageUid);
- updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
- sideStageUid);
- updateSplitRatioState(splitRatio);
- FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
- FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__ENTER,
- enterReason,
- 0 /* exitReason */,
- splitRatio,
- mLastMainStagePosition,
- mLastMainStageUid,
- mLastSideStagePosition,
- mLastSideStageUid,
- mDragEnterSessionId != null ? mDragEnterSessionId.getId() : 0,
- mLoggerSessionId.getId());
- }
-
- /**
- * Logs when the user exits splitscreen. Only one of the main or side stages should be
- * specified to indicate which position was focused as a part of exiting (both can be unset).
- */
- public void logExit(int exitReason, @SplitPosition int mainStagePosition, int mainStageUid,
- @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
- if (mLoggerSessionId == null) {
- // Ignore changes until we've started logging the session
- return;
- }
- if ((mainStagePosition != SPLIT_POSITION_UNDEFINED
- && sideStagePosition != SPLIT_POSITION_UNDEFINED)
- || (mainStageUid != 0 && sideStageUid != 0)) {
- throw new IllegalArgumentException("Only main or side stage should be set");
- }
- FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
- FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__EXIT,
- 0 /* enterReason */,
- exitReason,
- 0f /* splitRatio */,
- getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
- mainStageUid,
- getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
- sideStageUid,
- 0 /* dragInstanceId */,
- mLoggerSessionId.getId());
-
- // Reset states
- mLoggerSessionId = null;
- mDragEnterPosition = SPLIT_POSITION_UNDEFINED;
- mDragEnterSessionId = null;
- mLastMainStagePosition = -1;
- mLastMainStageUid = -1;
- mLastSideStagePosition = -1;
- mLastSideStageUid = -1;
- }
-
- /**
- * Logs when an app in the main stage changes.
- */
- public void logMainStageAppChange(@SplitPosition int mainStagePosition, int mainStageUid,
- boolean isLandscape) {
- if (mLoggerSessionId == null) {
- // Ignore changes until we've started logging the session
- return;
- }
- if (!updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition,
- isLandscape), mainStageUid)) {
- // Ignore if there are no user perceived changes
- return;
- }
-
- FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
- FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__APP_CHANGE,
- 0 /* enterReason */,
- 0 /* exitReason */,
- 0f /* splitRatio */,
- mLastMainStagePosition,
- mLastMainStageUid,
- 0 /* sideStagePosition */,
- 0 /* sideStageUid */,
- 0 /* dragInstanceId */,
- mLoggerSessionId.getId());
- }
-
- /**
- * Logs when an app in the side stage changes.
- */
- public void logSideStageAppChange(@SplitPosition int sideStagePosition, int sideStageUid,
- boolean isLandscape) {
- if (mLoggerSessionId == null) {
- // Ignore changes until we've started logging the session
- return;
- }
- if (!updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition,
- isLandscape), sideStageUid)) {
- // Ignore if there are no user perceived changes
- return;
- }
-
- FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
- FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__APP_CHANGE,
- 0 /* enterReason */,
- 0 /* exitReason */,
- 0f /* splitRatio */,
- 0 /* mainStagePosition */,
- 0 /* mainStageUid */,
- mLastSideStagePosition,
- mLastSideStageUid,
- 0 /* dragInstanceId */,
- mLoggerSessionId.getId());
- }
-
- /**
- * Logs when the splitscreen ratio changes.
- */
- public void logResize(float splitRatio) {
- if (mLoggerSessionId == null) {
- // Ignore changes until we've started logging the session
- return;
- }
- if (splitRatio <= 0f || splitRatio >= 1f) {
- // Don't bother reporting resizes that end up dismissing the split, that will be logged
- // via the exit event
- return;
- }
- if (!updateSplitRatioState(splitRatio)) {
- // Ignore if there are no user perceived changes
- return;
- }
- FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
- FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__RESIZE,
- 0 /* enterReason */,
- 0 /* exitReason */,
- mLastSplitRatio,
- 0 /* mainStagePosition */, 0 /* mainStageUid */,
- 0 /* sideStagePosition */, 0 /* sideStageUid */,
- 0 /* dragInstanceId */,
- mLoggerSessionId.getId());
- }
-
- /**
- * Logs when the apps in splitscreen are swapped.
- */
- public void logSwap(@SplitPosition int mainStagePosition, int mainStageUid,
- @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
- if (mLoggerSessionId == null) {
- // Ignore changes until we've started logging the session
- return;
- }
-
- updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
- mainStageUid);
- updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
- sideStageUid);
- FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
- FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__SWAP,
- 0 /* enterReason */,
- 0 /* exitReason */,
- 0f /* splitRatio */,
- mLastMainStagePosition,
- mLastMainStageUid,
- mLastSideStagePosition,
- mLastSideStageUid,
- 0 /* dragInstanceId */,
- mLoggerSessionId.getId());
- }
-
- private boolean updateMainStageState(int mainStagePosition, int mainStageUid) {
- boolean changed = (mLastMainStagePosition != mainStagePosition)
- || (mLastMainStageUid != mainStageUid);
- if (!changed) {
- return false;
- }
-
- mLastMainStagePosition = mainStagePosition;
- mLastMainStageUid = mainStageUid;
- return true;
- }
-
- private boolean updateSideStageState(int sideStagePosition, int sideStageUid) {
- boolean changed = (mLastSideStagePosition != sideStagePosition)
- || (mLastSideStageUid != sideStageUid);
- if (!changed) {
- return false;
- }
-
- mLastSideStagePosition = sideStagePosition;
- mLastSideStageUid = sideStageUid;
- return true;
- }
-
- private boolean updateSplitRatioState(float splitRatio) {
- boolean changed = Float.compare(mLastSplitRatio, splitRatio) != 0;
- if (!changed) {
- return false;
- }
-
- mLastSplitRatio = splitRatio;
- return true;
- }
-
- public int getDragEnterReasonFromSplitPosition(@SplitPosition int position,
- boolean isLandscape) {
- if (isLandscape) {
- return position == SPLIT_POSITION_TOP_OR_LEFT
- ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_LEFT
- : FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_RIGHT;
- } else {
- return position == SPLIT_POSITION_TOP_OR_LEFT
- ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_TOP
- : FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_BOTTOM;
- }
- }
-
- private int getMainStagePositionFromSplitPosition(@SplitPosition int position,
- boolean isLandscape) {
- if (position == SPLIT_POSITION_UNDEFINED) {
- return 0;
- }
- if (isLandscape) {
- return position == SPLIT_POSITION_TOP_OR_LEFT
- ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__LEFT
- : FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__RIGHT;
- } else {
- return position == SPLIT_POSITION_TOP_OR_LEFT
- ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__TOP
- : FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__BOTTOM;
- }
- }
-
- private int getSideStagePositionFromSplitPosition(@SplitPosition int position,
- boolean isLandscape) {
- if (position == SPLIT_POSITION_UNDEFINED) {
- return 0;
- }
- if (isLandscape) {
- return position == SPLIT_POSITION_TOP_OR_LEFT
- ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__LEFT
- : FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__RIGHT;
- } else {
- return position == SPLIT_POSITION_TOP_OR_LEFT
- ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__TOP
- : FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__BOTTOM;
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
deleted file mode 100644
index de0feeecad4b..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
+++ /dev/null
@@ -1,1333 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.stagesplit;
-
-import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.view.WindowManager.TRANSIT_TO_BACK;
-import static android.view.WindowManager.TRANSIT_TO_FRONT;
-import static android.view.WindowManager.transitTypeToString;
-
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
-import static com.android.wm.shell.stagesplit.SplitScreen.STAGE_TYPE_MAIN;
-import static com.android.wm.shell.stagesplit.SplitScreen.STAGE_TYPE_SIDE;
-import static com.android.wm.shell.stagesplit.SplitScreen.STAGE_TYPE_UNDEFINED;
-import static com.android.wm.shell.stagesplit.SplitScreen.stageTypeToString;
-import static com.android.wm.shell.stagesplit.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
-import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
-import static com.android.wm.shell.transition.Transitions.isClosingType;
-import static com.android.wm.shell.transition.Transitions.isOpeningType;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
-import android.app.PendingIntent;
-import android.app.WindowConfiguration;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.hardware.devicestate.DeviceStateManager;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.Slog;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationRunner;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.view.WindowManager;
-import android.window.DisplayAreaInfo;
-import android.window.RemoteTransition;
-import android.window.TransitionInfo;
-import android.window.TransitionRequestInfo;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.InstanceId;
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.DisplayInsetsController;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.common.split.SplitLayout;
-import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
-import com.android.wm.shell.common.split.SplitWindowManager;
-import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.transition.Transitions;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-import javax.inject.Provider;
-
-/**
- * Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
- * {@link SideStage} stages.
- * Some high-level rules:
- * - The {@link StageCoordinator} is only considered active if the {@link SideStage} contains at
- * least one child task.
- * - The {@link MainStage} should only have children if the coordinator is active.
- * - The {@link SplitLayout} divider is only visible if both the {@link MainStage}
- * and {@link SideStage} are visible.
- * - The {@link MainStage} configuration is fullscreen when the {@link SideStage} isn't visible.
- * This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
- * {@link #onStageHasChildrenChanged(StageListenerImpl).}
- */
-class StageCoordinator implements SplitLayout.SplitLayoutHandler,
- RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler {
-
- private static final String TAG = StageCoordinator.class.getSimpleName();
-
- /** internal value for mDismissTop that represents no dismiss */
- private static final int NO_DISMISS = -2;
-
- private final SurfaceSession mSurfaceSession = new SurfaceSession();
-
- private final MainStage mMainStage;
- private final StageListenerImpl mMainStageListener = new StageListenerImpl();
- private final StageTaskUnfoldController mMainUnfoldController;
- private final SideStage mSideStage;
- private final StageListenerImpl mSideStageListener = new StageListenerImpl();
- private final StageTaskUnfoldController mSideUnfoldController;
- @SplitPosition
- private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
-
- private final int mDisplayId;
- private SplitLayout mSplitLayout;
- private boolean mDividerVisible;
- private final SyncTransactionQueue mSyncQueue;
- private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
- private final ShellTaskOrganizer mTaskOrganizer;
- private DisplayAreaInfo mDisplayAreaInfo;
- private final Context mContext;
- private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
- private final DisplayImeController mDisplayImeController;
- private final DisplayInsetsController mDisplayInsetsController;
- private final SplitScreenTransitions mSplitTransitions;
- private final SplitscreenEventLogger mLogger;
- private boolean mExitSplitScreenOnHide;
- private boolean mKeyguardOccluded;
-
- // TODO(b/187041611): remove this flag after totally deprecated legacy split
- /** Whether the device is supporting legacy split or not. */
- private boolean mUseLegacySplit;
-
- @SplitScreen.StageType private int mDismissTop = NO_DISMISS;
-
- /** The target stage to dismiss to when unlock after folded. */
- @SplitScreen.StageType private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
-
- private final Runnable mOnTransitionAnimationComplete = () -> {
- // If still playing, let it finish.
- if (!isSplitScreenVisible()) {
- // Update divider state after animation so that it is still around and positioned
- // properly for the animation itself.
- setDividerVisibility(false);
- mSplitLayout.resetDividerPosition();
- }
- mDismissTop = NO_DISMISS;
- };
-
- private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
- new SplitWindowManager.ParentContainerCallbacks() {
- @Override
- public void attachToParentSurface(SurfaceControl.Builder b) {
- mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b);
- }
-
- @Override
- public void onLeashReady(SurfaceControl leash) {
- mSyncQueue.runInSync(t -> applyDividerVisibility(t));
- }
- };
-
- StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
- RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
- DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool, SplitscreenEventLogger logger,
- Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
- mContext = context;
- mDisplayId = displayId;
- mSyncQueue = syncQueue;
- mRootTDAOrganizer = rootTDAOrganizer;
- mTaskOrganizer = taskOrganizer;
- mLogger = logger;
- mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
- mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
-
- mMainStage = new MainStage(
- mTaskOrganizer,
- mDisplayId,
- mMainStageListener,
- mSyncQueue,
- mSurfaceSession,
- mMainUnfoldController);
- mSideStage = new SideStage(
- mContext,
- mTaskOrganizer,
- mDisplayId,
- mSideStageListener,
- mSyncQueue,
- mSurfaceSession,
- mSideUnfoldController);
- mDisplayImeController = displayImeController;
- mDisplayInsetsController = displayInsetsController;
- mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSideStage);
- mRootTDAOrganizer.registerListener(displayId, this);
- final DeviceStateManager deviceStateManager =
- mContext.getSystemService(DeviceStateManager.class);
- deviceStateManager.registerCallback(taskOrganizer.getExecutor(),
- new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged));
- mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
- mOnTransitionAnimationComplete);
- transitions.addHandler(this);
- }
-
- @VisibleForTesting
- StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
- RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
- MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
- Transitions transitions, TransactionPool transactionPool,
- SplitscreenEventLogger logger,
- Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
- mContext = context;
- mDisplayId = displayId;
- mSyncQueue = syncQueue;
- mRootTDAOrganizer = rootTDAOrganizer;
- mTaskOrganizer = taskOrganizer;
- mMainStage = mainStage;
- mSideStage = sideStage;
- mDisplayImeController = displayImeController;
- mDisplayInsetsController = displayInsetsController;
- mRootTDAOrganizer.registerListener(displayId, this);
- mSplitLayout = splitLayout;
- mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
- mOnTransitionAnimationComplete);
- mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
- mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
- mLogger = logger;
- transitions.addHandler(this);
- }
-
- @VisibleForTesting
- SplitScreenTransitions getSplitTransitions() {
- return mSplitTransitions;
- }
-
- boolean isSplitScreenVisible() {
- return mSideStageListener.mVisible && mMainStageListener.mVisible;
- }
-
- boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SplitPosition int sideStagePosition) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- setSideStagePosition(sideStagePosition, wct);
- mMainStage.activate(getMainStageBounds(), wct);
- mSideStage.addTask(task, getSideStageBounds(), wct);
- mSyncQueue.queue(wct);
- mSyncQueue.runInSync(
- t -> updateSurfaceBounds(null /* layout */, t, false /* applyResizingOffset */));
- return true;
- }
-
- boolean removeFromSideStage(int taskId) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
-
- /**
- * {@link MainStage} will be deactivated in {@link #onStageHasChildrenChanged} if the
- * {@link SideStage} no longer has children.
- */
- final boolean result = mSideStage.removeTask(taskId,
- mMainStage.isActive() ? mMainStage.mRootTaskInfo.token : null,
- wct);
- mTaskOrganizer.applyTransaction(wct);
- return result;
- }
-
- void setSideStageOutline(boolean enable) {
- mSideStage.enableOutline(enable);
- }
-
- /** Starts 2 tasks in one transition. */
- void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
- @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- @Nullable RemoteTransition remoteTransition) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- mainOptions = mainOptions != null ? mainOptions : new Bundle();
- sideOptions = sideOptions != null ? sideOptions : new Bundle();
- setSideStagePosition(sidePosition, wct);
-
- // Build a request WCT that will launch both apps such that task 0 is on the main stage
- // while task 1 is on the side stage.
- mMainStage.activate(getMainStageBounds(), wct);
- mSideStage.setBounds(getSideStageBounds(), wct);
-
- // Make sure the launch options will put tasks in the corresponding split roots
- addActivityOptions(mainOptions, mMainStage);
- addActivityOptions(sideOptions, mSideStage);
-
- // Add task launch requests
- wct.startTask(mainTaskId, mainOptions);
- wct.startTask(sideTaskId, sideOptions);
-
- mSplitTransitions.startEnterTransition(
- TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this);
- }
-
- /** Starts 2 tasks in one legacy transition. */
- void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
- int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- RemoteAnimationAdapter adapter) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- // Need to add another wrapper here in shell so that we can inject the divider bar
- // and also manage the process elevation via setRunningRemote
- IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
- @Override
- public void onAnimationStart(@WindowManager.TransitionOldType int transit,
- RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers,
- RemoteAnimationTarget[] nonApps,
- final IRemoteAnimationFinishedCallback finishedCallback) {
- RemoteAnimationTarget[] augmentedNonApps =
- new RemoteAnimationTarget[nonApps.length + 1];
- for (int i = 0; i < nonApps.length; ++i) {
- augmentedNonApps[i] = nonApps[i];
- }
- augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
- try {
- ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
- adapter.getCallingApplication());
- adapter.getRunner().onAnimationStart(transit, apps, wallpapers, nonApps,
- finishedCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error starting remote animation", e);
- }
- }
-
- @Override
- public void onAnimationCancelled(boolean isKeyguardOccluded) {
- try {
- adapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error starting remote animation", e);
- }
- }
- };
- RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
- wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
-
- if (mainOptions == null) {
- mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
- } else {
- ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
- mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
- }
-
- sideOptions = sideOptions != null ? sideOptions : new Bundle();
- setSideStagePosition(sidePosition, wct);
-
- // Build a request WCT that will launch both apps such that task 0 is on the main stage
- // while task 1 is on the side stage.
- mMainStage.activate(getMainStageBounds(), wct);
- mSideStage.setBounds(getSideStageBounds(), wct);
-
- // Make sure the launch options will put tasks in the corresponding split roots
- addActivityOptions(mainOptions, mMainStage);
- addActivityOptions(sideOptions, mSideStage);
-
- // Add task launch requests
- wct.startTask(mainTaskId, mainOptions);
- wct.startTask(sideTaskId, sideOptions);
-
- // Using legacy transitions, so we can't use blast sync since it conflicts.
- mTaskOrganizer.applyTransaction(wct);
- }
-
- public void startIntent(PendingIntent intent, Intent fillInIntent,
- @SplitScreen.StageType int stage, @SplitPosition int position,
- @androidx.annotation.Nullable Bundle options,
- @Nullable RemoteTransition remoteTransition) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- options = resolveStartStage(stage, position, options, wct);
- wct.sendPendingIntent(intent, fillInIntent, options);
- mSplitTransitions.startEnterTransition(
- TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, remoteTransition, this);
- }
-
- Bundle resolveStartStage(@SplitScreen.StageType int stage,
- @SplitPosition int position, @androidx.annotation.Nullable Bundle options,
- @androidx.annotation.Nullable WindowContainerTransaction wct) {
- switch (stage) {
- case STAGE_TYPE_UNDEFINED: {
- // Use the stage of the specified position is valid.
- if (position != SPLIT_POSITION_UNDEFINED) {
- if (position == getSideStagePosition()) {
- options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct);
- } else {
- options = resolveStartStage(STAGE_TYPE_MAIN, position, options, wct);
- }
- } else {
- // Exit split-screen and launch fullscreen since stage wasn't specified.
- prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
- }
- break;
- }
- case STAGE_TYPE_SIDE: {
- if (position != SPLIT_POSITION_UNDEFINED) {
- setSideStagePosition(position, wct);
- } else {
- position = getSideStagePosition();
- }
- if (options == null) {
- options = new Bundle();
- }
- updateActivityOptions(options, position);
- break;
- }
- case STAGE_TYPE_MAIN: {
- if (position != SPLIT_POSITION_UNDEFINED) {
- // Set the side stage opposite of what we want to the main stage.
- final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT
- ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
- setSideStagePosition(sideStagePosition, wct);
- } else {
- position = getMainStagePosition();
- }
- if (options == null) {
- options = new Bundle();
- }
- updateActivityOptions(options, position);
- break;
- }
- default:
- throw new IllegalArgumentException("Unknown stage=" + stage);
- }
-
- return options;
- }
-
- @SplitPosition
- int getSideStagePosition() {
- return mSideStagePosition;
- }
-
- @SplitPosition
- int getMainStagePosition() {
- return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
- ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
- }
-
- void setSideStagePosition(@SplitPosition int sideStagePosition,
- @Nullable WindowContainerTransaction wct) {
- setSideStagePosition(sideStagePosition, true /* updateBounds */, wct);
- }
-
- private void setSideStagePosition(@SplitPosition int sideStagePosition, boolean updateBounds,
- @Nullable WindowContainerTransaction wct) {
- if (mSideStagePosition == sideStagePosition) return;
- mSideStagePosition = sideStagePosition;
- sendOnStagePositionChanged();
-
- if (mSideStageListener.mVisible && updateBounds) {
- if (wct == null) {
- // onLayoutSizeChanged builds/applies a wct with the contents of updateWindowBounds.
- onLayoutSizeChanged(mSplitLayout);
- } else {
- updateWindowBounds(mSplitLayout, wct);
- updateUnfoldBounds();
- }
- }
- }
-
- void setSideStageVisibility(boolean visible) {
- if (mSideStageListener.mVisible == visible) return;
-
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- mSideStage.setVisibility(visible, wct);
- mTaskOrganizer.applyTransaction(wct);
- }
-
- void onKeyguardOccludedChanged(boolean occluded) {
- // Do not exit split directly, because it needs to wait for task info update to determine
- // which task should remain on top after split dismissed.
- mKeyguardOccluded = occluded;
- }
-
- void onKeyguardVisibilityChanged(boolean showing) {
- if (!showing && mMainStage.isActive()
- && mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) {
- exitSplitScreen(mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage,
- SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED);
- }
- }
-
- void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
- mExitSplitScreenOnHide = exitSplitScreenOnHide;
- }
-
- void exitSplitScreen(int toTopTaskId, int exitReason) {
- StageTaskListener childrenToTop = null;
- if (mMainStage.containsTask(toTopTaskId)) {
- childrenToTop = mMainStage;
- } else if (mSideStage.containsTask(toTopTaskId)) {
- childrenToTop = mSideStage;
- }
-
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- if (childrenToTop != null) {
- childrenToTop.reorderChild(toTopTaskId, true /* onTop */, wct);
- }
- applyExitSplitScreen(childrenToTop, wct, exitReason);
- }
-
- private void exitSplitScreen(StageTaskListener childrenToTop, int exitReason) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- applyExitSplitScreen(childrenToTop, wct, exitReason);
- }
-
- private void applyExitSplitScreen(
- StageTaskListener childrenToTop,
- WindowContainerTransaction wct, int exitReason) {
- mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
- mMainStage.deactivate(wct, childrenToTop == mMainStage);
- mTaskOrganizer.applyTransaction(wct);
- mSyncQueue.runInSync(t -> t
- .setWindowCrop(mMainStage.mRootLeash, null)
- .setWindowCrop(mSideStage.mRootLeash, null));
- // Hide divider and reset its position.
- setDividerVisibility(false);
- mSplitLayout.resetDividerPosition();
- mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
- if (childrenToTop != null) {
- logExitToStage(exitReason, childrenToTop == mMainStage);
- } else {
- logExit(exitReason);
- }
- }
-
- /**
- * Unlike exitSplitScreen, this takes a stagetype vs an actual stage-reference and populates
- * an existing WindowContainerTransaction (rather than applying immediately). This is intended
- * to be used when exiting split might be bundled with other window operations.
- */
- void prepareExitSplitScreen(@SplitScreen.StageType int stageToTop,
- @NonNull WindowContainerTransaction wct) {
- mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
- mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
- }
-
- void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
- outTopOrLeftBounds.set(mSplitLayout.getBounds1());
- outBottomOrRightBounds.set(mSplitLayout.getBounds2());
- }
-
- private void addActivityOptions(Bundle opts, StageTaskListener stage) {
- opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
- }
-
- void updateActivityOptions(Bundle opts, @SplitPosition int position) {
- addActivityOptions(opts, position == mSideStagePosition ? mSideStage : mMainStage);
- }
-
- void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
- if (mListeners.contains(listener)) return;
- mListeners.add(listener);
- sendStatusToListener(listener);
- }
-
- void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) {
- mListeners.remove(listener);
- }
-
- void sendStatusToListener(SplitScreen.SplitScreenListener listener) {
- listener.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition());
- listener.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition());
- listener.onSplitVisibilityChanged(isSplitScreenVisible());
- mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE);
- mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN);
- }
-
- private void sendOnStagePositionChanged() {
- for (int i = mListeners.size() - 1; i >= 0; --i) {
- final SplitScreen.SplitScreenListener l = mListeners.get(i);
- l.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition());
- l.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition());
- }
- }
-
- private void onStageChildTaskStatusChanged(StageListenerImpl stageListener, int taskId,
- boolean present, boolean visible) {
- int stage;
- if (present) {
- stage = stageListener == mSideStageListener ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
- } else {
- // No longer on any stage
- stage = STAGE_TYPE_UNDEFINED;
- }
- if (stage == STAGE_TYPE_MAIN) {
- mLogger.logMainStageAppChange(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- mSplitLayout.isLandscape());
- } else {
- mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLandscape());
- }
-
- for (int i = mListeners.size() - 1; i >= 0; --i) {
- mListeners.get(i).onTaskStageChanged(taskId, stage, visible);
- }
- }
-
- private void sendSplitVisibilityChanged() {
- for (int i = mListeners.size() - 1; i >= 0; --i) {
- final SplitScreen.SplitScreenListener l = mListeners.get(i);
- l.onSplitVisibilityChanged(mDividerVisible);
- }
-
- if (mMainUnfoldController != null && mSideUnfoldController != null) {
- mMainUnfoldController.onSplitVisibilityChanged(mDividerVisible);
- mSideUnfoldController.onSplitVisibilityChanged(mDividerVisible);
- }
- }
-
- private void onStageRootTaskAppeared(StageListenerImpl stageListener) {
- if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) {
- mUseLegacySplit = mContext.getResources().getBoolean(R.bool.config_useLegacySplit);
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- // Make the stages adjacent to each other so they occlude what's behind them.
- wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token,
- true /* moveTogether */);
-
- // Only sets side stage as launch-adjacent-flag-root when the device is not using legacy
- // split to prevent new split behavior confusing users.
- if (!mUseLegacySplit) {
- wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
- }
-
- mTaskOrganizer.applyTransaction(wct);
- }
- }
-
- private void onStageRootTaskVanished(StageListenerImpl stageListener) {
- if (stageListener == mMainStageListener || stageListener == mSideStageListener) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- // Deactivate the main stage if it no longer has a root task.
- mMainStage.deactivate(wct);
-
- if (!mUseLegacySplit) {
- wct.clearLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
- }
-
- mTaskOrganizer.applyTransaction(wct);
- }
- }
-
- private void setDividerVisibility(boolean visible) {
- if (mDividerVisible == visible) return;
- mDividerVisible = visible;
- if (visible) {
- mSplitLayout.init();
- updateUnfoldBounds();
- } else {
- mSplitLayout.release();
- }
- sendSplitVisibilityChanged();
- }
-
- private void onStageVisibilityChanged(StageListenerImpl stageListener) {
- final boolean sideStageVisible = mSideStageListener.mVisible;
- final boolean mainStageVisible = mMainStageListener.mVisible;
- final boolean bothStageVisible = sideStageVisible && mainStageVisible;
- final boolean bothStageInvisible = !sideStageVisible && !mainStageVisible;
- final boolean sameVisibility = sideStageVisible == mainStageVisible;
- // Only add or remove divider when both visible or both invisible to avoid sometimes we only
- // got one stage visibility changed for a moment and it will cause flicker.
- if (sameVisibility) {
- setDividerVisibility(bothStageVisible);
- }
-
- if (bothStageInvisible) {
- if (mExitSplitScreenOnHide
- // Don't dismiss staged split when both stages are not visible due to sleeping display,
- // like the cases keyguard showing or screen off.
- || (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping)) {
- exitSplitScreen(null /* childrenToTop */,
- SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
- }
- } else if (mKeyguardOccluded) {
- // At least one of the stages is visible while keyguard occluded. Dismiss split because
- // there's show-when-locked activity showing on top of keyguard. Also make sure the
- // task contains show-when-locked activity remains on top after split dismissed.
- final StageTaskListener toTop =
- mainStageVisible ? mMainStage : (sideStageVisible ? mSideStage : null);
- exitSplitScreen(toTop, SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP);
- }
-
- mSyncQueue.runInSync(t -> {
- // Same above, we only set root tasks and divider leash visibility when both stage
- // change to visible or invisible to avoid flicker.
- if (sameVisibility) {
- t.setVisibility(mSideStage.mRootLeash, bothStageVisible)
- .setVisibility(mMainStage.mRootLeash, bothStageVisible);
- applyDividerVisibility(t);
- applyOutlineVisibility(t);
- }
- });
- }
-
- private void applyDividerVisibility(SurfaceControl.Transaction t) {
- final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
- if (dividerLeash == null) {
- return;
- }
-
- if (mDividerVisible) {
- t.show(dividerLeash)
- .setLayer(dividerLeash, Integer.MAX_VALUE)
- .setPosition(dividerLeash,
- mSplitLayout.getDividerBounds().left,
- mSplitLayout.getDividerBounds().top);
- } else {
- t.hide(dividerLeash);
- }
- }
-
- private void applyOutlineVisibility(SurfaceControl.Transaction t) {
- final SurfaceControl outlineLeash = mSideStage.getOutlineLeash();
- if (outlineLeash == null) {
- return;
- }
-
- if (mDividerVisible) {
- t.show(outlineLeash).setLayer(outlineLeash, Integer.MAX_VALUE);
- } else {
- t.hide(outlineLeash);
- }
- }
-
- private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
- final boolean hasChildren = stageListener.mHasChildren;
- final boolean isSideStage = stageListener == mSideStageListener;
- if (!hasChildren) {
- if (isSideStage && mMainStageListener.mVisible) {
- // Exit to main stage if side stage no longer has children.
- exitSplitScreen(mMainStage, SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED);
- } else if (!isSideStage && mSideStageListener.mVisible) {
- // Exit to side stage if main stage no longer has children.
- exitSplitScreen(mSideStage, SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED);
- }
- } else if (isSideStage) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- // Make sure the main stage is active.
- mMainStage.activate(getMainStageBounds(), wct);
- mSideStage.setBounds(getSideStageBounds(), wct);
- mTaskOrganizer.applyTransaction(wct);
- }
- if (!mLogger.hasStartedSession() && mMainStageListener.mHasChildren
- && mSideStageListener.mHasChildren) {
- mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
- getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLandscape());
- }
- }
-
- @VisibleForTesting
- IBinder onSnappedToDismissTransition(boolean mainStageToTop) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- prepareExitSplitScreen(mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE, wct);
- return mSplitTransitions.startSnapToDismiss(wct, this);
- }
-
- @Override
- public void onSnappedToDismiss(boolean bottomOrRight) {
- final boolean mainStageToTop =
- bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
- : mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
- if (ENABLE_SHELL_TRANSITIONS) {
- onSnappedToDismissTransition(mainStageToTop);
- return;
- }
- exitSplitScreen(mainStageToTop ? mMainStage : mSideStage,
- SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER);
- }
-
- @Override
- public void onDoubleTappedDivider() {
- setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
- ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
- mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLandscape());
- }
-
- @Override
- public void onLayoutPositionChanging(SplitLayout layout) {
- mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */));
- }
-
- @Override
- public void onLayoutSizeChanging(SplitLayout layout) {
- mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */));
- mSideStage.setOutlineVisibility(false);
- }
-
- @Override
- public void onLayoutSizeChanged(SplitLayout layout) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- updateWindowBounds(layout, wct);
- updateUnfoldBounds();
- mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, false /* applyResizingOffset */));
- mSideStage.setOutlineVisibility(true);
- mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
- }
-
- private void updateUnfoldBounds() {
- if (mMainUnfoldController != null && mSideUnfoldController != null) {
- mMainUnfoldController.onLayoutChanged(getMainStageBounds());
- mSideUnfoldController.onLayoutChanged(getSideStageBounds());
- }
- }
-
- /**
- * Populates `wct` with operations that match the split windows to the current layout.
- * To match relevant surfaces, make sure to call updateSurfaceBounds after `wct` is applied
- */
- private void updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) {
- final StageTaskListener topLeftStage =
- mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
- final StageTaskListener bottomRightStage =
- mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
- layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
- }
-
- void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t,
- boolean applyResizingOffset) {
- final StageTaskListener topLeftStage =
- mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
- final StageTaskListener bottomRightStage =
- mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
- (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
- bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer,
- applyResizingOffset);
- }
-
- @Override
- public int getSplitItemPosition(WindowContainerToken token) {
- if (token == null) {
- return SPLIT_POSITION_UNDEFINED;
- }
-
- if (token.equals(mMainStage.mRootTaskInfo.getToken())) {
- return getMainStagePosition();
- } else if (token.equals(mSideStage.mRootTaskInfo.getToken())) {
- return getSideStagePosition();
- }
-
- return SPLIT_POSITION_UNDEFINED;
- }
-
- @Override
- public void setLayoutOffsetTarget(int offsetX, int offsetY, SplitLayout layout) {
- final StageTaskListener topLeftStage =
- mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
- final StageTaskListener bottomRightStage =
- mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- layout.applyLayoutOffsetTarget(wct, offsetX, offsetY, topLeftStage.mRootTaskInfo,
- bottomRightStage.mRootTaskInfo);
- mTaskOrganizer.applyTransaction(wct);
- }
-
- @Override
- public void onDisplayAreaAppeared(DisplayAreaInfo displayAreaInfo) {
- mDisplayAreaInfo = displayAreaInfo;
- if (mSplitLayout == null) {
- mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
- mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
- mDisplayImeController, mTaskOrganizer, SplitLayout.PARALLAX_DISMISSING);
- mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
-
- if (mMainUnfoldController != null && mSideUnfoldController != null) {
- mMainUnfoldController.init();
- mSideUnfoldController.init();
- }
- }
- }
-
- @Override
- public void onDisplayAreaVanished(DisplayAreaInfo displayAreaInfo) {
- throw new IllegalStateException("Well that was unexpected...");
- }
-
- @Override
- public void onDisplayAreaInfoChanged(DisplayAreaInfo displayAreaInfo) {
- mDisplayAreaInfo = displayAreaInfo;
- if (mSplitLayout != null
- && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)
- && mMainStage.isActive()) {
- onLayoutSizeChanged(mSplitLayout);
- }
- }
-
- private void onFoldedStateChanged(boolean folded) {
- mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
- if (!folded) return;
-
- if (mMainStage.isFocused()) {
- mTopStageAfterFoldDismiss = STAGE_TYPE_MAIN;
- } else if (mSideStage.isFocused()) {
- mTopStageAfterFoldDismiss = STAGE_TYPE_SIDE;
- }
- }
-
- private Rect getSideStageBounds() {
- return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
- ? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
- }
-
- private Rect getMainStageBounds() {
- return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
- ? mSplitLayout.getBounds2() : mSplitLayout.getBounds1();
- }
-
- /**
- * Get the stage that should contain this `taskInfo`. The stage doesn't necessarily contain
- * this task (yet) so this can also be used to identify which stage to put a task into.
- */
- private StageTaskListener getStageOfTask(ActivityManager.RunningTaskInfo taskInfo) {
- // TODO(b/184679596): Find a way to either include task-org information in the transition,
- // or synchronize task-org callbacks so we can use stage.containsTask
- if (mMainStage.mRootTaskInfo != null
- && taskInfo.parentTaskId == mMainStage.mRootTaskInfo.taskId) {
- return mMainStage;
- } else if (mSideStage.mRootTaskInfo != null
- && taskInfo.parentTaskId == mSideStage.mRootTaskInfo.taskId) {
- return mSideStage;
- }
- return null;
- }
-
- @SplitScreen.StageType
- private int getStageType(StageTaskListener stage) {
- return stage == mMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
- }
-
- @Override
- public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
- @Nullable TransitionRequestInfo request) {
- final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
- if (triggerTask == null) {
- // still want to monitor everything while in split-screen, so return non-null.
- return isSplitScreenVisible() ? new WindowContainerTransaction() : null;
- }
-
- WindowContainerTransaction out = null;
- final @WindowManager.TransitionType int type = request.getType();
- if (isSplitScreenVisible()) {
- // try to handle everything while in split-screen, so return a WCT even if it's empty.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
- + "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
- + " sideChildren=%d", triggerTask.taskId, transitTypeToString(type),
- mMainStage.getChildCount(), mSideStage.getChildCount());
- out = new WindowContainerTransaction();
- final StageTaskListener stage = getStageOfTask(triggerTask);
- if (stage != null) {
- // dismiss split if the last task in one of the stages is going away
- if (isClosingType(type) && stage.getChildCount() == 1) {
- // The top should be the opposite side that is closing:
- mDismissTop = getStageType(stage) == STAGE_TYPE_MAIN
- ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
- }
- } else {
- if (triggerTask.getActivityType() == ACTIVITY_TYPE_HOME && isOpeningType(type)) {
- // Going home so dismiss both.
- mDismissTop = STAGE_TYPE_UNDEFINED;
- }
- }
- if (mDismissTop != NO_DISMISS) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
- + " deduced Dismiss from request. toTop=%s",
- stageTypeToString(mDismissTop));
- prepareExitSplitScreen(mDismissTop, out);
- mSplitTransitions.mPendingDismiss = transition;
- }
- } else {
- // Not in split mode, so look for an open into a split stage just so we can whine and
- // complain about how this isn't a supported operation.
- if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)) {
- if (getStageOfTask(triggerTask) != null) {
- throw new IllegalStateException("Entering split implicitly with only one task"
- + " isn't supported.");
- }
- }
- }
- return out;
- }
-
- @Override
- public boolean startAnimation(@NonNull IBinder transition,
- @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (transition != mSplitTransitions.mPendingDismiss
- && transition != mSplitTransitions.mPendingEnter) {
- // Not entering or exiting, so just do some house-keeping and validation.
-
- // If we're not in split-mode, just abort so something else can handle it.
- if (!isSplitScreenVisible()) return false;
-
- for (int iC = 0; iC < info.getChanges().size(); ++iC) {
- final TransitionInfo.Change change = info.getChanges().get(iC);
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (taskInfo == null || !taskInfo.hasParentTask()) continue;
- final StageTaskListener stage = getStageOfTask(taskInfo);
- if (stage == null) continue;
- if (isOpeningType(change.getMode())) {
- if (!stage.containsTask(taskInfo.taskId)) {
- Log.w(TAG, "Expected onTaskAppeared on " + stage + " to have been called"
- + " with " + taskInfo.taskId + " before startAnimation().");
- }
- } else if (isClosingType(change.getMode())) {
- if (stage.containsTask(taskInfo.taskId)) {
- Log.w(TAG, "Expected onTaskVanished on " + stage + " to have been called"
- + " with " + taskInfo.taskId + " before startAnimation().");
- }
- }
- }
- if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
- // TODO(shell-transitions): Implement a fallback behavior for now.
- throw new IllegalStateException("Somehow removed the last task in a stage"
- + " outside of a proper transition");
- // This can happen in some pathological cases. For example:
- // 1. main has 2 tasks [Task A (Single-task), Task B], side has one task [Task C]
- // 2. Task B closes itself and starts Task A in LAUNCH_ADJACENT at the same time
- // In this case, the result *should* be that we leave split.
- // TODO(b/184679596): Find a way to either include task-org information in
- // the transition, or synchronize task-org callbacks.
- }
-
- // Use normal animations.
- return false;
- }
-
- boolean shouldAnimate = true;
- if (mSplitTransitions.mPendingEnter == transition) {
- shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
- } else if (mSplitTransitions.mPendingDismiss == transition) {
- shouldAnimate = startPendingDismissAnimation(transition, info, startTransaction);
- }
- if (!shouldAnimate) return false;
-
- mSplitTransitions.playAnimation(transition, info, startTransaction, finishTransaction,
- finishCallback, mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
- return true;
- }
-
- private boolean startPendingEnterAnimation(@NonNull IBinder transition,
- @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
- if (info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN) {
- // First, verify that we actually have opened 2 apps in split.
- TransitionInfo.Change mainChild = null;
- TransitionInfo.Change sideChild = null;
- for (int iC = 0; iC < info.getChanges().size(); ++iC) {
- final TransitionInfo.Change change = info.getChanges().get(iC);
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (taskInfo == null || !taskInfo.hasParentTask()) continue;
- final @SplitScreen.StageType int stageType = getStageType(getStageOfTask(taskInfo));
- if (stageType == STAGE_TYPE_MAIN) {
- mainChild = change;
- } else if (stageType == STAGE_TYPE_SIDE) {
- sideChild = change;
- }
- }
- if (mainChild == null || sideChild == null) {
- throw new IllegalStateException("Launched 2 tasks in split, but didn't receive"
- + " 2 tasks in transition. Possibly one of them failed to launch");
- // TODO: fallback logic. Probably start a new transition to exit split before
- // applying anything here. Ideally consolidate with transition-merging.
- }
-
- // Update local states (before animating).
- setDividerVisibility(true);
- setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */,
- null /* wct */);
- setSplitsVisible(true);
-
- addDividerBarToTransition(info, t, true /* show */);
-
- // Make some noise if things aren't totally expected. These states shouldn't effect
- // transitions locally, but remotes (like Launcher) may get confused if they were
- // depending on listener callbacks. This can happen because task-organizer callbacks
- // aren't serialized with transition callbacks.
- // TODO(b/184679596): Find a way to either include task-org information in
- // the transition, or synchronize task-org callbacks.
- if (!mMainStage.containsTask(mainChild.getTaskInfo().taskId)) {
- Log.w(TAG, "Expected onTaskAppeared on " + mMainStage
- + " to have been called with " + mainChild.getTaskInfo().taskId
- + " before startAnimation().");
- }
- if (!mSideStage.containsTask(sideChild.getTaskInfo().taskId)) {
- Log.w(TAG, "Expected onTaskAppeared on " + mSideStage
- + " to have been called with " + sideChild.getTaskInfo().taskId
- + " before startAnimation().");
- }
- return true;
- } else {
- // TODO: other entry method animations
- throw new RuntimeException("Unsupported split-entry");
- }
- }
-
- private boolean startPendingDismissAnimation(@NonNull IBinder transition,
- @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
- // Make some noise if things aren't totally expected. These states shouldn't effect
- // transitions locally, but remotes (like Launcher) may get confused if they were
- // depending on listener callbacks. This can happen because task-organizer callbacks
- // aren't serialized with transition callbacks.
- // TODO(b/184679596): Find a way to either include task-org information in
- // the transition, or synchronize task-org callbacks.
- if (mMainStage.getChildCount() != 0) {
- final StringBuilder tasksLeft = new StringBuilder();
- for (int i = 0; i < mMainStage.getChildCount(); ++i) {
- tasksLeft.append(i != 0 ? ", " : "");
- tasksLeft.append(mMainStage.mChildrenTaskInfo.keyAt(i));
- }
- Log.w(TAG, "Expected onTaskVanished on " + mMainStage
- + " to have been called with [" + tasksLeft.toString()
- + "] before startAnimation().");
- }
- if (mSideStage.getChildCount() != 0) {
- final StringBuilder tasksLeft = new StringBuilder();
- for (int i = 0; i < mSideStage.getChildCount(); ++i) {
- tasksLeft.append(i != 0 ? ", " : "");
- tasksLeft.append(mSideStage.mChildrenTaskInfo.keyAt(i));
- }
- Log.w(TAG, "Expected onTaskVanished on " + mSideStage
- + " to have been called with [" + tasksLeft.toString()
- + "] before startAnimation().");
- }
-
- // Update local states.
- setSplitsVisible(false);
- // Wait until after animation to update divider
-
- if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
- // Reset crops so they don't interfere with subsequent launches
- t.setWindowCrop(mMainStage.mRootLeash, null);
- t.setWindowCrop(mSideStage.mRootLeash, null);
- }
-
- if (mDismissTop == STAGE_TYPE_UNDEFINED) {
- // Going home (dismissing both splits)
-
- // TODO: Have a proper remote for this. Until then, though, reset state and use the
- // normal animation stuff (which falls back to the normal launcher remote).
- t.hide(mSplitLayout.getDividerLeash());
- setDividerVisibility(false);
- mSplitTransitions.mPendingDismiss = null;
- return false;
- }
-
- addDividerBarToTransition(info, t, false /* show */);
- // We're dismissing split by moving the other one to fullscreen.
- // Since we don't have any animations for this yet, just use the internal example
- // animations.
- return true;
- }
-
- private void addDividerBarToTransition(@NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t, boolean show) {
- final SurfaceControl leash = mSplitLayout.getDividerLeash();
- final TransitionInfo.Change barChange = new TransitionInfo.Change(null /* token */, leash);
- final Rect bounds = mSplitLayout.getDividerBounds();
- barChange.setStartAbsBounds(bounds);
- barChange.setEndAbsBounds(bounds);
- barChange.setMode(show ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK);
- barChange.setFlags(FLAG_IS_DIVIDER_BAR);
- // Technically this should be order-0, but this is running after layer assignment
- // and it's a special case, so just add to end.
- info.addChange(barChange);
- // Be default, make it visible. The remote animator can adjust alpha if it plans to animate.
- if (show) {
- t.setAlpha(leash, 1.f);
- t.setLayer(leash, Integer.MAX_VALUE);
- t.setPosition(leash, bounds.left, bounds.top);
- t.show(leash);
- }
- }
-
- RemoteAnimationTarget getDividerBarLegacyTarget() {
- final Rect bounds = mSplitLayout.getDividerBounds();
- return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */,
- mSplitLayout.getDividerLeash(), false /* isTranslucent */, null /* clipRect */,
- null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */,
- new android.graphics.Point(0, 0) /* position */, bounds, bounds,
- new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */,
- null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
- }
-
- RemoteAnimationTarget getOutlineLegacyTarget() {
- final Rect bounds = mSideStage.mRootTaskInfo.configuration.windowConfiguration.getBounds();
- // Leverage TYPE_DOCK_DIVIDER type when wrapping outline remote animation target in order to
- // distinguish as a split auxiliary target in Launcher.
- return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */,
- mSideStage.getOutlineLeash(), false /* isTranslucent */, null /* clipRect */,
- null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */,
- new android.graphics.Point(0, 0) /* position */, bounds, bounds,
- new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */,
- null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw, String prefix) {
- final String innerPrefix = prefix + " ";
- final String childPrefix = innerPrefix + " ";
- pw.println(prefix + TAG + " mDisplayId=" + mDisplayId);
- pw.println(innerPrefix + "mDividerVisible=" + mDividerVisible);
- pw.println(innerPrefix + "MainStage");
- pw.println(childPrefix + "isActive=" + mMainStage.isActive());
- mMainStageListener.dump(pw, childPrefix);
- pw.println(innerPrefix + "SideStage");
- mSideStageListener.dump(pw, childPrefix);
- pw.println(innerPrefix + "mSplitLayout=" + mSplitLayout);
- }
-
- /**
- * Directly set the visibility of both splits. This assumes hasChildren matches visibility.
- * This is intended for batch use, so it assumes other state management logic is already
- * handled.
- */
- private void setSplitsVisible(boolean visible) {
- mMainStageListener.mVisible = mSideStageListener.mVisible = visible;
- mMainStageListener.mHasChildren = mSideStageListener.mHasChildren = visible;
- }
-
- /**
- * Sets drag info to be logged when splitscreen is next entered.
- */
- public void logOnDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) {
- mLogger.enterRequestedByDrag(position, dragSessionId);
- }
-
- /**
- * Logs the exit of splitscreen.
- */
- private void logExit(int exitReason) {
- mLogger.logExit(exitReason,
- SPLIT_POSITION_UNDEFINED, 0 /* mainStageUid */,
- SPLIT_POSITION_UNDEFINED, 0 /* sideStageUid */,
- mSplitLayout.isLandscape());
- }
-
- /**
- * Logs the exit of splitscreen to a specific stage. This must be called before the exit is
- * executed.
- */
- private void logExitToStage(int exitReason, boolean toMainStage) {
- mLogger.logExit(exitReason,
- toMainStage ? getMainStagePosition() : SPLIT_POSITION_UNDEFINED,
- toMainStage ? mMainStage.getTopChildTaskUid() : 0 /* mainStageUid */,
- !toMainStage ? getSideStagePosition() : SPLIT_POSITION_UNDEFINED,
- !toMainStage ? mSideStage.getTopChildTaskUid() : 0 /* sideStageUid */,
- mSplitLayout.isLandscape());
- }
-
- class StageListenerImpl implements StageTaskListener.StageListenerCallbacks {
- boolean mHasRootTask = false;
- boolean mVisible = false;
- boolean mHasChildren = false;
-
- @Override
- public void onRootTaskAppeared() {
- mHasRootTask = true;
- StageCoordinator.this.onStageRootTaskAppeared(this);
- }
-
- @Override
- public void onStatusChanged(boolean visible, boolean hasChildren) {
- if (!mHasRootTask) return;
-
- if (mHasChildren != hasChildren) {
- mHasChildren = hasChildren;
- StageCoordinator.this.onStageHasChildrenChanged(this);
- }
- if (mVisible != visible) {
- mVisible = visible;
- StageCoordinator.this.onStageVisibilityChanged(this);
- }
- }
-
- @Override
- public void onChildTaskStatusChanged(int taskId, boolean present, boolean visible) {
- StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present, visible);
- }
-
- @Override
- public void onRootTaskVanished() {
- reset();
- StageCoordinator.this.onStageRootTaskVanished(this);
- }
-
- @Override
- public void onNoLongerSupportMultiWindow() {
- if (mMainStage.isActive()) {
- StageCoordinator.this.exitSplitScreen(null /* childrenToTop */,
- SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW);
- }
- }
-
- private void reset() {
- mHasRootTask = false;
- mVisible = false;
- mHasChildren = false;
- }
-
- public void dump(@NonNull PrintWriter pw, String prefix) {
- pw.println(prefix + "mHasRootTask=" + mHasRootTask);
- pw.println(prefix + "mVisible=" + mVisible);
- pw.println(prefix + "mHasChildren=" + mHasChildren);
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java
deleted file mode 100644
index 7b679580fa87..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.stagesplit;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
-
-import android.annotation.CallSuper;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.SparseArray;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.window.WindowContainerTransaction;
-
-import androidx.annotation.NonNull;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.SurfaceUtils;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import java.io.PrintWriter;
-
-/**
- * Base class that handle common task org. related for split-screen stages.
- * Note that this class and its sub-class do not directly perform hierarchy operations.
- * They only serve to hold a collection of tasks and provide APIs like
- * {@link #setBounds(Rect, WindowContainerTransaction)} for the centralized {@link StageCoordinator}
- * to perform operations in-sync with other containers.
- *
- * @see StageCoordinator
- */
-class StageTaskListener implements ShellTaskOrganizer.TaskListener {
- private static final String TAG = StageTaskListener.class.getSimpleName();
-
- protected static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD};
- protected static final int[] CONTROLLED_WINDOWING_MODES =
- {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
- protected static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE =
- {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW};
-
- /** Callback interface for listening to changes in a split-screen stage. */
- public interface StageListenerCallbacks {
- void onRootTaskAppeared();
-
- void onStatusChanged(boolean visible, boolean hasChildren);
-
- void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
-
- void onRootTaskVanished();
- void onNoLongerSupportMultiWindow();
- }
-
- private final StageListenerCallbacks mCallbacks;
- private final SurfaceSession mSurfaceSession;
- protected final SyncTransactionQueue mSyncQueue;
-
- protected ActivityManager.RunningTaskInfo mRootTaskInfo;
- protected SurfaceControl mRootLeash;
- protected SurfaceControl mDimLayer;
- protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>();
- private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>();
-
- private final StageTaskUnfoldController mStageTaskUnfoldController;
-
- StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId,
- StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession,
- @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
- mCallbacks = callbacks;
- mSyncQueue = syncQueue;
- mSurfaceSession = surfaceSession;
- mStageTaskUnfoldController = stageTaskUnfoldController;
- taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
- }
-
- int getChildCount() {
- return mChildrenTaskInfo.size();
- }
-
- boolean containsTask(int taskId) {
- return mChildrenTaskInfo.contains(taskId);
- }
-
- /**
- * Returns the top activity uid for the top child task.
- */
- int getTopChildTaskUid() {
- for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
- final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i);
- if (info.topActivityInfo == null) {
- continue;
- }
- return info.topActivityInfo.applicationInfo.uid;
- }
- return 0;
- }
-
- /** @return {@code true} if this listener contains the currently focused task. */
- boolean isFocused() {
- if (mRootTaskInfo == null) {
- return false;
- }
-
- if (mRootTaskInfo.isFocused) {
- return true;
- }
-
- for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
- if (mChildrenTaskInfo.valueAt(i).isFocused) {
- return true;
- }
- }
-
- return false;
- }
-
- @Override
- @CallSuper
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- if (mRootTaskInfo == null && !taskInfo.hasParentTask()) {
- mRootLeash = leash;
- mRootTaskInfo = taskInfo;
- mCallbacks.onRootTaskAppeared();
- sendStatusChanged();
- mSyncQueue.runInSync(t -> {
- t.hide(mRootLeash);
- mDimLayer =
- SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession);
- });
- } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
- final int taskId = taskInfo.taskId;
- mChildrenLeashes.put(taskId, leash);
- mChildrenTaskInfo.put(taskId, taskInfo);
- updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
- mCallbacks.onChildTaskStatusChanged(taskId, true /* present */, taskInfo.isVisible);
- if (ENABLE_SHELL_TRANSITIONS) {
- // Status is managed/synchronized by the transition lifecycle.
- return;
- }
- sendStatusChanged();
- } else {
- throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
- + "\n mRootTaskInfo: " + mRootTaskInfo);
- }
-
- if (mStageTaskUnfoldController != null) {
- mStageTaskUnfoldController.onTaskAppeared(taskInfo, leash);
- }
- }
-
- @Override
- @CallSuper
- public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- if (!taskInfo.supportsMultiWindow) {
- // Leave split screen if the task no longer supports multi window.
- mCallbacks.onNoLongerSupportMultiWindow();
- return;
- }
- if (mRootTaskInfo.taskId == taskInfo.taskId) {
- mRootTaskInfo = taskInfo;
- } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
- mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
- mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */,
- taskInfo.isVisible);
- if (!ENABLE_SHELL_TRANSITIONS) {
- updateChildTaskSurface(
- taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */);
- }
- } else {
- throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
- + "\n mRootTaskInfo: " + mRootTaskInfo);
- }
- if (ENABLE_SHELL_TRANSITIONS) {
- // Status is managed/synchronized by the transition lifecycle.
- return;
- }
- sendStatusChanged();
- }
-
- @Override
- @CallSuper
- public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- final int taskId = taskInfo.taskId;
- if (mRootTaskInfo.taskId == taskId) {
- mCallbacks.onRootTaskVanished();
- mSyncQueue.runInSync(t -> t.remove(mDimLayer));
- mRootTaskInfo = null;
- } else if (mChildrenTaskInfo.contains(taskId)) {
- mChildrenTaskInfo.remove(taskId);
- mChildrenLeashes.remove(taskId);
- mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible);
- if (ENABLE_SHELL_TRANSITIONS) {
- // Status is managed/synchronized by the transition lifecycle.
- return;
- }
- sendStatusChanged();
- } else {
- throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
- + "\n mRootTaskInfo: " + mRootTaskInfo);
- }
-
- if (mStageTaskUnfoldController != null) {
- mStageTaskUnfoldController.onTaskVanished(taskInfo);
- }
- }
-
- @Override
- public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
- b.setParent(findTaskSurface(taskId));
- }
-
- @Override
- public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
- SurfaceControl.Transaction t) {
- t.reparent(sc, findTaskSurface(taskId));
- }
-
- private SurfaceControl findTaskSurface(int taskId) {
- if (mRootTaskInfo.taskId == taskId) {
- return mRootLeash;
- } else if (mChildrenLeashes.contains(taskId)) {
- return mChildrenLeashes.get(taskId);
- } else {
- throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
- }
- }
-
- void setBounds(Rect bounds, WindowContainerTransaction wct) {
- wct.setBounds(mRootTaskInfo.token, bounds);
- }
-
- void reorderChild(int taskId, boolean onTop, WindowContainerTransaction wct) {
- if (!containsTask(taskId)) {
- return;
- }
- wct.reorder(mChildrenTaskInfo.get(taskId).token, onTop /* onTop */);
- }
-
- void setVisibility(boolean visible, WindowContainerTransaction wct) {
- wct.reorder(mRootTaskInfo.token, visible /* onTop */);
- }
-
- void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener,
- @SplitScreen.StageType int stage) {
- for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
- int taskId = mChildrenTaskInfo.keyAt(i);
- listener.onTaskStageChanged(taskId, stage,
- mChildrenTaskInfo.get(taskId).isVisible);
- }
- }
-
- private void updateChildTaskSurface(ActivityManager.RunningTaskInfo taskInfo,
- SurfaceControl leash, boolean firstAppeared) {
- final Point taskPositionInParent = taskInfo.positionInParent;
- mSyncQueue.runInSync(t -> {
- t.setWindowCrop(leash, null);
- t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y);
- if (firstAppeared && !ENABLE_SHELL_TRANSITIONS) {
- t.setAlpha(leash, 1f);
- t.setMatrix(leash, 1, 0, 0, 1);
- t.show(leash);
- }
- });
- }
-
- private void sendStatusChanged() {
- mCallbacks.onStatusChanged(mRootTaskInfo.isVisible, mChildrenTaskInfo.size() > 0);
- }
-
- @Override
- @CallSuper
- public void dump(@NonNull PrintWriter pw, String prefix) {
- final String innerPrefix = prefix + " ";
- final String childPrefix = innerPrefix + " ";
- pw.println(prefix + this);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskUnfoldController.java
deleted file mode 100644
index 62b9da6d4715..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskUnfoldController.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.stagesplit;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import android.animation.RectEvaluator;
-import android.animation.TypeEvaluator;
-import android.annotation.NonNull;
-import android.app.ActivityManager;
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.SparseArray;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-import android.view.SurfaceControl;
-
-import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.wm.shell.common.DisplayInsetsController;
-import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
-import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
-import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
-import com.android.wm.shell.unfold.UnfoldBackgroundController;
-
-import java.util.concurrent.Executor;
-
-/**
- * Controls transformations of the split screen task surfaces in response
- * to the unfolding/folding action on foldable devices
- */
-public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChangedListener {
-
- private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect());
- private static final float CROPPING_START_MARGIN_FRACTION = 0.05f;
-
- private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>();
- private final ShellUnfoldProgressProvider mUnfoldProgressProvider;
- private final DisplayInsetsController mDisplayInsetsController;
- private final UnfoldBackgroundController mBackgroundController;
- private final Executor mExecutor;
- private final int mExpandedTaskBarHeight;
- private final float mWindowCornerRadiusPx;
- private final Rect mStageBounds = new Rect();
- private final TransactionPool mTransactionPool;
-
- private InsetsSource mTaskbarInsetsSource;
- private boolean mBothStagesVisible;
-
- public StageTaskUnfoldController(@NonNull Context context,
- @NonNull TransactionPool transactionPool,
- @NonNull ShellUnfoldProgressProvider unfoldProgressProvider,
- @NonNull DisplayInsetsController displayInsetsController,
- @NonNull UnfoldBackgroundController backgroundController,
- @NonNull Executor executor) {
- mUnfoldProgressProvider = unfoldProgressProvider;
- mTransactionPool = transactionPool;
- mExecutor = executor;
- mBackgroundController = backgroundController;
- mDisplayInsetsController = displayInsetsController;
- mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
- mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.taskbar_frame_height);
- }
-
- /**
- * Initializes the controller, starts listening for the external events
- */
- public void init() {
- mUnfoldProgressProvider.addListener(mExecutor, this);
- mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this);
- }
-
- @Override
- public void insetsChanged(InsetsState insetsState) {
- mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
- for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
- AnimationContext context = mAnimationContextByTaskId.valueAt(i);
- context.update();
- }
- }
-
- /**
- * Called when split screen task appeared
- * @param taskInfo info for the appeared task
- * @param leash surface leash for the appeared task
- */
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- AnimationContext context = new AnimationContext(leash);
- mAnimationContextByTaskId.put(taskInfo.taskId, context);
- }
-
- /**
- * Called when a split screen task vanished
- * @param taskInfo info for the vanished task
- */
- public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId);
- if (context != null) {
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- resetSurface(transaction, context);
- transaction.apply();
- mTransactionPool.release(transaction);
- }
- mAnimationContextByTaskId.remove(taskInfo.taskId);
- }
-
- @Override
- public void onStateChangeProgress(float progress) {
- if (mAnimationContextByTaskId.size() == 0 || !mBothStagesVisible) return;
-
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- mBackgroundController.ensureBackground(transaction);
-
- for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
- AnimationContext context = mAnimationContextByTaskId.valueAt(i);
-
- context.mCurrentCropRect.set(RECT_EVALUATOR
- .evaluate(progress, context.mStartCropRect, context.mEndCropRect));
-
- transaction.setWindowCrop(context.mLeash, context.mCurrentCropRect)
- .setCornerRadius(context.mLeash, mWindowCornerRadiusPx);
- }
-
- transaction.apply();
-
- mTransactionPool.release(transaction);
- }
-
- @Override
- public void onStateChangeFinished() {
- resetTransformations();
- }
-
- /**
- * Called when split screen visibility changes
- * @param bothStagesVisible true if both stages of the split screen are visible
- */
- public void onSplitVisibilityChanged(boolean bothStagesVisible) {
- mBothStagesVisible = bothStagesVisible;
- if (!bothStagesVisible) {
- resetTransformations();
- }
- }
-
- /**
- * Called when split screen stage bounds changed
- * @param bounds new bounds for this stage
- */
- public void onLayoutChanged(Rect bounds) {
- mStageBounds.set(bounds);
-
- for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
- final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
- context.update();
- }
- }
-
- private void resetTransformations() {
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
-
- for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
- final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
- resetSurface(transaction, context);
- }
- mBackgroundController.removeBackground(transaction);
- transaction.apply();
-
- mTransactionPool.release(transaction);
- }
-
- private void resetSurface(SurfaceControl.Transaction transaction, AnimationContext context) {
- transaction
- .setWindowCrop(context.mLeash, null)
- .setCornerRadius(context.mLeash, 0.0F);
- }
-
- private class AnimationContext {
- final SurfaceControl mLeash;
- final Rect mStartCropRect = new Rect();
- final Rect mEndCropRect = new Rect();
- final Rect mCurrentCropRect = new Rect();
-
- private AnimationContext(SurfaceControl leash) {
- this.mLeash = leash;
- update();
- }
-
- private void update() {
- mStartCropRect.set(mStageBounds);
-
- if (mTaskbarInsetsSource != null) {
- // Only insets the cropping window with taskbar when taskbar is expanded
- if (mTaskbarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
- mStartCropRect.inset(mTaskbarInsetsSource
- .calculateVisibleInsets(mStartCropRect));
- }
- }
-
- // Offset to surface coordinates as layout bounds are in screen coordinates
- mStartCropRect.offsetTo(0, 0);
-
- mEndCropRect.set(mStartCropRect);
-
- int maxSize = Math.max(mEndCropRect.width(), mEndCropRect.height());
- int margin = (int) (maxSize * CROPPING_START_MARGIN_FRACTION);
- mStartCropRect.inset(margin, margin, margin, margin);
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 54d62edf2570..a0e176c7ea68 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -261,7 +261,8 @@ public class StartingSurfaceDrawer {
WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
params.setFitInsetsSides(0);
params.setFitInsetsTypes(0);
- params.format = PixelFormat.TRANSLUCENT;
+ params.format = suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
+ ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 95bc579a4a51..19d3acbf28d4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -20,10 +20,8 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.graphics.Color.WHITE;
import static android.graphics.Color.alpha;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.ViewRootImpl.LOCAL_LAYOUT;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
-import static android.view.WindowLayout.UNSPECIFIED_LENGTH;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
@@ -53,7 +51,6 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityThread;
-import android.app.WindowConfiguration;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -80,7 +77,6 @@ import android.view.SurfaceSession;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
-import android.view.WindowLayout;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.window.ClientWindowFrames;
@@ -212,8 +208,6 @@ public class TaskSnapshotWindow {
final IWindowSession session = WindowManagerGlobal.getWindowSession();
final SurfaceControl surfaceControl = new SurfaceControl();
final ClientWindowFrames tmpFrames = new ClientWindowFrames();
- final WindowLayout windowLayout = new WindowLayout();
- final Rect displayCutoutSafe = new Rect();
final InsetsSourceControl[] tmpControls = new InsetsSourceControl[0];
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
@@ -238,7 +232,8 @@ public class TaskSnapshotWindow {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#addToDisplay");
final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
- info.requestedVisibilities, tmpInputChannel, tmpInsetsState, tmpControls);
+ info.requestedVisibilities, tmpInputChannel, tmpInsetsState, tmpControls,
+ new Rect());
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
@@ -250,25 +245,9 @@ public class TaskSnapshotWindow {
window.setOuter(snapshotSurface);
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#relayout");
- if (LOCAL_LAYOUT) {
- if (!surfaceControl.isValid()) {
- session.updateVisibility(window, layoutParams, View.VISIBLE,
- tmpMergedConfiguration, surfaceControl, tmpInsetsState, tmpControls);
- }
- tmpInsetsState.getDisplayCutoutSafe(displayCutoutSafe);
- final WindowConfiguration winConfig =
- tmpMergedConfiguration.getMergedConfiguration().windowConfiguration;
- windowLayout.computeFrames(layoutParams, tmpInsetsState, displayCutoutSafe,
- winConfig.getBounds(), winConfig.getWindowingMode(), UNSPECIFIED_LENGTH,
- UNSPECIFIED_LENGTH, info.requestedVisibilities,
- null /* attachedWindowFrame */, 1f /* compatScale */, tmpFrames);
- session.updateLayout(window, layoutParams, 0 /* flags */, tmpFrames,
- UNSPECIFIED_LENGTH, UNSPECIFIED_LENGTH);
- } else {
- session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0,
- tmpFrames, tmpMergedConfiguration, surfaceControl, tmpInsetsState,
- tmpControls, new Bundle());
- }
+ session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0,
+ tmpFrames, tmpMergedConfiguration, surfaceControl, tmpInsetsState,
+ tmpControls, new Bundle());
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} catch (RemoteException e) {
snapshotSurface.clearWindowSynced();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ConfigurationChangeListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ConfigurationChangeListener.java
new file mode 100644
index 000000000000..2fca8f0ecc76
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ConfigurationChangeListener.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.sysui;
+
+import android.content.res.Configuration;
+
+/**
+ * Callbacks for when the configuration changes.
+ */
+public interface ConfigurationChangeListener {
+
+ /**
+ * Called when a configuration changes. This precedes all the following callbacks.
+ */
+ default void onConfigurationChanged(Configuration newConfiguration) {}
+
+ /**
+ * Convenience method to the above, called when the density or font scale changes.
+ */
+ default void onDensityOrFontScaleChanged() {}
+
+ /**
+ * Convenience method to the above, called when the smallest screen width changes.
+ */
+ default void onSmallestScreenWidthChanged() {}
+
+ /**
+ * Convenience method to the above, called when the system theme changes, including dark/light
+ * UI_MODE changes.
+ */
+ default void onThemeChanged() {}
+
+ /**
+ * Convenience method to the above, called when the local list or layout direction changes.
+ */
+ default void onLocaleOrLayoutDirectionChanged() {}
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/KeyguardChangeListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/KeyguardChangeListener.java
new file mode 100644
index 000000000000..1c0b35894acd
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/KeyguardChangeListener.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.sysui;
+
+/**
+ * Callbacks for when the keyguard changes.
+ */
+public interface KeyguardChangeListener {
+ /**
+ * Notifies the Shell that the keyguard is showing (and if so, whether it is occluded).
+ */
+ default void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
+ boolean animatingDismiss) {}
+
+ /**
+ * Notifies the Shell when the keyguard dismiss animation has finished.
+ *
+ * TODO(b/206741900) deprecate this path once we're able to animate the PiP window as part of
+ * keyguard dismiss animation.
+ */
+ default void onKeyguardDismissAnimationFinished() {}
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java
index 06f4367752fb..0427efb30b9f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellCommandHandler.java
@@ -14,15 +14,14 @@
* limitations under the License.
*/
-package com.android.wm.shell;
+package com.android.wm.shell.sysui;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import com.android.wm.shell.apppairs.AppPairsController;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.recents.RecentTasksController;
@@ -36,62 +35,50 @@ import java.util.Optional;
*
* Use with {@code adb shell dumpsys activity service SystemUIService WMShell ...}.
*/
-public final class ShellCommandHandlerImpl {
- private static final String TAG = ShellCommandHandlerImpl.class.getSimpleName();
+public final class ShellCommandHandler {
+ private static final String TAG = ShellCommandHandler.class.getSimpleName();
- private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
private final Optional<SplitScreenController> mSplitScreenOptional;
private final Optional<Pip> mPipOptional;
private final Optional<OneHandedController> mOneHandedOptional;
private final Optional<HideDisplayCutoutController> mHideDisplayCutout;
- private final Optional<AppPairsController> mAppPairsOptional;
private final Optional<RecentTasksController> mRecentTasks;
private final ShellTaskOrganizer mShellTaskOrganizer;
private final KidsModeTaskOrganizer mKidsModeTaskOrganizer;
private final ShellExecutor mMainExecutor;
- private final HandlerImpl mImpl = new HandlerImpl();
- public ShellCommandHandlerImpl(
+ public ShellCommandHandler(
+ ShellController shellController,
ShellTaskOrganizer shellTaskOrganizer,
KidsModeTaskOrganizer kidsModeTaskOrganizer,
- Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<Pip> pipOptional,
Optional<OneHandedController> oneHandedOptional,
Optional<HideDisplayCutoutController> hideDisplayCutout,
- Optional<AppPairsController> appPairsOptional,
Optional<RecentTasksController> recentTasks,
ShellExecutor mainExecutor) {
mShellTaskOrganizer = shellTaskOrganizer;
mKidsModeTaskOrganizer = kidsModeTaskOrganizer;
mRecentTasks = recentTasks;
- mLegacySplitScreenOptional = legacySplitScreenOptional;
mSplitScreenOptional = splitScreenOptional;
mPipOptional = pipOptional;
mOneHandedOptional = oneHandedOptional;
mHideDisplayCutout = hideDisplayCutout;
- mAppPairsOptional = appPairsOptional;
mMainExecutor = mainExecutor;
- }
-
- public ShellCommandHandler asShellCommandHandler() {
- return mImpl;
+ // TODO(238217847): To be removed once the command handler dependencies are inverted
+ shellController.setShellCommandHandler(this);
}
/** Dumps WM Shell internal state. */
- private void dump(PrintWriter pw) {
+ public void dump(PrintWriter pw) {
mShellTaskOrganizer.dump(pw, "");
pw.println();
pw.println();
mPipOptional.ifPresent(pip -> pip.dump(pw));
- mLegacySplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw));
mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw));
mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw));
pw.println();
pw.println();
- mAppPairsOptional.ifPresent(appPairs -> appPairs.dump(pw, ""));
- pw.println();
- pw.println();
mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw, ""));
pw.println();
pw.println();
@@ -103,16 +90,12 @@ public final class ShellCommandHandlerImpl {
/** Returns {@code true} if command was found and executed. */
- private boolean handleCommand(final String[] args, PrintWriter pw) {
+ public boolean handleCommand(final String[] args, PrintWriter pw) {
if (args.length < 2) {
// Argument at position 0 is "WMShell".
return false;
}
switch (args[1]) {
- case "pair":
- return runPair(args, pw);
- case "unpair":
- return runUnpair(args, pw);
case "moveToSideStage":
return runMoveToSideStage(args, pw);
case "removeFromSideStage":
@@ -126,29 +109,6 @@ public final class ShellCommandHandlerImpl {
}
}
- private boolean runPair(String[] args, PrintWriter pw) {
- if (args.length < 4) {
- // First two arguments are "WMShell" and command name.
- pw.println("Error: two task ids should be provided as arguments");
- return false;
- }
- final int taskId1 = new Integer(args[2]);
- final int taskId2 = new Integer(args[3]);
- mAppPairsOptional.ifPresent(appPairs -> appPairs.pair(taskId1, taskId2));
- return true;
- }
-
- private boolean runUnpair(String[] args, PrintWriter pw) {
- if (args.length < 3) {
- // First two arguments are "WMShell" and command name.
- pw.println("Error: task id should be provided as an argument");
- return false;
- }
- final int taskId = new Integer(args[2]);
- mAppPairsOptional.ifPresent(appPairs -> appPairs.unpair(taskId));
- return true;
- }
-
private boolean runMoveToSideStage(String[] args, PrintWriter pw) {
if (args.length < 3) {
// First arguments are "WMShell" and command name.
@@ -203,28 +163,4 @@ public final class ShellCommandHandlerImpl {
pw.println(" Sets the position of the side-stage.");
return true;
}
-
- private class HandlerImpl implements ShellCommandHandler {
- @Override
- public void dump(PrintWriter pw) {
- try {
- mMainExecutor.executeBlocking(() -> ShellCommandHandlerImpl.this.dump(pw));
- } catch (InterruptedException e) {
- throw new RuntimeException("Failed to dump the Shell in 2s", e);
- }
- }
-
- @Override
- public boolean handleCommand(String[] args, PrintWriter pw) {
- try {
- boolean[] result = new boolean[1];
- mMainExecutor.executeBlocking(() -> {
- result[0] = ShellCommandHandlerImpl.this.handleCommand(args, pw);
- });
- return result[0];
- } catch (InterruptedException e) {
- throw new RuntimeException("Failed to handle Shell command in 2s", e);
- }
- }
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
new file mode 100644
index 000000000000..618028c1544f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.sysui;
+
+import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS;
+import static android.content.pm.ActivityInfo.CONFIG_FONT_SCALE;
+import static android.content.pm.ActivityInfo.CONFIG_LAYOUT_DIRECTION;
+import static android.content.pm.ActivityInfo.CONFIG_LOCALE;
+import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SYSUI_EVENTS;
+
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ExternalThread;
+
+import java.io.PrintWriter;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Handles event callbacks from SysUI that can be used within the Shell.
+ */
+public class ShellController {
+ private static final String TAG = ShellController.class.getSimpleName();
+
+ private final ShellExecutor mMainExecutor;
+ private final ShellInterfaceImpl mImpl = new ShellInterfaceImpl();
+
+ private ShellInit mShellInit;
+ private ShellCommandHandler mShellCommandHandler;
+
+ private final CopyOnWriteArrayList<ConfigurationChangeListener> mConfigChangeListeners =
+ new CopyOnWriteArrayList<>();
+ private final CopyOnWriteArrayList<KeyguardChangeListener> mKeyguardChangeListeners =
+ new CopyOnWriteArrayList<>();
+ private Configuration mLastConfiguration;
+
+
+ public ShellController(ShellExecutor mainExecutor) {
+ mMainExecutor = mainExecutor;
+ }
+
+ /**
+ * Returns the external interface to this controller.
+ */
+ public ShellInterface asShell() {
+ return mImpl;
+ }
+
+ /**
+ * Sets the init handler to call back to.
+ * TODO(238217847): This is only exposed this way until we can remove the dependencies from the
+ * init handler to other classes.
+ */
+ public void setShellInit(ShellInit shellInit) {
+ mShellInit = shellInit;
+ }
+
+ /**
+ * Sets the command handler to call back to.
+ * TODO(238217847): This is only exposed this way until we can remove the dependencies from the
+ * command handler to other classes.
+ */
+ public void setShellCommandHandler(ShellCommandHandler shellCommandHandler) {
+ mShellCommandHandler = shellCommandHandler;
+ }
+
+ /**
+ * Adds a new configuration listener. The configuration change callbacks are not made in any
+ * particular order.
+ */
+ public void addConfigurationChangeListener(ConfigurationChangeListener listener) {
+ mConfigChangeListeners.remove(listener);
+ mConfigChangeListeners.add(listener);
+ }
+
+ /**
+ * Removes an existing configuration listener.
+ */
+ public void removeConfigurationChangeListener(ConfigurationChangeListener listener) {
+ mConfigChangeListeners.remove(listener);
+ }
+
+ /**
+ * Adds a new Keyguard listener. The Keyguard change callbacks are not made in any
+ * particular order.
+ */
+ public void addKeyguardChangeListener(KeyguardChangeListener listener) {
+ mKeyguardChangeListeners.remove(listener);
+ mKeyguardChangeListeners.add(listener);
+ }
+
+ /**
+ * Removes an existing Keyguard listener.
+ */
+ public void removeKeyguardChangeListener(KeyguardChangeListener listener) {
+ mKeyguardChangeListeners.remove(listener);
+ }
+
+ @VisibleForTesting
+ void onConfigurationChanged(Configuration newConfig) {
+ // The initial config is send on startup and doesn't trigger listener callbacks
+ if (mLastConfiguration == null) {
+ mLastConfiguration = new Configuration(newConfig);
+ ProtoLog.v(WM_SHELL_SYSUI_EVENTS, "Initial Configuration: %s", newConfig);
+ return;
+ }
+
+ final int diff = newConfig.diff(mLastConfiguration);
+ ProtoLog.v(WM_SHELL_SYSUI_EVENTS, "New configuration change: %s", newConfig);
+ ProtoLog.v(WM_SHELL_SYSUI_EVENTS, "\tchanges=%s",
+ Configuration.configurationDiffToString(diff));
+ final boolean densityFontScaleChanged = (diff & CONFIG_FONT_SCALE) != 0
+ || (diff & ActivityInfo.CONFIG_DENSITY) != 0;
+ final boolean smallestScreenWidthChanged = (diff & CONFIG_SMALLEST_SCREEN_SIZE) != 0;
+ final boolean themeChanged = (diff & CONFIG_ASSETS_PATHS) != 0
+ || (diff & CONFIG_UI_MODE) != 0;
+ final boolean localOrLayoutDirectionChanged = (diff & CONFIG_LOCALE) != 0
+ || (diff & CONFIG_LAYOUT_DIRECTION) != 0;
+
+ // Update the last configuration and call listeners
+ mLastConfiguration.updateFrom(newConfig);
+ for (ConfigurationChangeListener listener : mConfigChangeListeners) {
+ listener.onConfigurationChanged(newConfig);
+ if (densityFontScaleChanged) {
+ listener.onDensityOrFontScaleChanged();
+ }
+ if (smallestScreenWidthChanged) {
+ listener.onSmallestScreenWidthChanged();
+ }
+ if (themeChanged) {
+ listener.onThemeChanged();
+ }
+ if (localOrLayoutDirectionChanged) {
+ listener.onLocaleOrLayoutDirectionChanged();
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void onKeyguardVisibilityChanged(boolean visible, boolean occluded, boolean animatingDismiss) {
+ for (KeyguardChangeListener listener : mKeyguardChangeListeners) {
+ listener.onKeyguardVisibilityChanged(visible, occluded, animatingDismiss);
+ }
+ }
+
+ @VisibleForTesting
+ void onKeyguardDismissAnimationFinished() {
+ for (KeyguardChangeListener listener : mKeyguardChangeListeners) {
+ listener.onKeyguardDismissAnimationFinished();
+ }
+ }
+
+ public void dump(@NonNull PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + TAG);
+ pw.println(innerPrefix + "mConfigChangeListeners=" + mConfigChangeListeners.size());
+ pw.println(innerPrefix + "mLastConfiguration=" + mLastConfiguration);
+ pw.println(innerPrefix + "mKeyguardChangeListeners=" + mKeyguardChangeListeners.size());
+ }
+
+ /**
+ * The interface for calls from outside the Shell, within the host process.
+ */
+ @ExternalThread
+ private class ShellInterfaceImpl implements ShellInterface {
+
+ @Override
+ public void onInit() {
+ try {
+ mMainExecutor.executeBlocking(() -> mShellInit.init());
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Failed to initialize the Shell in 2s", e);
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ try {
+ mMainExecutor.executeBlocking(() -> mShellCommandHandler.dump(pw));
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Failed to dump the Shell in 2s", e);
+ }
+ }
+
+ @Override
+ public boolean handleCommand(String[] args, PrintWriter pw) {
+ try {
+ boolean[] result = new boolean[1];
+ mMainExecutor.executeBlocking(() -> {
+ result[0] = mShellCommandHandler.handleCommand(args, pw);
+ });
+ return result[0];
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Failed to handle Shell command in 2s", e);
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfiguration) {
+ mMainExecutor.execute(() ->
+ ShellController.this.onConfigurationChanged(newConfiguration));
+ }
+
+ @Override
+ public void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
+ boolean animatingDismiss) {
+ mMainExecutor.execute(() ->
+ ShellController.this.onKeyguardVisibilityChanged(visible, occluded,
+ animatingDismiss));
+ }
+
+ @Override
+ public void onKeyguardDismissAnimationFinished() {
+ mMainExecutor.execute(() ->
+ ShellController.this.onKeyguardDismissAnimationFinished());
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
index 62fb840d29d1..2619b37b67d8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
@@ -14,36 +14,47 @@
* limitations under the License.
*/
-package com.android.wm.shell;
+package com.android.wm.shell.sysui;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_INIT;
-import com.android.wm.shell.apppairs.AppPairsController;
+import android.os.Build;
+import android.os.SystemClock;
+import android.util.Pair;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
-import com.android.wm.shell.fullscreen.FullscreenUnfoldController;
import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingWindowController;
+import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
+import java.util.ArrayList;
import java.util.Optional;
/**
- * The entry point implementation into the shell for initializing shell internal state.
+ * The entry point implementation into the shell for initializing shell internal state. Classes
+ * which need to setup on start should inject an instance of this class and add an init callback.
*/
-public class ShellInitImpl {
- private static final String TAG = ShellInitImpl.class.getSimpleName();
+public class ShellInit {
+ private static final String TAG = ShellInit.class.getSimpleName();
private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
@@ -53,20 +64,23 @@ public class ShellInitImpl {
private final KidsModeTaskOrganizer mKidsModeTaskOrganizer;
private final Optional<BubbleController> mBubblesOptional;
private final Optional<SplitScreenController> mSplitScreenOptional;
- private final Optional<AppPairsController> mAppPairsOptional;
private final Optional<PipTouchHandler> mPipTouchHandlerOptional;
private final FullscreenTaskListener mFullscreenTaskListener;
- private final Optional<FullscreenUnfoldController> mFullscreenUnfoldController;
+ private final Optional<UnfoldAnimationController> mUnfoldController;
private final Optional<UnfoldTransitionHandler> mUnfoldTransitionHandler;
- private final Optional<FreeformTaskListener> mFreeformTaskListenerOptional;
+ private final Optional<FreeformTaskListener<?>> mFreeformTaskListenerOptional;
private final ShellExecutor mMainExecutor;
private final Transitions mTransitions;
private final StartingWindowController mStartingWindow;
private final Optional<RecentTasksController> mRecentTasks;
+ private final Optional<ActivityEmbeddingController> mActivityEmbeddingOptional;
- private final InitImpl mImpl = new InitImpl();
+ // An ordered list of init callbacks to be made once shell is first started
+ private final ArrayList<Pair<String, Runnable>> mInitCallbacks = new ArrayList<>();
+ private boolean mHasInitialized;
- public ShellInitImpl(
+ public ShellInit(
+ ShellController shellController,
DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
@@ -75,13 +89,13 @@ public class ShellInitImpl {
KidsModeTaskOrganizer kidsModeTaskOrganizer,
Optional<BubbleController> bubblesOptional,
Optional<SplitScreenController> splitScreenOptional,
- Optional<AppPairsController> appPairsOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
- Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController,
+ Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
- Optional<FreeformTaskListener> freeformTaskListenerOptional,
+ Optional<FreeformTaskListener<?>> freeformTaskListenerOptional,
Optional<RecentTasksController> recentTasks,
+ Optional<ActivityEmbeddingController> activityEmbeddingOptional,
Transitions transitions,
StartingWindowController startingWindow,
ShellExecutor mainExecutor) {
@@ -93,23 +107,21 @@ public class ShellInitImpl {
mKidsModeTaskOrganizer = kidsModeTaskOrganizer;
mBubblesOptional = bubblesOptional;
mSplitScreenOptional = splitScreenOptional;
- mAppPairsOptional = appPairsOptional;
mFullscreenTaskListener = fullscreenTaskListener;
mPipTouchHandlerOptional = pipTouchHandlerOptional;
- mFullscreenUnfoldController = fullscreenUnfoldTransitionController;
+ mUnfoldController = unfoldAnimationController;
mUnfoldTransitionHandler = unfoldTransitionHandler;
mFreeformTaskListenerOptional = freeformTaskListenerOptional;
mRecentTasks = recentTasks;
+ mActivityEmbeddingOptional = activityEmbeddingOptional;
mTransitions = transitions;
mMainExecutor = mainExecutor;
mStartingWindow = startingWindow;
+ // TODO(238217847): To be removed once the init dependencies are inverted
+ shellController.setShellInit(this);
}
- public ShellInit asShellInit() {
- return mImpl;
- }
-
- private void init() {
+ private void legacyInit() {
// Start listening for display and insets changes
mDisplayController.initialize();
mDisplayInsetsController.initialize();
@@ -121,7 +133,6 @@ public class ShellInitImpl {
mShellTaskOrganizer.initStartingWindow(mStartingWindow);
mShellTaskOrganizer.registerOrganizer();
- mAppPairsOptional.ifPresent(AppPairsController::onOrganizerRegistered);
mSplitScreenOptional.ifPresent(SplitScreenController::onOrganizerRegistered);
mBubblesOptional.ifPresent(BubbleController::initialize);
@@ -130,7 +141,15 @@ public class ShellInitImpl {
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mTransitions.register(mShellTaskOrganizer);
+ mActivityEmbeddingOptional.ifPresent(ActivityEmbeddingController::init);
mUnfoldTransitionHandler.ifPresent(UnfoldTransitionHandler::init);
+ if (mSplitScreenOptional.isPresent() && mPipTouchHandlerOptional.isPresent()) {
+ final DefaultMixedHandler mixedHandler = new DefaultMixedHandler(mTransitions,
+ mPipTouchHandlerOptional.get().getTransitionHandler(),
+ mSplitScreenOptional.get().getTransitionHandler());
+ // Added at end so that it has highest priority.
+ mTransitions.addHandler(mixedHandler);
+ }
}
// TODO(b/181599115): This should really be the pip controller, but until we can provide the
@@ -143,22 +162,50 @@ public class ShellInitImpl {
mShellTaskOrganizer.addListenerForType(
f, ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM));
- mFullscreenUnfoldController.ifPresent(FullscreenUnfoldController::init);
+ mUnfoldController.ifPresent(UnfoldAnimationController::init);
mRecentTasks.ifPresent(RecentTasksController::init);
// Initialize kids mode task organizer
mKidsModeTaskOrganizer.initialize(mStartingWindow);
}
- @ExternalThread
- private class InitImpl implements ShellInit {
- @Override
- public void init() {
- try {
- mMainExecutor.executeBlocking(() -> ShellInitImpl.this.init());
- } catch (InterruptedException e) {
- throw new RuntimeException("Failed to initialize the Shell in 2s", e);
+ /**
+ * Adds a callback to the ordered list of callbacks be made when Shell is first started. This
+ * can be used in class constructors when dagger is used to ensure that the initialization order
+ * matches the dependency order.
+ */
+ public <T extends Object> void addInitCallback(Runnable r, T instance) {
+ if (mHasInitialized) {
+ if (Build.isDebuggable()) {
+ // All callbacks must be added prior to the Shell being initialized
+ throw new IllegalArgumentException("Can not add callback after init");
}
+ return;
}
+ final String className = instance.getClass().getSimpleName();
+ mInitCallbacks.add(new Pair<>(className, r));
+ ProtoLog.v(WM_SHELL_INIT, "Adding init callback for %s", className);
+ }
+
+ /**
+ * Calls all the init callbacks when the Shell is first starting.
+ */
+ @VisibleForTesting
+ public void init() {
+ ProtoLog.v(WM_SHELL_INIT, "Initializing Shell Components: %d", mInitCallbacks.size());
+ // Init in order of registration
+ for (int i = 0; i < mInitCallbacks.size(); i++) {
+ final Pair<String, Runnable> info = mInitCallbacks.get(i);
+ final long t1 = SystemClock.uptimeMillis();
+ info.second.run();
+ final long t2 = SystemClock.uptimeMillis();
+ ProtoLog.v(WM_SHELL_INIT, "\t%s took %dms", info.first, (t2 - t1));
+ }
+ mInitCallbacks.clear();
+
+ // TODO: To be removed
+ legacyInit();
+
+ mHasInitialized = true;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java
new file mode 100644
index 000000000000..254c253b0042
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.sysui;
+
+import android.content.res.Configuration;
+
+import java.io.PrintWriter;
+
+/**
+ * General interface for notifying the Shell of common SysUI events like configuration or keyguard
+ * changes.
+ */
+public interface ShellInterface {
+
+ /**
+ * Initializes the shell state.
+ */
+ default void onInit() {}
+
+ /**
+ * Dumps the shell state.
+ */
+ default void dump(PrintWriter pw) {}
+
+ /**
+ * Handles a shell command.
+ */
+ default boolean handleCommand(final String[] args, PrintWriter pw) {
+ return false;
+ }
+
+ /**
+ * Notifies the Shell that the configuration has changed.
+ */
+ default void onConfigurationChanged(Configuration newConfiguration) {}
+
+ /**
+ * Notifies the Shell that the keyguard is showing (and if so, whether it is occluded) or not
+ * showing, and whether it is animating a dismiss.
+ */
+ default void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
+ boolean animatingDismiss) {}
+
+ /**
+ * Notifies the Shell when the keyguard dismiss animation has finished.
+ */
+ default void onKeyguardDismissAnimationFinished() {}
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java
deleted file mode 100644
index ad9dda619370..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.tasksurfacehelper;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.graphics.Rect;
-import android.view.SurfaceControl;
-
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-/**
- * Interface to communicate with a Task's SurfaceControl.
- */
-public interface TaskSurfaceHelper {
-
- /** Sets the METADATA_GAME_MODE for the layer corresponding to the task **/
- default void setGameModeForTask(int taskId, int gameMode) {}
-
- /** Takes a screenshot for a task **/
- default void screenshotTask(RunningTaskInfo taskInfo, Rect crop, Executor executor,
- Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) {}
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.java
deleted file mode 100644
index 064d9d1231c1..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.tasksurfacehelper;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.graphics.Rect;
-import android.view.SurfaceControl;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.ShellExecutor;
-
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-/**
- * Intermediary controller that communicates with {@link ShellTaskOrganizer} to send commands
- * to SurfaceControl.
- */
-public class TaskSurfaceHelperController {
-
- private final ShellTaskOrganizer mTaskOrganizer;
- private final ShellExecutor mMainExecutor;
- private final TaskSurfaceHelperImpl mImpl = new TaskSurfaceHelperImpl();
-
- public TaskSurfaceHelperController(ShellTaskOrganizer taskOrganizer,
- ShellExecutor mainExecutor) {
- mTaskOrganizer = taskOrganizer;
- mMainExecutor = mainExecutor;
- }
-
- public TaskSurfaceHelper asTaskSurfaceHelper() {
- return mImpl;
- }
-
- /**
- * Sends a Transaction to set the game mode metadata on the
- * corresponding SurfaceControl
- */
- public void setGameModeForTask(int taskId, int gameMode) {
- mTaskOrganizer.setSurfaceMetadata(taskId, SurfaceControl.METADATA_GAME_MODE, gameMode);
- }
-
- /**
- * Take screenshot of the specified task.
- */
- public void screenshotTask(RunningTaskInfo taskInfo, Rect crop,
- Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) {
- mTaskOrganizer.screenshotTask(taskInfo, crop, consumer);
- }
-
- private class TaskSurfaceHelperImpl implements TaskSurfaceHelper {
- @Override
- public void setGameModeForTask(int taskId, int gameMode) {
- mMainExecutor.execute(() -> {
- TaskSurfaceHelperController.this.setGameModeForTask(taskId, gameMode);
- });
- }
-
- @Override
- public void screenshotTask(RunningTaskInfo taskInfo, Rect crop, Executor executor,
- Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) {
- mMainExecutor.execute(() -> {
- TaskSurfaceHelperController.this.screenshotTask(taskInfo, crop,
- (t) -> executor.execute(() -> consumer.accept(t)));
- });
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
new file mode 100644
index 000000000000..11b453cb24a2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.transition;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
+import static com.android.wm.shell.splitscreen.StageCoordinator.FLAG_IS_DIVIDER_BAR;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.splitscreen.StageCoordinator;
+
+import java.util.ArrayList;
+
+/**
+ * A handler for dealing with transitions involving multiple other handlers. For example: an
+ * activity in split-screen going into PiP.
+ */
+public class DefaultMixedHandler implements Transitions.TransitionHandler {
+
+ private final Transitions mPlayer;
+ private final PipTransitionController mPipHandler;
+ private final StageCoordinator mSplitHandler;
+
+ private static class MixedTransition {
+ static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
+
+ /** The default animation for this mixed transition. */
+ static final int ANIM_TYPE_DEFAULT = 0;
+
+ /** For ENTER_PIP_FROM_SPLIT, indicates that this is a to-home animation. */
+ static final int ANIM_TYPE_GOING_HOME = 1;
+
+ final int mType;
+ int mAnimType = 0;
+ final IBinder mTransition;
+
+ Transitions.TransitionFinishCallback mFinishCallback = null;
+ Transitions.TransitionHandler mLeftoversHandler = null;
+
+ /**
+ * Mixed transitions are made up of multiple "parts". This keeps track of how many
+ * parts are currently animating.
+ */
+ int mInFlightSubAnimations = 0;
+
+ MixedTransition(int type, IBinder transition) {
+ mType = type;
+ mTransition = transition;
+ }
+ }
+ private final ArrayList<MixedTransition> mActiveTransitions = new ArrayList<>();
+
+ public DefaultMixedHandler(@NonNull Transitions player,
+ @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) {
+ mPlayer = player;
+ mPipHandler = pipHandler;
+ mSplitHandler = splitHandler;
+ }
+
+ @Nullable
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request) {
+ if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitActive()) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a PiP-enter request while "
+ + "Split-Screen is active, so treat it as Mixed.");
+ if (request.getRemoteTransition() != null) {
+ throw new IllegalStateException("Unexpected remote transition in"
+ + "pip-enter-from-split request");
+ }
+ mActiveTransitions.add(new MixedTransition(MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT,
+ transition));
+
+ WindowContainerTransaction out = new WindowContainerTransaction();
+ mPipHandler.augmentRequest(transition, request, out);
+ mSplitHandler.addEnterOrExitIfNeeded(request, out);
+ return out;
+ }
+ return null;
+ }
+
+ private TransitionInfo subCopy(@NonNull TransitionInfo info,
+ @WindowManager.TransitionType int newType) {
+ final TransitionInfo out = new TransitionInfo(newType, info.getFlags());
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ out.getChanges().add(info.getChanges().get(i));
+ }
+ out.setRootLeash(info.getRootLeash(), info.getRootOffset().x, info.getRootOffset().y);
+ out.setAnimationOptions(info.getAnimationOptions());
+ return out;
+ }
+
+ private boolean isHomeOpening(@NonNull TransitionInfo.Change change) {
+ return change.getTaskInfo() != null
+ && change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME;
+ }
+
+ private boolean isWallpaper(@NonNull TransitionInfo.Change change) {
+ return (change.getFlags() & FLAG_IS_WALLPAPER) != 0;
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ MixedTransition mixed = null;
+ for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
+ if (mActiveTransitions.get(i).mTransition != transition) continue;
+ mixed = mActiveTransitions.get(i);
+ break;
+ }
+ if (mixed == null) return false;
+
+ if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
+ return animateEnterPipFromSplit(mixed, info, startTransaction, finishTransaction,
+ finishCallback);
+ } else {
+ mActiveTransitions.remove(mixed);
+ throw new IllegalStateException("Starting mixed animation without a known mixed type? "
+ + mixed.mType);
+ }
+ }
+
+ private boolean animateEnterPipFromSplit(@NonNull final MixedTransition mixed,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
+ + "entering PIP while Split-Screen is active.");
+ TransitionInfo.Change pipChange = null;
+ TransitionInfo.Change wallpaper = null;
+ final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK);
+ boolean homeIsOpening = false;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (mPipHandler.isEnteringPip(change, info.getType())) {
+ if (pipChange != null) {
+ throw new IllegalStateException("More than 1 pip-entering changes in one"
+ + " transition? " + info);
+ }
+ pipChange = change;
+ // going backwards, so remove-by-index is fine.
+ everythingElse.getChanges().remove(i);
+ } else if (isHomeOpening(change)) {
+ homeIsOpening = true;
+ } else if (isWallpaper(change)) {
+ wallpaper = change;
+ }
+ }
+ if (pipChange == null) {
+ // um, something probably went wrong.
+ return false;
+ }
+ final boolean isGoingHome = homeIsOpening;
+ mixed.mFinishCallback = finishCallback;
+ Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
+ --mixed.mInFlightSubAnimations;
+ if (mixed.mInFlightSubAnimations > 0) return;
+ mActiveTransitions.remove(mixed);
+ if (isGoingHome) {
+ mSplitHandler.onTransitionAnimationComplete();
+ }
+ mixed.mFinishCallback.onTransitionFinished(wct, wctCB);
+ };
+ if (isGoingHome) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is actually mixed "
+ + "since entering-PiP caused us to leave split and return home.");
+ // We need to split the transition into 2 parts: the pip part (animated by pip)
+ // and the dismiss-part (animated by launcher).
+ mixed.mInFlightSubAnimations = 2;
+ // immediately make the wallpaper visible (so that we don't see it pop-in during
+ // the time it takes to start recents animation (which is remote).
+ if (wallpaper != null) {
+ startTransaction.show(wallpaper.getLeash()).setAlpha(wallpaper.getLeash(), 1.f);
+ }
+ // make a new startTransaction because pip's startEnterAnimation "consumes" it so
+ // we need a separate one to send over to launcher.
+ SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
+ // Let split update internal state for dismiss.
+ mSplitHandler.prepareDismissAnimation(STAGE_TYPE_UNDEFINED,
+ EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT,
+ finishTransaction);
+
+ // We are trying to accommodate launcher's close animation which can't handle the
+ // divider-bar, so if split-handler is closing the divider-bar, just hide it and remove
+ // from transition info.
+ for (int i = everythingElse.getChanges().size() - 1; i >= 0; --i) {
+ if ((everythingElse.getChanges().get(i).getFlags() & FLAG_IS_DIVIDER_BAR) != 0) {
+ everythingElse.getChanges().remove(i);
+ break;
+ }
+ }
+
+ mPipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
+ finishCB);
+ // Dispatch the rest of the transition normally. This will most-likely be taken by
+ // recents or default handler.
+ mixed.mLeftoversHandler = mPlayer.dispatchTransition(mixed.mTransition, everythingElse,
+ otherStartT, finishTransaction, finishCB, this);
+ } else {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Not leaving split, so just "
+ + "forward animation to Pip-Handler.");
+ // This happens if the pip-ing activity is in a multi-activity task (and thus a
+ // new pip task is spawned). In this case, we don't actually exit split so we can
+ // just let pip transition handle the animation verbatim.
+ mixed.mInFlightSubAnimations = 1;
+ mPipHandler.startAnimation(mixed.mTransition, info, startTransaction, finishTransaction,
+ finishCB);
+ }
+ return true;
+ }
+
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ for (int i = 0; i < mActiveTransitions.size(); ++i) {
+ if (mActiveTransitions.get(i) != mergeTarget) continue;
+ MixedTransition mixed = mActiveTransitions.get(i);
+ if (mixed.mInFlightSubAnimations <= 0) {
+ // Already done, so no need to end it.
+ return;
+ }
+ if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
+ if (mixed.mAnimType == MixedTransition.ANIM_TYPE_GOING_HOME) {
+ boolean ended = mSplitHandler.end();
+ // If split couldn't end (because it is remote), then don't end everything else
+ // since we have to play out the animation anyways.
+ if (!ended) return;
+ mPipHandler.end();
+ if (mixed.mLeftoversHandler != null) {
+ mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget,
+ finishCallback);
+ }
+ } else {
+ mPipHandler.end();
+ }
+ } else {
+ throw new IllegalStateException("Playing a mixed transition with unknown type? "
+ + mixed.mType);
+ }
+ }
+ }
+
+ @Override
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
+ MixedTransition mixed = null;
+ for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
+ if (mActiveTransitions.get(i).mTransition != transition) continue;
+ mixed = mActiveTransitions.remove(i);
+ break;
+ }
+ if (mixed == null) return;
+ if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
+ mPipHandler.onTransitionConsumed(transition, aborted);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 9154226b7b22..dcd6277966dd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -24,6 +24,7 @@ import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE;
@@ -42,6 +43,7 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.transitTypeToString;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
@@ -470,8 +472,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
final Rect clipRect = Transitions.isClosingType(change.getMode())
- ? mRotator.getEndBoundsInStartRotation(change)
- : change.getEndAbsBounds();
+ ? new Rect(mRotator.getEndBoundsInStartRotation(change))
+ : new Rect(change.getEndAbsBounds());
+ clipRect.offsetTo(0, 0);
if (delayedEdgeExtension) {
// If the edge extension needs to happen after the startTransition has been
@@ -520,6 +523,18 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
return true;
}
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ArrayList<Animator> anims = mAnimations.get(mergeTarget);
+ if (anims == null) return;
+ for (int i = anims.size() - 1; i >= 0; --i) {
+ final Animator anim = anims.get(i);
+ mAnimExecutor.execute(anim::end);
+ }
+ }
+
private void edgeExtendWindow(TransitionInfo.Change change,
Animation a, SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction) {
@@ -684,6 +699,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final Rect endBounds = Transitions.isClosingType(changeMode)
? mRotator.getEndBoundsInStartRotation(change)
: change.getEndAbsBounds();
+ final boolean isDream =
+ isTask && change.getTaskInfo().topActivityType == ACTIVITY_TYPE_DREAM;
if (info.isKeyguardGoingAway()) {
a = mTransitionAnimation.loadKeyguardExitAnimation(flags,
@@ -726,7 +743,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
} else {
int animAttr = 0;
boolean translucent = false;
- if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
+ if (isDream) {
+ if (type == TRANSIT_OPEN) {
+ animAttr = enter
+ ? R.styleable.WindowAnimation_dreamActivityOpenEnterAnimation
+ : R.styleable.WindowAnimation_dreamActivityOpenExitAnimation;
+ } else if (type == TRANSIT_CLOSE) {
+ animAttr = enter
+ ? 0
+ : R.styleable.WindowAnimation_dreamActivityCloseExitAnimation;
+ }
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
animAttr = enter
? R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
: R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
@@ -790,6 +817,11 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
a = mTransitionAnimation.loadDefaultAnimationAttr(animAttr, translucent);
}
}
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "loadAnimation: anim=%s animAttr=0x%x type=%s isEntrance=%b", a, animAttr,
+ transitTypeToString(type),
+ enter);
}
if (a != null) {
@@ -834,13 +866,19 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
});
};
va.addListener(new AnimatorListenerAdapter() {
+ private boolean mFinished = false;
+
@Override
public void onAnimationEnd(Animator animation) {
+ if (mFinished) return;
+ mFinished = true;
finisher.run();
}
@Override
public void onAnimationCancel(Animator animation) {
+ if (mFinished) return;
+ mFinished = true;
finisher.run();
}
});
@@ -954,7 +992,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private static void applyTransformation(long time, SurfaceControl.Transaction t,
SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix,
- Point position, float cornerRadius, @Nullable Rect clipRect) {
+ Point position, float cornerRadius, @Nullable Rect immutableClipRect) {
anim.getTransformation(time, transformation);
if (position != null) {
transformation.getMatrix().postTranslate(position.x, position.y);
@@ -962,6 +1000,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
t.setMatrix(leash, transformation.getMatrix(), matrix);
t.setAlpha(leash, transformation.getAlpha());
+ final Rect clipRect = immutableClipRect == null ? null : new Rect(immutableClipRect);
Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE);
if (!extensionInsets.equals(Insets.NONE) && clipRect != null && !clipRect.isEmpty()) {
// Clip out any overflowing edge extension
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 3e2a0e635a75..ebaece2189aa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -99,6 +99,8 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
+ " during unit tests");
}
mRemote.getRemoteTransition().startAnimation(transition, info, startTransaction, cb);
+ // assume that remote will apply the start transaction.
+ startTransaction.clear();
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error running remote transition.", e);
if (mRemote.asBinder() != null) {
@@ -120,6 +122,11 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
@Override
public void onTransitionFinished(WindowContainerTransaction wct,
SurfaceControl.Transaction sct) {
+ // We have merged, since we sent the transaction over binder, the one in this
+ // process won't be cleared if the remote applied it. We don't actually know if the
+ // remote applied the transaction, but applying twice will break surfaceflinger
+ // so just assume the worst-case and clear the local transaction.
+ t.clear();
mMainExecutor.execute(
() -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index ece9f47e8788..b15c48cb5889 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -83,7 +83,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
}
@Override
- public void onTransitionMerged(@NonNull IBinder transition) {
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) {
mRequestedRemotes.remove(transition);
}
@@ -139,6 +139,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
+ " during unit tests");
}
remote.getRemoteTransition().startAnimation(transition, info, startTransaction, cb);
+ // assume that remote will apply the start transaction.
+ startTransaction.clear();
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error running remote transition.", e);
unhandleDeath(remote.asBinder(), finishCallback);
@@ -162,6 +164,11 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
@Override
public void onTransitionFinished(WindowContainerTransaction wct,
SurfaceControl.Transaction sct) {
+ // We have merged, since we sent the transaction over binder, the one in this
+ // process won't be cleared if the remote applied it. We don't actually know if the
+ // remote applied the transaction, but applying twice will break surfaceflinger
+ // so just assume the worst-case and clear the local transaction.
+ t.clear();
mMainExecutor.execute(() -> {
if (!mRequestedRemotes.containsKey(mergeTarget)) {
Log.e(TAG, "Merged transition finished after it's mergeTarget (the "
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 435d67087f34..fa22c7ca94d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -23,6 +23,7 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
@@ -287,12 +288,14 @@ public class Transitions implements RemoteCallable<Transitions> {
finishT.setAlpha(leash, 1.f);
}
} else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
- // Wallpaper is a bit of an anomaly: it's visibility is tied to other WindowStates.
- // As a result, we actually can't hide it's WindowToken because there may not be a
- // transition associated with it becoming visible again. Fortunately, since it is
- // always z-ordered to the back, we don't have to worry about it flickering to the
- // front during reparenting, so the hide here isn't necessary for it.
- if ((change.getFlags() & FLAG_IS_WALLPAPER) == 0) {
+ // Wallpaper/IME are anomalies: their visibility is tied to other WindowStates.
+ // As a result, we actually can't hide their WindowTokens because there may not be a
+ // transition associated with them becoming visible again. Fortunately, since
+ // wallpapers are always z-ordered to the back, we don't have to worry about it
+ // flickering to the front during reparenting. Similarly, the IME is reparented to
+ // the associated app, so its visibility is coupled. So, an explicit hide is not
+ // needed visually anyways.
+ if ((change.getFlags() & (FLAG_IS_WALLPAPER | FLAG_IS_INPUT_METHOD)) == 0) {
finishT.hide(leash);
}
}
@@ -309,13 +312,14 @@ public class Transitions implements RemoteCallable<Transitions> {
if (info.getRootLeash().isValid()) {
t.show(info.getRootLeash());
}
+ final int numChanges = info.getChanges().size();
// Put animating stuff above this line and put static stuff below it.
- int zSplitLine = info.getChanges().size();
+ final int zSplitLine = numChanges + 1;
// changes should be ordered top-to-bottom in z
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ for (int i = numChanges - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
final SurfaceControl leash = change.getLeash();
- final int mode = info.getChanges().get(i).getMode();
+ final int mode = change.getMode();
// Don't reparent anything that isn't independent within its parents
if (!TransitionInfo.isIndependent(change, info)) {
@@ -329,26 +333,31 @@ public class Transitions implements RemoteCallable<Transitions> {
t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x,
change.getStartAbsBounds().top - info.getRootOffset().y);
}
+ final int layer;
// Put all the OPEN/SHOW on top
- if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
+ if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
+ // Wallpaper is always at the bottom.
+ layer = 0;
+ } else if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
if (isOpening) {
// put on top
- t.setLayer(leash, zSplitLine + info.getChanges().size() - i);
+ layer = zSplitLine + numChanges - i;
} else {
// put on bottom
- t.setLayer(leash, zSplitLine - i);
+ layer = zSplitLine - i;
}
} else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
if (isOpening) {
// put on bottom and leave visible
- t.setLayer(leash, zSplitLine - i);
+ layer = zSplitLine - i;
} else {
// put on top
- t.setLayer(leash, zSplitLine + info.getChanges().size() - i);
+ layer = zSplitLine + numChanges - i;
}
} else { // CHANGE or other
- t.setLayer(leash, zSplitLine + info.getChanges().size() - i);
+ layer = zSplitLine + numChanges - i;
}
+ t.setLayer(leash, layer);
}
}
@@ -377,6 +386,7 @@ public class Transitions implements RemoteCallable<Transitions> {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Invalid root leash (%s): %s",
transitionToken, info);
t.apply();
+ finishT.apply();
onAbort(transitionToken);
return;
}
@@ -400,6 +410,7 @@ public class Transitions implements RemoteCallable<Transitions> {
}
if (nonTaskChange && transferStartingWindow) {
t.apply();
+ finishT.apply();
// Treat this as an abort since we are bypassing any merge logic and effectively
// finishing immediately.
onAbort(transitionToken);
@@ -435,33 +446,42 @@ public class Transitions implements RemoteCallable<Transitions> {
playing.mToken, (wct, cb) -> onFinish(merging.mToken, wct, cb));
}
- boolean startAnimation(@NonNull ActiveTransition active, TransitionHandler handler) {
- return handler.startAnimation(active.mToken, active.mInfo, active.mStartT, active.mFinishT,
- (wct, cb) -> onFinish(active.mToken, wct, cb));
- }
-
- void playTransition(@NonNull ActiveTransition active) {
+ private void playTransition(@NonNull ActiveTransition active) {
setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT);
// If a handler already chose to run this animation, try delegating to it first.
if (active.mHandler != null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
active.mHandler);
- if (startAnimation(active, active.mHandler)) {
+ boolean consumed = active.mHandler.startAnimation(active.mToken, active.mInfo,
+ active.mStartT, active.mFinishT, (wct, cb) -> onFinish(active.mToken, wct, cb));
+ if (consumed) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
return;
}
}
- // Otherwise give every other handler a chance (in order)
+ // Otherwise give every other handler a chance
+ active.mHandler = dispatchTransition(active.mToken, active.mInfo, active.mStartT,
+ active.mFinishT, (wct, cb) -> onFinish(active.mToken, wct, cb), active.mHandler);
+ }
+
+ /**
+ * Gives every handler (in order) a chance to animate until one consumes the transition.
+ * @return the handler which consumed the transition.
+ */
+ TransitionHandler dispatchTransition(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT,
+ @NonNull TransitionFinishCallback finishCB, @Nullable TransitionHandler skip) {
for (int i = mHandlers.size() - 1; i >= 0; --i) {
- if (mHandlers.get(i) == active.mHandler) continue;
+ if (mHandlers.get(i) == skip) continue;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s",
mHandlers.get(i));
- if (startAnimation(active, mHandlers.get(i))) {
+ boolean consumed = mHandlers.get(i).startAnimation(transition, info, startT, finishT,
+ finishCB);
+ if (consumed) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
mHandlers.get(i));
- active.mHandler = mHandlers.get(i);
- return;
+ return mHandlers.get(i);
}
}
throw new IllegalStateException(
@@ -496,15 +516,20 @@ public class Transitions implements RemoteCallable<Transitions> {
active.mMerged = true;
active.mAborted = abort;
if (active.mHandler != null) {
- active.mHandler.onTransitionMerged(active.mToken);
+ active.mHandler.onTransitionConsumed(active.mToken, abort);
}
return;
}
- mActiveTransitions.get(activeIdx).mAborted = abort;
+ final ActiveTransition active = mActiveTransitions.get(activeIdx);
+ active.mAborted = abort;
+ if (active.mAborted && active.mHandler != null) {
+ // Notifies to clean-up the aborted transition.
+ active.mHandler.onTransitionConsumed(transition, true /* aborted */);
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"Transition animation finished (abort=%b), notifying core %s", abort, transition);
// Merge all relevant transactions together
- SurfaceControl.Transaction fullFinish = mActiveTransitions.get(activeIdx).mFinishT;
+ SurfaceControl.Transaction fullFinish = active.mFinishT;
for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) {
final ActiveTransition toMerge = mActiveTransitions.get(iA);
if (!toMerge.mMerged) break;
@@ -533,6 +558,10 @@ public class Transitions implements RemoteCallable<Transitions> {
while (mActiveTransitions.size() > activeIdx
&& mActiveTransitions.get(activeIdx).mAborted) {
ActiveTransition aborted = mActiveTransitions.remove(activeIdx);
+ // Notifies to clean-up the aborted transition.
+ if (aborted.mHandler != null) {
+ aborted.mHandler.onTransitionConsumed(transition, true /* aborted */);
+ }
mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */);
}
if (mActiveTransitions.size() <= activeIdx) {
@@ -615,8 +644,9 @@ public class Transitions implements RemoteCallable<Transitions> {
if (wct == null) {
wct = new WindowContainerTransaction();
}
- mDisplayController.getChangeController().dispatchOnRotateDisplay(wct,
- change.getDisplayId(), change.getStartRotation(), change.getEndRotation());
+ mDisplayController.getChangeController().dispatchOnDisplayChange(wct,
+ change.getDisplayId(), change.getStartRotation(), change.getEndRotation(),
+ null /* newDisplayAreaInfo */);
}
}
active.mToken = mOrganizer.startTransition(
@@ -714,9 +744,10 @@ public class Transitions implements RemoteCallable<Transitions> {
/**
* Called when a transition which was already "claimed" by this handler has been merged
- * into another animation. Gives this handler a chance to clean-up any expectations.
+ * into another animation or has been aborted. Gives this handler a chance to clean-up any
+ * expectations.
*/
- default void onTransitionMerged(@NonNull IBinder transition) { }
+ default void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) { }
/**
* Sets transition animation scale settings value to handler.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java
new file mode 100644
index 000000000000..05a024a0eb12
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.unfold;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.TaskInfo;
+import android.util.SparseArray;
+import android.view.SurfaceControl;
+
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
+import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.Executor;
+
+import dagger.Lazy;
+
+/**
+ * Manages fold/unfold animations of tasks on foldable devices.
+ * When folding or unfolding a foldable device we play animations that
+ * transform task cropping/scaling/rounded corners.
+ *
+ * This controller manages:
+ * 1) Folding/unfolding when Shell transitions disabled
+ * 2) Folding when Shell transitions enabled, unfolding is managed by
+ * {@link com.android.wm.shell.unfold.UnfoldTransitionHandler}
+ */
+public class UnfoldAnimationController implements UnfoldListener {
+
+ private final ShellUnfoldProgressProvider mUnfoldProgressProvider;
+ private final Executor mExecutor;
+ private final TransactionPool mTransactionPool;
+ private final List<UnfoldTaskAnimator> mAnimators;
+ private final Lazy<Optional<UnfoldTransitionHandler>> mUnfoldTransitionHandler;
+
+ private final SparseArray<SurfaceControl> mTaskSurfaces = new SparseArray<>();
+ private final SparseArray<UnfoldTaskAnimator> mAnimatorsByTaskId = new SparseArray<>();
+
+ public UnfoldAnimationController(@NonNull TransactionPool transactionPool,
+ @NonNull ShellUnfoldProgressProvider unfoldProgressProvider,
+ @NonNull List<UnfoldTaskAnimator> animators,
+ @NonNull Lazy<Optional<UnfoldTransitionHandler>> unfoldTransitionHandler,
+ @NonNull Executor executor) {
+ mUnfoldProgressProvider = unfoldProgressProvider;
+ mUnfoldTransitionHandler = unfoldTransitionHandler;
+ mTransactionPool = transactionPool;
+ mExecutor = executor;
+ mAnimators = animators;
+ }
+
+ /**
+ * Initializes the controller, starts listening for the external events
+ */
+ public void init() {
+ mUnfoldProgressProvider.addListener(mExecutor, this);
+
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ animator.init();
+ animator.start();
+ }
+ }
+
+ /**
+ * Called when a task appeared
+ * @param taskInfo info for the appeared task
+ * @param leash surface leash for the appeared task
+ */
+ public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+ mTaskSurfaces.put(taskInfo.taskId, leash);
+
+ // Find the first matching animator
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ if (animator.isApplicableTask(taskInfo)) {
+ mAnimatorsByTaskId.put(taskInfo.taskId, animator);
+ animator.onTaskAppeared(taskInfo, leash);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Called when task info changed
+ * @param taskInfo info for the changed task
+ */
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+ final UnfoldTaskAnimator animator = mAnimatorsByTaskId.get(taskInfo.taskId);
+ final boolean isCurrentlyApplicable = animator != null;
+
+ if (isCurrentlyApplicable) {
+ final boolean isApplicable = animator.isApplicableTask(taskInfo);
+ if (isApplicable) {
+ // Still applicable, send update
+ animator.onTaskChanged(taskInfo);
+ } else {
+ // Became inapplicable
+ resetTask(animator, taskInfo);
+ animator.onTaskVanished(taskInfo);
+ mAnimatorsByTaskId.remove(taskInfo.taskId);
+ }
+ } else {
+ // Find the first matching animator
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator currentAnimator = mAnimators.get(i);
+ if (currentAnimator.isApplicableTask(taskInfo)) {
+ // Became applicable
+ mAnimatorsByTaskId.put(taskInfo.taskId, currentAnimator);
+
+ SurfaceControl leash = mTaskSurfaces.get(taskInfo.taskId);
+ currentAnimator.onTaskAppeared(taskInfo, leash);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when a task vanished
+ * @param taskInfo info for the vanished task
+ */
+ public void onTaskVanished(RunningTaskInfo taskInfo) {
+ mTaskSurfaces.remove(taskInfo.taskId);
+
+ final UnfoldTaskAnimator animator = mAnimatorsByTaskId.get(taskInfo.taskId);
+ final boolean isCurrentlyApplicable = animator != null;
+
+ if (isCurrentlyApplicable) {
+ resetTask(animator, taskInfo);
+ animator.onTaskVanished(taskInfo);
+ mAnimatorsByTaskId.remove(taskInfo.taskId);
+ }
+ }
+
+ @Override
+ public void onStateChangeStarted() {
+ if (mUnfoldTransitionHandler.get().get().willHandleTransition()) {
+ return;
+ }
+
+ SurfaceControl.Transaction transaction = null;
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ if (animator.hasActiveTasks()) {
+ if (transaction == null) transaction = mTransactionPool.acquire();
+ animator.prepareStartTransaction(transaction);
+ }
+ }
+
+ if (transaction != null) {
+ transaction.apply();
+ mTransactionPool.release(transaction);
+ }
+ }
+
+ @Override
+ public void onStateChangeProgress(float progress) {
+ if (mUnfoldTransitionHandler.get().get().willHandleTransition()) {
+ return;
+ }
+
+ SurfaceControl.Transaction transaction = null;
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ if (animator.hasActiveTasks()) {
+ if (transaction == null) transaction = mTransactionPool.acquire();
+ animator.applyAnimationProgress(progress, transaction);
+ }
+ }
+
+ if (transaction != null) {
+ transaction.apply();
+ mTransactionPool.release(transaction);
+ }
+ }
+
+ @Override
+ public void onStateChangeFinished() {
+ if (mUnfoldTransitionHandler.get().get().willHandleTransition()) {
+ return;
+ }
+
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ animator.resetAllSurfaces(transaction);
+ animator.prepareFinishTransaction(transaction);
+ }
+
+ transaction.apply();
+
+ mTransactionPool.release(transaction);
+ }
+
+ private void resetTask(UnfoldTaskAnimator animator, TaskInfo taskInfo) {
+ if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
+ // PiP task has its own cleanup path, ignore surface reset to avoid conflict.
+ return;
+ }
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ animator.resetSurface(taskInfo, transaction);
+ transaction.apply();
+ mTransactionPool.release(transaction);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java
index 9faf454261d3..86ca292399cb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java
@@ -79,7 +79,7 @@ public class UnfoldBackgroundController {
}
private float[] getBackgroundColor(Context context) {
- int colorInt = context.getResources().getColor(R.color.unfold_transition_background);
+ int colorInt = context.getResources().getColor(R.color.taskbar_background);
return new float[]{
(float) red(colorInt) / 255.0F,
(float) green(colorInt) / 255.0F,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index 639603941c18..9bf32faa12bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.unfold;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.TRANSIT_CHANGE;
import android.os.IBinder;
@@ -34,11 +32,19 @@ import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback;
import com.android.wm.shell.transition.Transitions.TransitionHandler;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
+import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
+import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
+import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
+/**
+ * Transition handler that is responsible for animating app surfaces when unfolding of foldable
+ * devices. It does not handle the folding animation, which is done in
+ * {@link UnfoldAnimationController}.
+ */
public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListener {
private final ShellUnfoldProgressProvider mUnfoldProgressProvider;
@@ -51,17 +57,26 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
@Nullable
private IBinder mTransition;
- private final List<TransitionInfo.Change> mAnimatedFullscreenTasks = new ArrayList<>();
+ private final List<UnfoldTaskAnimator> mAnimators = new ArrayList<>();
public UnfoldTransitionHandler(ShellUnfoldProgressProvider unfoldProgressProvider,
- TransactionPool transactionPool, Executor executor, Transitions transitions) {
+ FullscreenUnfoldTaskAnimator fullscreenUnfoldAnimator,
+ SplitTaskUnfoldAnimator splitUnfoldTaskAnimator,
+ TransactionPool transactionPool,
+ Executor executor, Transitions transitions) {
mUnfoldProgressProvider = unfoldProgressProvider;
mTransactionPool = transactionPool;
mExecutor = executor;
mTransitions = transitions;
+
+ mAnimators.add(splitUnfoldTaskAnimator);
+ mAnimators.add(fullscreenUnfoldAnimator);
}
public void init() {
+ for (int i = 0; i < mAnimators.size(); i++) {
+ mAnimators.get(i).init();
+ }
mTransitions.addHandler(this);
mUnfoldProgressProvider.addListener(mExecutor, this);
}
@@ -71,49 +86,69 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull TransitionFinishCallback finishCallback) {
-
if (transition != mTransition) return false;
- startTransaction.apply();
-
- mAnimatedFullscreenTasks.clear();
- info.getChanges().forEach(change -> {
- final boolean allowedToAnimate = change.getTaskInfo() != null
- && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
- && change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME
- && change.getMode() == TRANSIT_CHANGE;
-
- if (allowedToAnimate) {
- mAnimatedFullscreenTasks.add(change);
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ animator.clearTasks();
+
+ info.getChanges().forEach(change -> {
+ if (change.getTaskInfo() != null
+ && change.getMode() == TRANSIT_CHANGE
+ && animator.isApplicableTask(change.getTaskInfo())) {
+ animator.onTaskAppeared(change.getTaskInfo(), change.getLeash());
+ }
+ });
+
+ if (animator.hasActiveTasks()) {
+ animator.prepareStartTransaction(startTransaction);
+ animator.prepareFinishTransaction(finishTransaction);
+ animator.start();
}
- });
+ }
+ startTransaction.apply();
mFinishCallback = finishCallback;
- mTransition = null;
return true;
}
@Override
public void onStateChangeProgress(float progress) {
- mAnimatedFullscreenTasks.forEach(change -> {
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ if (mTransition == null) return;
+
+ SurfaceControl.Transaction transaction = null;
- // TODO: this is a placeholder animation, replace with a spec version in the next CLs
- final float testScale = 0.8f + 0.2f * progress;
- transaction.setScale(change.getLeash(), testScale, testScale);
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ if (animator.hasActiveTasks()) {
+ if (transaction == null) {
+ transaction = mTransactionPool.acquire();
+ }
+
+ animator.applyAnimationProgress(progress, transaction);
+ }
+ }
+
+ if (transaction != null) {
transaction.apply();
mTransactionPool.release(transaction);
- });
+ }
}
@Override
public void onStateChangeFinished() {
- if (mFinishCallback != null) {
- mFinishCallback.onTransitionFinished(null, null);
- mFinishCallback = null;
- mAnimatedFullscreenTasks.clear();
+ if (mFinishCallback == null) return;
+
+ for (int i = 0; i < mAnimators.size(); i++) {
+ final UnfoldTaskAnimator animator = mAnimators.get(i);
+ animator.clearTasks();
+ animator.stop();
}
+
+ mFinishCallback.onTransitionFinished(null, null);
+ mFinishCallback = null;
+ mTransition = null;
}
@Nullable
@@ -127,4 +162,8 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
}
return null;
}
+
+ public boolean willHandleTransition() {
+ return mTransition != null;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java
index aa3868cfca84..eab82f00e962 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.android.wm.shell.fullscreen;
+package com.android.wm.shell.unfold.animation;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.util.MathUtils.lerp;
import static android.view.Display.DEFAULT_DISPLAY;
import android.animation.RectEvaluator;
import android.animation.TypeEvaluator;
import android.annotation.NonNull;
-import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Matrix;
@@ -32,22 +32,26 @@ import android.util.SparseArray;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.wm.shell.common.DisplayInsetsController;
-import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
-import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
-import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
import com.android.wm.shell.unfold.UnfoldBackgroundController;
-import java.util.concurrent.Executor;
-
/**
- * Controls full screen app unfold transition: animating cropping window and scaling when
- * folding or unfolding a foldable device.
+ * This helper class contains logic that calculates scaling and cropping parameters
+ * for the folding/unfolding animation. As an input it receives TaskInfo objects and
+ * surfaces leashes and as an output it could fill surface transactions with required
+ * transformations.
+ *
+ * This class is used by
+ * {@link com.android.wm.shell.unfold.UnfoldTransitionHandler} and
+ * {@link UnfoldAnimationController}. They use independent
+ * instances of FullscreenUnfoldTaskAnimator.
*/
-public final class FullscreenUnfoldController implements UnfoldListener,
- OnInsetsChangedListener {
+public class FullscreenUnfoldTaskAnimator implements UnfoldTaskAnimator,
+ DisplayInsetsController.OnInsetsChangedListener {
private static final float[] FLOAT_9 = new float[9];
private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect());
@@ -57,49 +61,77 @@ public final class FullscreenUnfoldController implements UnfoldListener,
private static final float END_SCALE = 1f;
private static final float START_SCALE = END_SCALE - VERTICAL_START_MARGIN * 2;
- private final Executor mExecutor;
- private final ShellUnfoldProgressProvider mProgressProvider;
- private final DisplayInsetsController mDisplayInsetsController;
-
private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>();
+ private final int mExpandedTaskBarHeight;
+ private final float mWindowCornerRadiusPx;
+ private final DisplayInsetsController mDisplayInsetsController;
private final UnfoldBackgroundController mBackgroundController;
private InsetsSource mTaskbarInsetsSource;
- private final float mWindowCornerRadiusPx;
- private final float mExpandedTaskBarHeight;
-
- private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
-
- public FullscreenUnfoldController(
- @NonNull Context context,
- @NonNull Executor executor,
+ public FullscreenUnfoldTaskAnimator(Context context,
@NonNull UnfoldBackgroundController backgroundController,
- @NonNull ShellUnfoldProgressProvider progressProvider,
- @NonNull DisplayInsetsController displayInsetsController
- ) {
- mExecutor = executor;
- mProgressProvider = progressProvider;
+ DisplayInsetsController displayInsetsController) {
mDisplayInsetsController = displayInsetsController;
- mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
+ mBackgroundController = backgroundController;
mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.taskbar_frame_height);
- mBackgroundController = backgroundController;
+ mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
}
- /**
- * Initializes the controller
- */
public void init() {
- mProgressProvider.addListener(mExecutor, this);
mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this);
}
@Override
- public void onStateChangeProgress(float progress) {
- if (mAnimationContextByTaskId.size() == 0) return;
+ public void insetsChanged(InsetsState insetsState) {
+ mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+ context.update(mTaskbarInsetsSource, context.mTaskInfo);
+ }
+ }
+
+ public boolean hasActiveTasks() {
+ return mAnimationContextByTaskId.size() > 0;
+ }
+
+ @Override
+ public void onTaskAppeared(TaskInfo taskInfo, SurfaceControl leash) {
+ AnimationContext animationContext = new AnimationContext(leash, mTaskbarInsetsSource,
+ taskInfo);
+ mAnimationContextByTaskId.put(taskInfo.taskId, animationContext);
+ }
+
+ @Override
+ public void onTaskChanged(TaskInfo taskInfo) {
+ AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId);
+ if (animationContext != null) {
+ animationContext.update(mTaskbarInsetsSource, taskInfo);
+ }
+ }
+
+ @Override
+ public void onTaskVanished(TaskInfo taskInfo) {
+ mAnimationContextByTaskId.remove(taskInfo.taskId);
+ }
- mBackgroundController.ensureBackground(mTransaction);
+ @Override
+ public void clearTasks() {
+ mAnimationContextByTaskId.clear();
+ }
+
+ @Override
+ public void resetSurface(TaskInfo taskInfo, Transaction transaction) {
+ final AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId);
+ if (context != null) {
+ resetSurface(context, transaction);
+ }
+ }
+
+ @Override
+ public void applyAnimationProgress(float progress, Transaction transaction) {
+ if (mAnimationContextByTaskId.size() == 0) return;
for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
@@ -111,75 +143,41 @@ public final class FullscreenUnfoldController implements UnfoldListener,
context.mMatrix.setScale(scale, scale, context.mCurrentCropRect.exactCenterX(),
context.mCurrentCropRect.exactCenterY());
- mTransaction.setWindowCrop(context.mLeash, context.mCurrentCropRect)
+ transaction.setWindowCrop(context.mLeash, context.mCurrentCropRect)
.setMatrix(context.mLeash, context.mMatrix, FLOAT_9)
- .setCornerRadius(context.mLeash, mWindowCornerRadiusPx);
+ .setCornerRadius(context.mLeash, mWindowCornerRadiusPx)
+ .show(context.mLeash);
}
-
- mTransaction.apply();
}
@Override
- public void onStateChangeFinished() {
- for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
- final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
- resetSurface(context);
- }
-
- mBackgroundController.removeBackground(mTransaction);
- mTransaction.apply();
+ public void prepareStartTransaction(Transaction transaction) {
+ mBackgroundController.ensureBackground(transaction);
}
@Override
- public void insetsChanged(InsetsState insetsState) {
- mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
- for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
- AnimationContext context = mAnimationContextByTaskId.valueAt(i);
- context.update(mTaskbarInsetsSource, context.mTaskInfo);
- }
+ public void prepareFinishTransaction(Transaction transaction) {
+ mBackgroundController.removeBackground(transaction);
}
- /**
- * Called when a new matching task appeared
- */
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- AnimationContext animationContext = new AnimationContext(leash, mTaskbarInsetsSource,
- taskInfo);
- mAnimationContextByTaskId.put(taskInfo.taskId, animationContext);
- }
-
- /**
- * Called when matching task changed
- */
- public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId);
- if (animationContext != null) {
- animationContext.update(mTaskbarInsetsSource, taskInfo);
- }
+ @Override
+ public boolean isApplicableTask(TaskInfo taskInfo) {
+ return taskInfo != null && taskInfo.isVisible()
+ && taskInfo.realActivity != null // to filter out parents created by organizer
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ && taskInfo.getActivityType() != ACTIVITY_TYPE_HOME;
}
- /**
- * Called when matching task vanished
- */
- public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId);
- if (animationContext != null) {
- // PiP task has its own cleanup path, ignore surface reset to avoid conflict.
- if (taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED) {
- resetSurface(animationContext);
- }
- mAnimationContextByTaskId.remove(taskInfo.taskId);
- }
-
- if (mAnimationContextByTaskId.size() == 0) {
- mBackgroundController.removeBackground(mTransaction);
+ @Override
+ public void resetAllSurfaces(Transaction transaction) {
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+ resetSurface(context, transaction);
}
-
- mTransaction.apply();
}
- private void resetSurface(AnimationContext context) {
- mTransaction
+ private void resetSurface(AnimationContext context, Transaction transaction) {
+ transaction
.setWindowCrop(context.mLeash, null)
.setCornerRadius(context.mLeash, 0.0F)
.setMatrix(context.mLeash, 1.0F, 0.0F, 0.0F, 1.0F)
@@ -197,10 +195,9 @@ public final class FullscreenUnfoldController implements UnfoldListener,
TaskInfo mTaskInfo;
- private AnimationContext(SurfaceControl leash,
- InsetsSource taskBarInsetsSource,
- TaskInfo taskInfo) {
- this.mLeash = leash;
+ private AnimationContext(SurfaceControl leash, InsetsSource taskBarInsetsSource,
+ TaskInfo taskInfo) {
+ mLeash = leash;
update(taskBarInsetsSource, taskInfo);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
index 59eecb5db136..6e10ebe94c5d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,17 +14,19 @@
* limitations under the License.
*/
-package com.android.wm.shell.splitscreen;
+package com.android.wm.shell.unfold.animation;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import android.animation.RectEvaluator;
import android.animation.TypeEvaluator;
-import android.annotation.NonNull;
-import android.app.ActivityManager;
+import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -32,67 +34,130 @@ import android.util.SparseArray;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.wm.shell.common.DisplayInsetsController;
-import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
-import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
-import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
-import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
+import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
+import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
import com.android.wm.shell.unfold.UnfoldBackgroundController;
+import java.util.Optional;
import java.util.concurrent.Executor;
+import dagger.Lazy;
+
/**
- * Controls transformations of the split screen task surfaces in response
- * to the unfolding/folding action on foldable devices
+ * This helper class contains logic that calculates scaling and cropping parameters
+ * for the folding/unfolding animation. As an input it receives TaskInfo objects and
+ * surfaces leashes and as an output it could fill surface transactions with required
+ * transformations.
+ *
+ * This class is used by
+ * {@link com.android.wm.shell.unfold.UnfoldTransitionHandler} and
+ * {@link UnfoldAnimationController}.
+ * They use independent instances of SplitTaskUnfoldAnimator.
*/
-public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChangedListener {
+public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator,
+ DisplayInsetsController.OnInsetsChangedListener, SplitScreenListener {
private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect());
private static final float CROPPING_START_MARGIN_FRACTION = 0.05f;
- private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>();
- private final ShellUnfoldProgressProvider mUnfoldProgressProvider;
- private final DisplayInsetsController mDisplayInsetsController;
- private final UnfoldBackgroundController mBackgroundController;
private final Executor mExecutor;
+ private final DisplayInsetsController mDisplayInsetsController;
+ private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>();
private final int mExpandedTaskBarHeight;
private final float mWindowCornerRadiusPx;
- private final Rect mStageBounds = new Rect();
- private final TransactionPool mTransactionPool;
+ private final Lazy<Optional<SplitScreenController>> mSplitScreenController;
+ private final UnfoldBackgroundController mUnfoldBackgroundController;
+
+ private final Rect mMainStageBounds = new Rect();
+ private final Rect mSideStageBounds = new Rect();
+ private final Rect mRootStageBounds = new Rect();
private InsetsSource mTaskbarInsetsSource;
- private boolean mBothStagesVisible;
-
- public StageTaskUnfoldController(@NonNull Context context,
- @NonNull TransactionPool transactionPool,
- @NonNull ShellUnfoldProgressProvider unfoldProgressProvider,
- @NonNull DisplayInsetsController displayInsetsController,
- @NonNull UnfoldBackgroundController backgroundController,
- @NonNull Executor executor) {
- mUnfoldProgressProvider = unfoldProgressProvider;
- mTransactionPool = transactionPool;
- mExecutor = executor;
- mBackgroundController = backgroundController;
+
+ @SplitPosition
+ private int mMainStagePosition = SPLIT_POSITION_UNDEFINED;
+ @SplitPosition
+ private int mSideStagePosition = SPLIT_POSITION_UNDEFINED;
+
+ public SplitTaskUnfoldAnimator(Context context, Executor executor,
+ Lazy<Optional<SplitScreenController>> splitScreenController,
+ UnfoldBackgroundController unfoldBackgroundController,
+ DisplayInsetsController displayInsetsController) {
mDisplayInsetsController = displayInsetsController;
- mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
+ mExecutor = executor;
+ mUnfoldBackgroundController = unfoldBackgroundController;
+ mSplitScreenController = splitScreenController;
mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.taskbar_frame_height);
+ mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
}
- /**
- * Initializes the controller, starts listening for the external events
- */
+ /** Initializes the animator, this should be called only once */
+ @Override
public void init() {
- mUnfoldProgressProvider.addListener(mExecutor, this);
mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this);
}
+ /**
+ * Starts listening for split-screen changes and gets initial split-screen
+ * layout information through the listener
+ */
+ @Override
+ public void start() {
+ mSplitScreenController.get().get().asSplitScreen()
+ .registerSplitScreenListener(this, mExecutor);
+ }
+
+ /**
+ * Stops listening for the split-screen layout changes
+ */
+ @Override
+ public void stop() {
+ mSplitScreenController.get().get().asSplitScreen()
+ .unregisterSplitScreenListener(this);
+ }
+
@Override
public void insetsChanged(InsetsState insetsState) {
mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+ updateContexts();
+ }
+
+ @Override
+ public void onTaskStageChanged(int taskId, int stage, boolean visible) {
+ final AnimationContext context = mAnimationContextByTaskId.get(taskId);
+ if (context != null) {
+ context.mStageType = stage;
+ context.update();
+ }
+ }
+
+ @Override
+ public void onStagePositionChanged(int stage, int position) {
+ if (stage == STAGE_TYPE_MAIN) {
+ mMainStagePosition = position;
+ } else {
+ mSideStagePosition = position;
+ }
+ updateContexts();
+ }
+
+ @Override
+ public void onSplitBoundsChanged(Rect rootBounds, Rect mainBounds, Rect sideBounds) {
+ mRootStageBounds.set(rootBounds);
+ mMainStageBounds.set(mainBounds);
+ mSideStageBounds.set(sideBounds);
+ updateContexts();
+ }
+
+ private void updateContexts() {
for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
AnimationContext context = mAnimationContextByTaskId.valueAt(i);
context.update();
@@ -100,44 +165,73 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
}
/**
- * Called when split screen task appeared
- * @param taskInfo info for the appeared task
- * @param leash surface leash for the appeared task
+ * Register a split task in the animator
+ * @param taskInfo info of the task
+ * @param leash the surface of the task
*/
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- // Only handle child task surface here.
- if (!taskInfo.hasParentTask()) return;
-
+ @Override
+ public void onTaskAppeared(TaskInfo taskInfo, SurfaceControl leash) {
AnimationContext context = new AnimationContext(leash);
mAnimationContextByTaskId.put(taskInfo.taskId, context);
}
/**
- * Called when a split screen task vanished
- * @param taskInfo info for the vanished task
+ * Unregister the task from the unfold animation
+ * @param taskInfo info of the task
+ */
+ @Override
+ public void onTaskVanished(TaskInfo taskInfo) {
+ mAnimationContextByTaskId.remove(taskInfo.taskId);
+ }
+
+ @Override
+ public boolean isApplicableTask(TaskInfo taskInfo) {
+ return taskInfo.hasParentTask()
+ && taskInfo.isVisible
+ && taskInfo.realActivity != null // to filter out parents created by organizer
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW;
+ }
+
+ /**
+ * Clear all registered tasks
*/
- public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- if (!taskInfo.hasParentTask()) return;
+ @Override
+ public void clearTasks() {
+ mAnimationContextByTaskId.clear();
+ }
+ /**
+ * Reset transformations of the task that could have been applied by the animator
+ * @param taskInfo task to reset
+ * @param transaction a transaction to write the changes to
+ */
+ @Override
+ public void resetSurface(TaskInfo taskInfo, Transaction transaction) {
AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId);
if (context != null) {
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
resetSurface(transaction, context);
- transaction.apply();
- mTransactionPool.release(transaction);
}
- mAnimationContextByTaskId.remove(taskInfo.taskId);
}
+ /**
+ * Reset all surface transformation that could have been introduced by the animator
+ * @param transaction to write changes to
+ */
@Override
- public void onStateChangeProgress(float progress) {
- if (mAnimationContextByTaskId.size() == 0 || !mBothStagesVisible) return;
-
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- mBackgroundController.ensureBackground(transaction);
+ public void resetAllSurfaces(Transaction transaction) {
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+ resetSurface(transaction, context);
+ }
+ }
+ @Override
+ public void applyAnimationProgress(float progress, Transaction transaction) {
for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+ if (context.mStageType == STAGE_TYPE_UNDEFINED) {
+ continue;
+ }
context.mCurrentCropRect.set(RECT_EVALUATOR
.evaluate(progress, context.mStartCropRect, context.mEndCropRect));
@@ -145,53 +239,25 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
transaction.setWindowCrop(context.mLeash, context.mCurrentCropRect)
.setCornerRadius(context.mLeash, mWindowCornerRadiusPx);
}
-
- transaction.apply();
-
- mTransactionPool.release(transaction);
}
@Override
- public void onStateChangeFinished() {
- resetTransformations();
+ public void prepareStartTransaction(Transaction transaction) {
+ mUnfoldBackgroundController.ensureBackground(transaction);
+ mSplitScreenController.get().get().updateSplitScreenSurfaces(transaction);
}
- /**
- * Called when split screen visibility changes
- * @param bothStagesVisible true if both stages of the split screen are visible
- */
- public void onSplitVisibilityChanged(boolean bothStagesVisible) {
- mBothStagesVisible = bothStagesVisible;
- if (!bothStagesVisible) {
- resetTransformations();
- }
+ @Override
+ public void prepareFinishTransaction(Transaction transaction) {
+ mUnfoldBackgroundController.removeBackground(transaction);
}
/**
- * Called when split screen stage bounds changed
- * @param bounds new bounds for this stage
+ * @return true if there are tasks to animate
*/
- public void onLayoutChanged(Rect bounds, @SplitPosition int splitPosition,
- boolean isLandscape) {
- mStageBounds.set(bounds);
-
- for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
- final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
- context.update(splitPosition, isLandscape);
- }
- }
-
- private void resetTransformations() {
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
-
- for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
- final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
- resetSurface(transaction, context);
- }
- mBackgroundController.removeBackground(transaction);
- transaction.apply();
-
- mTransactionPool.release(transaction);
+ @Override
+ public boolean hasActiveTasks() {
+ return mAnimationContextByTaskId.size() > 0;
}
private void resetSurface(SurfaceControl.Transaction transaction, AnimationContext context) {
@@ -202,26 +268,24 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
private class AnimationContext {
final SurfaceControl mLeash;
+
final Rect mStartCropRect = new Rect();
final Rect mEndCropRect = new Rect();
final Rect mCurrentCropRect = new Rect();
- private @SplitPosition int mSplitPosition = SPLIT_POSITION_UNDEFINED;
- private boolean mIsLandscape = false;
+ @SplitScreen.StageType
+ int mStageType = STAGE_TYPE_UNDEFINED;
private AnimationContext(SurfaceControl leash) {
- this.mLeash = leash;
- update();
- }
-
- private void update(@SplitPosition int splitPosition, boolean isLandscape) {
- this.mSplitPosition = splitPosition;
- this.mIsLandscape = isLandscape;
+ mLeash = leash;
update();
}
private void update() {
- mStartCropRect.set(mStageBounds);
+ final Rect stageBounds = mStageType == STAGE_TYPE_MAIN
+ ? mMainStageBounds : mSideStageBounds;
+
+ mStartCropRect.set(stageBounds);
boolean taskbarExpanded = isTaskbarExpanded();
if (taskbarExpanded) {
@@ -239,7 +303,8 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
// Sides adjacent to split bar or task bar are not be animated.
Insets margins;
- if (mIsLandscape) { // Left and right splits.
+ final boolean isLandscape = mRootStageBounds.width() > mRootStageBounds.height();
+ if (isLandscape) { // Left and right splits.
margins = getLandscapeMargins(margin, taskbarExpanded);
} else { // Top and bottom splits.
margins = getPortraitMargins(margin, taskbarExpanded);
@@ -251,7 +316,9 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
int left = margin;
int right = margin;
int bottom = taskbarExpanded ? 0 : margin; // Taskbar margin.
- if (mSplitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
+ final int splitPosition = mStageType == STAGE_TYPE_MAIN
+ ? mMainStagePosition : mSideStagePosition;
+ if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
right = 0; // Divider margin.
} else {
left = 0; // Divider margin.
@@ -262,7 +329,9 @@ public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChange
private Insets getPortraitMargins(int margin, boolean taskbarExpanded) {
int bottom = margin;
int top = margin;
- if (mSplitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
+ final int splitPosition = mStageType == STAGE_TYPE_MAIN
+ ? mMainStagePosition : mSideStagePosition;
+ if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
bottom = 0; // Divider margin.
} else { // Bottom split.
top = 0; // Divider margin.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/UnfoldTaskAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/UnfoldTaskAnimator.java
new file mode 100644
index 000000000000..e1e366301b46
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/UnfoldTaskAnimator.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.unfold.animation;
+
+import android.app.TaskInfo;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+/**
+ * Interface for classes that handle animations of tasks when folding or unfolding
+ * foldable devices.
+ */
+public interface UnfoldTaskAnimator {
+ /**
+ * Initializes the animator, this should be called once in the lifetime of the animator
+ */
+ default void init() {}
+
+ /**
+ * Starts the animator, it might start listening for some events from the system.
+ * Applying animation should be done only when animator is started.
+ * Animator could be started/stopped several times.
+ */
+ default void start() {}
+
+ /**
+ * Stops the animator, it could unsubscribe from system events.
+ */
+ default void stop() {}
+
+ /**
+ * If this method returns true then task updates will be propagated to
+ * the animator using the onTaskAppeared/Changed/Vanished callbacks.
+ * @return true if this task should be animated by this animator
+ */
+ default boolean isApplicableTask(TaskInfo taskInfo) {
+ return false;
+ }
+
+ /**
+ * Called whenever a task applicable to this animator appeared
+ * (isApplicableTask returns true for this task)
+ *
+ * @param taskInfo info of the appeared task
+ * @param leash surface of the task
+ */
+ default void onTaskAppeared(TaskInfo taskInfo, SurfaceControl leash) {}
+
+ /**
+ * Called whenever a task applicable to this animator changed
+ * @param taskInfo info of the changed task
+ */
+ default void onTaskChanged(TaskInfo taskInfo) {}
+
+ /**
+ * Called whenever a task applicable to this animator vanished
+ * @param taskInfo info of the vanished task
+ */
+ default void onTaskVanished(TaskInfo taskInfo) {}
+
+ /**
+ * @return true if there tasks that could be potentially animated
+ */
+ default boolean hasActiveTasks() {
+ return false;
+ }
+
+ /**
+ * Clears all registered tasks in the animator
+ */
+ default void clearTasks() {}
+
+ /**
+ * Apply task surfaces transformations based on the current unfold progress
+ * @param progress unfold transition progress
+ * @param transaction to write changes to
+ */
+ default void applyAnimationProgress(float progress, Transaction transaction) {}
+
+ /**
+ * Apply task surfaces transformations that should be set before starting the animation
+ * @param transaction to write changes to
+ */
+ default void prepareStartTransaction(Transaction transaction) {}
+
+ /**
+ * Apply task surfaces transformations that should be set after finishing the animation
+ * @param transaction to write changes to
+ */
+ default void prepareFinishTransaction(Transaction transaction) {}
+
+ /**
+ * Resets task surface to its initial transformation
+ * @param transaction to write changes to
+ */
+ default void resetSurface(TaskInfo taskInfo, Transaction transaction) {}
+
+ /**
+ * Resets all task surfaces to their initial transformations
+ * @param transaction to write changes to
+ */
+ default void resetAllSurfaces(Transaction transaction) {}
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldShellTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldShellTransition.java
new file mode 100644
index 000000000000..4c868305dcdb
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldShellTransition.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.unfold.qualifier;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/**
+ * Indicates that this class is used for the shell unfold transition
+ */
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface UnfoldShellTransition {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldTransition.java
new file mode 100644
index 000000000000..4d2b3e6f899b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/qualifier/UnfoldTransition.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.unfold.qualifier;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/**
+ * Indicates that this class is used for unfold transition implemented
+ * without using Shell transitions
+ */
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+public @interface UnfoldTransition {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
index 603d05d78fc0..2cff1714aff6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
@@ -30,7 +30,7 @@ import androidx.annotation.Nullable;
public class GroupedRecentTaskInfo implements Parcelable {
public @NonNull ActivityManager.RecentTaskInfo mTaskInfo1;
public @Nullable ActivityManager.RecentTaskInfo mTaskInfo2;
- public @Nullable StagedSplitBounds mStagedSplitBounds;
+ public @Nullable SplitBounds mSplitBounds;
public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1) {
this(task1, null, null);
@@ -38,24 +38,24 @@ public class GroupedRecentTaskInfo implements Parcelable {
public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1,
@Nullable ActivityManager.RecentTaskInfo task2,
- @Nullable StagedSplitBounds stagedSplitBounds) {
+ @Nullable SplitBounds splitBounds) {
mTaskInfo1 = task1;
mTaskInfo2 = task2;
- mStagedSplitBounds = stagedSplitBounds;
+ mSplitBounds = splitBounds;
}
GroupedRecentTaskInfo(Parcel parcel) {
mTaskInfo1 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
mTaskInfo2 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
- mStagedSplitBounds = parcel.readTypedObject(StagedSplitBounds.CREATOR);
+ mSplitBounds = parcel.readTypedObject(SplitBounds.CREATOR);
}
@Override
public String toString() {
String taskString = "Task1: " + getTaskInfo(mTaskInfo1)
+ ", Task2: " + getTaskInfo(mTaskInfo2);
- if (mStagedSplitBounds != null) {
- taskString += ", SplitBounds: " + mStagedSplitBounds.toString();
+ if (mSplitBounds != null) {
+ taskString += ", SplitBounds: " + mSplitBounds.toString();
}
return taskString;
}
@@ -76,7 +76,7 @@ public class GroupedRecentTaskInfo implements Parcelable {
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeTypedObject(mTaskInfo1, flags);
parcel.writeTypedObject(mTaskInfo2, flags);
- parcel.writeTypedObject(mStagedSplitBounds, flags);
+ parcel.writeTypedObject(mSplitBounds, flags);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
index a0c84cc33ebd..e90389764af3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/StagedSplitBounds.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
@@ -25,7 +25,7 @@ import java.util.Objects;
* Container of various information needed to display split screen
* tasks/leashes/etc in Launcher
*/
-public class StagedSplitBounds implements Parcelable {
+public class SplitBounds implements Parcelable {
public final Rect leftTopBounds;
public final Rect rightBottomBounds;
/** This rect represents the actual gap between the two apps */
@@ -43,7 +43,7 @@ public class StagedSplitBounds implements Parcelable {
public final int leftTopTaskId;
public final int rightBottomTaskId;
- public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds,
+ public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds,
int leftTopTaskId, int rightBottomTaskId) {
this.leftTopBounds = leftTopBounds;
this.rightBottomBounds = rightBottomBounds;
@@ -66,7 +66,7 @@ public class StagedSplitBounds implements Parcelable {
topTaskPercent = this.leftTopBounds.height() / (float) rightBottomBounds.bottom;
}
- public StagedSplitBounds(Parcel parcel) {
+ public SplitBounds(Parcel parcel) {
leftTopBounds = parcel.readTypedObject(Rect.CREATOR);
rightBottomBounds = parcel.readTypedObject(Rect.CREATOR);
visualDividerBounds = parcel.readTypedObject(Rect.CREATOR);
@@ -96,11 +96,11 @@ public class StagedSplitBounds implements Parcelable {
@Override
public boolean equals(Object obj) {
- if (!(obj instanceof StagedSplitBounds)) {
+ if (!(obj instanceof SplitBounds)) {
return false;
}
// Only need to check the base fields (the other fields are derived from these)
- final StagedSplitBounds other = (StagedSplitBounds) obj;
+ final SplitBounds other = (SplitBounds) obj;
return Objects.equals(leftTopBounds, other.leftTopBounds)
&& Objects.equals(rightBottomBounds, other.rightBottomBounds)
&& leftTopTaskId == other.leftTopTaskId
@@ -120,15 +120,15 @@ public class StagedSplitBounds implements Parcelable {
+ "AppsVertical? " + appsStackedVertically;
}
- public static final Creator<StagedSplitBounds> CREATOR = new Creator<StagedSplitBounds>() {
+ public static final Creator<SplitBounds> CREATOR = new Creator<SplitBounds>() {
@Override
- public StagedSplitBounds createFromParcel(Parcel in) {
- return new StagedSplitBounds(in);
+ public SplitBounds createFromParcel(Parcel in) {
+ return new SplitBounds(in);
}
@Override
- public StagedSplitBounds[] newArray(int size) {
- return new StagedSplitBounds[size];
+ public SplitBounds[] newArray(int size) {
+ return new SplitBounds[size];
}
};
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
new file mode 100644
index 000000000000..6d28d73996f0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.windowdecor;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
+import android.content.Context;
+import android.os.Handler;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+/**
+ * View model for the window decoration with a caption and shadows. Works with
+ * {@link CaptionWindowDecoration}.
+ */
+public class CaptionWindowDecorViewModel implements WindowDecorViewModel<CaptionWindowDecoration> {
+ private final ActivityTaskManager mActivityTaskManager;
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final Context mContext;
+ private final Handler mMainHandler;
+ private final DisplayController mDisplayController;
+ private final SyncTransactionQueue mSyncQueue;
+
+ public CaptionWindowDecorViewModel(
+ Context context,
+ Handler mainHandler,
+ ShellTaskOrganizer taskOrganizer,
+ DisplayController displayController,
+ SyncTransactionQueue syncQueue) {
+ mContext = context;
+ mMainHandler = mainHandler;
+ mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
+ mTaskOrganizer = taskOrganizer;
+ mDisplayController = displayController;
+ mSyncQueue = syncQueue;
+ }
+
+ @Override
+ public CaptionWindowDecoration createWindowDecoration(
+ ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface) {
+ final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ taskSurface,
+ mMainHandler,
+ mSyncQueue);
+ TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration);
+ CaptionTouchEventListener touchEventListener =
+ new CaptionTouchEventListener(taskInfo, taskPositioner);
+ windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
+ windowDecoration.setDragResizeCallback(taskPositioner);
+ onTaskInfoChanged(taskInfo, windowDecoration);
+ return windowDecoration;
+ }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo, CaptionWindowDecoration decoration) {
+ decoration.relayout(taskInfo);
+
+ int statusBarColor = taskInfo.taskDescription.getStatusBarColor();
+ decoration.setCaptionColor(statusBarColor);
+ }
+
+ private class CaptionTouchEventListener implements
+ View.OnClickListener, View.OnTouchListener {
+
+ private final int mTaskId;
+ private final WindowContainerToken mTaskToken;
+ private final DragResizeCallback mDragResizeCallback;
+
+ private int mDragPointerId = -1;
+
+ private CaptionTouchEventListener(
+ RunningTaskInfo taskInfo, DragResizeCallback dragResizeCallback) {
+ mTaskId = taskInfo.taskId;
+ mTaskToken = taskInfo.token;
+ mDragResizeCallback = dragResizeCallback;
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.close_window) {
+ mActivityTaskManager.removeTask(mTaskId);
+ } else if (id == R.id.maximize_window) {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+ int targetWindowingMode = taskInfo.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
+ ? WINDOWING_MODE_FULLSCREEN : WINDOWING_MODE_FREEFORM;
+ int displayWindowingMode =
+ taskInfo.configuration.windowConfiguration.getDisplayWindowingMode();
+ wct.setWindowingMode(mTaskToken,
+ targetWindowingMode == displayWindowingMode
+ ? WINDOWING_MODE_UNDEFINED : targetWindowingMode);
+ if (targetWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ wct.setBounds(mTaskToken, null);
+ }
+ mSyncQueue.queue(wct);
+ }
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent e) {
+ if (v.getId() != R.id.caption) {
+ return false;
+ }
+ handleEventForMove(e);
+
+ if (e.getAction() != MotionEvent.ACTION_DOWN) {
+ return false;
+ }
+ RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+ if (taskInfo.isFocused) {
+ return false;
+ }
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reorder(mTaskToken, true /* onTop */);
+ mSyncQueue.queue(wct);
+ return true;
+ }
+
+ private void handleEventForMove(MotionEvent e) {
+ switch (e.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mDragPointerId = e.getPointerId(0);
+ mDragResizeCallback.onDragResizeStart(
+ 0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
+ break;
+ case MotionEvent.ACTION_MOVE: {
+ int dragPointerIdx = e.findPointerIndex(mDragPointerId);
+ mDragResizeCallback.onDragResizeMove(
+ e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
+ break;
+ }
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL: {
+ int dragPointerIdx = e.findPointerIndex(mDragPointerId);
+ mDragResizeCallback.onDragResizeEnd(
+ e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
new file mode 100644
index 000000000000..cdca051a4ee5
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.windowdecor;
+
+import android.app.ActivityManager;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.VectorDrawable;
+import android.os.Handler;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+/**
+ * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
+ * {@link CaptionWindowDecorViewModel}. The caption bar contains maximize and close buttons.
+ *
+ * {@link CaptionWindowDecorViewModel} can change the color of the caption bar based on the foremost
+ * app's request through {@link #setCaptionColor(int)}, in which it changes the foreground color of
+ * caption buttons according to the luminance of the background.
+ *
+ * The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't.
+ */
+public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> {
+ // The thickness of shadows of a window that has focus in DIP.
+ private static final int DECOR_SHADOW_FOCUSED_THICKNESS_IN_DIP = 20;
+ // The thickness of shadows of a window that doesn't have focus in DIP.
+ private static final int DECOR_SHADOW_UNFOCUSED_THICKNESS_IN_DIP = 5;
+
+ // Height of button (32dp) + 2 * margin (5dp each)
+ private static final int DECOR_CAPTION_HEIGHT_IN_DIP = 42;
+ private static final int RESIZE_HANDLE_IN_DIP = 30;
+
+ private static final Rect EMPTY_OUTSET = new Rect();
+ private static final Rect RESIZE_HANDLE_OUTSET = new Rect(
+ RESIZE_HANDLE_IN_DIP, RESIZE_HANDLE_IN_DIP, RESIZE_HANDLE_IN_DIP, RESIZE_HANDLE_IN_DIP);
+
+ private final Handler mHandler;
+ private final SyncTransactionQueue mSyncQueue;
+
+ private View.OnClickListener mOnCaptionButtonClickListener;
+ private View.OnTouchListener mOnCaptionTouchListener;
+ private DragResizeCallback mDragResizeCallback;
+
+ private DragResizeInputListener mDragResizeListener;
+
+ private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
+ new WindowDecoration.RelayoutResult<>();
+
+ CaptionWindowDecoration(
+ Context context,
+ DisplayController displayController,
+ ShellTaskOrganizer taskOrganizer,
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ Handler handler,
+ SyncTransactionQueue syncQueue) {
+ super(context, displayController, taskOrganizer, taskInfo, taskSurface);
+
+ mHandler = handler;
+ mSyncQueue = syncQueue;
+ }
+
+ void setCaptionListeners(
+ View.OnClickListener onCaptionButtonClickListener,
+ View.OnTouchListener onCaptionTouchListener) {
+ mOnCaptionButtonClickListener = onCaptionButtonClickListener;
+ mOnCaptionTouchListener = onCaptionTouchListener;
+ }
+
+ void setDragResizeCallback(DragResizeCallback dragResizeCallback) {
+ mDragResizeCallback = dragResizeCallback;
+ }
+
+ @Override
+ void relayout(ActivityManager.RunningTaskInfo taskInfo) {
+ final int shadowRadiusDp = taskInfo.isFocused
+ ? DECOR_SHADOW_FOCUSED_THICKNESS_IN_DIP : DECOR_SHADOW_UNFOCUSED_THICKNESS_IN_DIP;
+ final boolean isFreeform = mTaskInfo.configuration.windowConfiguration.getWindowingMode()
+ == WindowConfiguration.WINDOWING_MODE_FREEFORM;
+ final boolean isDragResizeable = isFreeform && mTaskInfo.isResizeable;
+ final Rect outset = isDragResizeable ? RESIZE_HANDLE_OUTSET : EMPTY_OUTSET;
+
+ WindowDecorLinearLayout oldRootView = mResult.mRootView;
+ final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ relayout(taskInfo, R.layout.caption_window_decoration, oldRootView,
+ DECOR_CAPTION_HEIGHT_IN_DIP, outset, shadowRadiusDp, t, wct, mResult);
+ taskInfo = null; // Clear it just in case we use it accidentally
+
+ mSyncQueue.runInSync(transaction -> {
+ transaction.merge(t);
+ t.close();
+
+ mTaskOrganizer.applyTransaction(wct);
+ });
+
+ if (mResult.mRootView == null) {
+ // This means something blocks the window decor from showing, e.g. the task is hidden.
+ // Nothing is set up in this case including the decoration surface.
+ return;
+ }
+ if (oldRootView != mResult.mRootView) {
+ setupRootView();
+ }
+
+ if (!isDragResizeable) {
+ closeDragResizeListener();
+ return;
+ }
+
+ if (oldDecorationSurface != mDecorationContainerSurface) {
+ closeDragResizeListener();
+ mDragResizeListener = new DragResizeInputListener(
+ mContext,
+ mHandler,
+ mDisplay.getDisplayId(),
+ mDecorationContainerSurface,
+ mDragResizeCallback);
+ }
+
+ mDragResizeListener.setGeometry(
+ mResult.mWidth, mResult.mHeight, (int) (mResult.mDensity * RESIZE_HANDLE_IN_DIP));
+ }
+
+ /**
+ * Sets up listeners when a new root view is created.
+ */
+ private void setupRootView() {
+ View caption = mResult.mRootView.findViewById(R.id.caption);
+
+ caption.setOnTouchListener(mOnCaptionTouchListener);
+ View maximize = caption.findViewById(R.id.maximize_window);
+ maximize.setOnClickListener(mOnCaptionButtonClickListener);
+ View close = caption.findViewById(R.id.close_window);
+ close.setOnClickListener(mOnCaptionButtonClickListener);
+ }
+
+ void setCaptionColor(int captionColor) {
+ if (mResult.mRootView == null) {
+ return;
+ }
+
+ View caption = mResult.mRootView.findViewById(R.id.caption);
+ GradientDrawable captionDrawable = (GradientDrawable) caption.getBackground();
+ captionDrawable.setColor(captionColor);
+
+ int buttonTintColorRes =
+ Color.valueOf(captionColor).luminance() < 0.5
+ ? R.color.decor_button_light_color
+ : R.color.decor_button_dark_color;
+ ColorStateList buttonTintColor =
+ caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */);
+ View maximize = caption.findViewById(R.id.maximize_window);
+ VectorDrawable maximizeBackground = (VectorDrawable) maximize.getBackground();
+ maximizeBackground.setTintList(buttonTintColor);
+
+ View close = caption.findViewById(R.id.close_window);
+ VectorDrawable closeBackground = (VectorDrawable) close.getBackground();
+ closeBackground.setTintList(buttonTintColor);
+ }
+
+ private void closeDragResizeListener() {
+ if (mDragResizeListener == null) {
+ return;
+ }
+ mDragResizeListener.close();
+ mDragResizeListener = null;
+ }
+
+ @Override
+ public void close() {
+ closeDragResizeListener();
+ super.close();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java
new file mode 100644
index 000000000000..ee160a15df19
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeCallback.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.windowdecor;
+
+/**
+ * Callback called when receiving drag-resize or drag-move related input events.
+ */
+public interface DragResizeCallback {
+ /**
+ * Called when a drag resize starts.
+ *
+ * @param ctrlType {@link TaskPositioner.CtrlType} indicating the direction of resizing, use
+ * {@code 0} to indicate it's a move
+ * @param x x coordinate in window decoration coordinate system where the drag resize starts
+ * @param y y coordinate in window decoration coordinate system where the drag resize starts
+ */
+ void onDragResizeStart(@TaskPositioner.CtrlType int ctrlType, float x, float y);
+
+ /**
+ * Called when the pointer moves during a drag resize.
+ * @param x x coordinate in window decoration coordinate system of the new pointer location
+ * @param y y coordinate in window decoration coordinate system of the new pointer location
+ */
+ void onDragResizeMove(float x, float y);
+
+ /**
+ * Called when a drag resize stops.
+ * @param x x coordinate in window decoration coordinate system where the drag resize stops
+ * @param y y coordinate in window decoration coordinate system where the drag resize stops
+ */
+ void onDragResizeEnd(float x, float y);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
new file mode 100644
index 000000000000..c6bbb027c8e6
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.windowdecor;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.hardware.input.InputManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.view.IWindowSession;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.MotionEvent;
+import android.view.PointerIcon;
+import android.view.SurfaceControl;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.view.BaseIWindow;
+
+/**
+ * An input event listener registered to InputDispatcher to receive input events on task edges and
+ * convert them to drag resize requests.
+ */
+class DragResizeInputListener implements AutoCloseable {
+ private static final String TAG = "DragResizeInputListener";
+
+ private final IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession();
+ private final Handler mHandler;
+ private final InputManager mInputManager;
+
+ private final int mDisplayId;
+ private final BaseIWindow mFakeWindow;
+ private final IBinder mFocusGrantToken;
+ private final SurfaceControl mDecorationSurface;
+ private final InputChannel mInputChannel;
+ private final TaskResizeInputEventReceiver mInputEventReceiver;
+ private final com.android.wm.shell.windowdecor.DragResizeCallback mCallback;
+
+ private int mWidth;
+ private int mHeight;
+ private int mResizeHandleThickness;
+
+ private int mDragPointerId = -1;
+
+ DragResizeInputListener(
+ Context context,
+ Handler handler,
+ int displayId,
+ SurfaceControl decorationSurface,
+ DragResizeCallback callback) {
+ mInputManager = context.getSystemService(InputManager.class);
+ mHandler = handler;
+ mDisplayId = displayId;
+ mDecorationSurface = decorationSurface;
+ // Use a fake window as the backing surface is a container layer and we don't want to create
+ // a buffer layer for it so we can't use ViewRootImpl.
+ mFakeWindow = new BaseIWindow();
+ mFakeWindow.setSession(mWindowSession);
+ mFocusGrantToken = new Binder();
+ mInputChannel = new InputChannel();
+ try {
+ mWindowSession.grantInputChannel(
+ mDisplayId,
+ new SurfaceControl(mDecorationSurface, TAG),
+ mFakeWindow,
+ null /* hostInputToken */,
+ FLAG_NOT_FOCUSABLE,
+ PRIVATE_FLAG_TRUSTED_OVERLAY,
+ TYPE_APPLICATION,
+ mFocusGrantToken,
+ TAG + " of " + decorationSurface.toString(),
+ mInputChannel);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+
+ mInputEventReceiver = new TaskResizeInputEventReceiver(mInputChannel, mHandler.getLooper());
+ mCallback = callback;
+ }
+
+ /**
+ * Updates geometry of this drag resize handler. Needs to be called every time there is a size
+ * change to notify the input event receiver it's ready to take the next input event. Otherwise
+ * it'll keep batching move events and the drag resize process is stalled.
+ *
+ * This is also used to update the touch regions of this handler every event dispatched here is
+ * a potential resize request.
+ *
+ * @param width The width of the drag resize handler in pixels, including resize handle
+ * thickness. That is task width + 2 * resize handle thickness.
+ * @param height The height of the drag resize handler in pixels, including resize handle
+ * thickness. That is task height + 2 * resize handle thickness.
+ * @param resizeHandleThickness The thickness of the resize handle in pixels.
+ */
+ void setGeometry(int width, int height, int resizeHandleThickness) {
+ if (mWidth == width && mHeight == height
+ && mResizeHandleThickness == resizeHandleThickness) {
+ return;
+ }
+
+ mWidth = width;
+ mHeight = height;
+ mResizeHandleThickness = resizeHandleThickness;
+
+ Region touchRegion = new Region();
+ final Rect topInputBounds = new Rect(0, 0, mWidth, mResizeHandleThickness);
+ touchRegion.union(topInputBounds);
+
+ final Rect leftInputBounds = new Rect(0, mResizeHandleThickness,
+ mResizeHandleThickness, mHeight - mResizeHandleThickness);
+ touchRegion.union(leftInputBounds);
+
+ final Rect rightInputBounds = new Rect(
+ mWidth - mResizeHandleThickness, mResizeHandleThickness,
+ mWidth, mHeight - mResizeHandleThickness);
+ touchRegion.union(rightInputBounds);
+
+ final Rect bottomInputBounds = new Rect(0, mHeight - mResizeHandleThickness,
+ mWidth, mHeight);
+ touchRegion.union(bottomInputBounds);
+
+ try {
+ mWindowSession.updateInputChannel(
+ mInputChannel.getToken(),
+ mDisplayId,
+ new SurfaceControl(
+ mDecorationSurface, "DragResizeInputListener#setTouchRegion"),
+ FLAG_NOT_FOCUSABLE,
+ PRIVATE_FLAG_TRUSTED_OVERLAY,
+ touchRegion);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+
+ // This marks all relevant components have handled the previous resize event and can take
+ // the next one now.
+ mInputEventReceiver.onHandledLastResizeEvent();
+ }
+
+ @Override
+ public void close() {
+ mInputChannel.dispose();
+ try {
+ mWindowSession.remove(mFakeWindow);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ private class TaskResizeInputEventReceiver extends InputEventReceiver {
+ private boolean mWaitingForLastResizeEventHandled;
+
+ private TaskResizeInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ private void onHandledLastResizeEvent() {
+ mWaitingForLastResizeEventHandled = false;
+ consumeBatchedInputEvents(-1);
+ }
+
+ @Override
+ public void onBatchedInputEventPending(int source) {
+ // InputEventReceiver keeps continuous move events in a batched event until explicitly
+ // consuming it or an incompatible event shows up (likely an up event in this case). We
+ // continue to keep move events in the next batched event until we receive a geometry
+ // update so that we don't put too much pressure on the framework with excessive number
+ // of input events if it can't handle them fast enough. It's more responsive to always
+ // resize the task to the latest received coordinates.
+ if (!mWaitingForLastResizeEventHandled) {
+ consumeBatchedInputEvents(-1);
+ }
+ }
+
+ @Override
+ public void onInputEvent(InputEvent inputEvent) {
+ finishInputEvent(inputEvent, handleInputEvent(inputEvent));
+ }
+
+ private boolean handleInputEvent(InputEvent inputEvent) {
+ if (!(inputEvent instanceof MotionEvent)) {
+ return false;
+ }
+
+ MotionEvent e = (MotionEvent) inputEvent;
+ switch (e.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ mDragPointerId = e.getPointerId(0);
+ mCallback.onDragResizeStart(
+ calculateCtrlType(e.getX(0), e.getY(0)), e.getRawX(0), e.getRawY(0));
+ mWaitingForLastResizeEventHandled = false;
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ int dragPointerIndex = e.findPointerIndex(mDragPointerId);
+ mCallback.onDragResizeMove(
+ e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
+ mWaitingForLastResizeEventHandled = true;
+ break;
+ }
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL: {
+ int dragPointerIndex = e.findPointerIndex(mDragPointerId);
+ mCallback.onDragResizeEnd(
+ e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
+ mWaitingForLastResizeEventHandled = false;
+ mDragPointerId = -1;
+ break;
+ }
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_MOVE: {
+ updateCursorType(e.getXCursorPosition(), e.getYCursorPosition());
+ break;
+ }
+ case MotionEvent.ACTION_HOVER_EXIT:
+ mInputManager.setPointerIconType(PointerIcon.TYPE_DEFAULT);
+ break;
+ }
+ return true;
+ }
+
+ @TaskPositioner.CtrlType
+ private int calculateCtrlType(float x, float y) {
+ int ctrlType = 0;
+ if (x < mResizeHandleThickness) {
+ ctrlType |= TaskPositioner.CTRL_TYPE_LEFT;
+ }
+ if (x > mWidth - mResizeHandleThickness) {
+ ctrlType |= TaskPositioner.CTRL_TYPE_RIGHT;
+ }
+ if (y < mResizeHandleThickness) {
+ ctrlType |= TaskPositioner.CTRL_TYPE_TOP;
+ }
+ if (y > mHeight - mResizeHandleThickness) {
+ ctrlType |= TaskPositioner.CTRL_TYPE_BOTTOM;
+ }
+ return ctrlType;
+ }
+
+ private void updateCursorType(float x, float y) {
+ @TaskPositioner.CtrlType int ctrlType = calculateCtrlType(x, y);
+
+ int cursorType = PointerIcon.TYPE_DEFAULT;
+ switch (ctrlType) {
+ case TaskPositioner.CTRL_TYPE_LEFT:
+ case TaskPositioner.CTRL_TYPE_RIGHT:
+ cursorType = PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
+ break;
+ case TaskPositioner.CTRL_TYPE_TOP:
+ case TaskPositioner.CTRL_TYPE_BOTTOM:
+ cursorType = PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
+ break;
+ case TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_TOP:
+ case TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_BOTTOM:
+ cursorType = PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
+ break;
+ case TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_BOTTOM:
+ case TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_TOP:
+ cursorType = PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
+ break;
+ }
+ mInputManager.setPointerIconType(cursorType);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskFocusStateConsumer.java
index af2ab158ab46..1c61802bbd5c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskFocusStateConsumer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,12 +14,8 @@
* limitations under the License.
*/
-package com.android.wm.shell.legacysplitscreen;
+package com.android.wm.shell.windowdecor;
-/**
- * Class to hold state of divider that needs to persist across configuration changes.
- */
-final class DividerState {
- public boolean animateAfterRecentsDrawn;
- public float mRatioPositionBeforeMinimized;
+interface TaskFocusStateConsumer {
+ void setTaskFocusState(boolean focused);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
new file mode 100644
index 000000000000..280569b05d87
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.windowdecor;
+
+import android.annotation.IntDef;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+
+class TaskPositioner implements DragResizeCallback {
+
+ @IntDef({CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM})
+ @interface CtrlType {}
+
+ static final int CTRL_TYPE_LEFT = 1;
+ static final int CTRL_TYPE_RIGHT = 2;
+ static final int CTRL_TYPE_TOP = 4;
+ static final int CTRL_TYPE_BOTTOM = 8;
+
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final WindowDecoration mWindowDecoration;
+
+ private final Rect mTaskBoundsAtDragStart = new Rect();
+ private final PointF mResizeStartPoint = new PointF();
+ private final Rect mResizeTaskBounds = new Rect();
+
+ private int mCtrlType;
+
+ TaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration) {
+ mTaskOrganizer = taskOrganizer;
+ mWindowDecoration = windowDecoration;
+ }
+
+ @Override
+ public void onDragResizeStart(int ctrlType, float x, float y) {
+ mCtrlType = ctrlType;
+
+ mTaskBoundsAtDragStart.set(
+ mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
+ mResizeStartPoint.set(x, y);
+ }
+
+ @Override
+ public void onDragResizeMove(float x, float y) {
+ changeBounds(x, y);
+ }
+
+ @Override
+ public void onDragResizeEnd(float x, float y) {
+ changeBounds(x, y);
+
+ mCtrlType = 0;
+ mTaskBoundsAtDragStart.setEmpty();
+ mResizeStartPoint.set(0, 0);
+ }
+
+ private void changeBounds(float x, float y) {
+ float deltaX = x - mResizeStartPoint.x;
+ mResizeTaskBounds.set(mTaskBoundsAtDragStart);
+ if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
+ mResizeTaskBounds.left += deltaX;
+ }
+ if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) {
+ mResizeTaskBounds.right += deltaX;
+ }
+ float deltaY = y - mResizeStartPoint.y;
+ if ((mCtrlType & CTRL_TYPE_TOP) != 0) {
+ mResizeTaskBounds.top += deltaY;
+ }
+ if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) {
+ mResizeTaskBounds.bottom += deltaY;
+ }
+ if (mCtrlType == 0) {
+ mResizeTaskBounds.offset((int) deltaX, (int) deltaY);
+ }
+
+ if (!mResizeTaskBounds.isEmpty()) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setBounds(mWindowDecoration.mTaskInfo.token, mResizeTaskBounds);
+ mTaskOrganizer.applyTransaction(wct);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorLinearLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorLinearLayout.java
new file mode 100644
index 000000000000..6d8001a2f92b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorLinearLayout.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.windowdecor;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+
+/**
+ * A {@link LinearLayout} that takes an additional task focused drawable state. The new state is
+ * used to select the correct background color for views in the window decoration.
+ */
+public class WindowDecorLinearLayout extends LinearLayout implements TaskFocusStateConsumer {
+ private static final int[] TASK_FOCUSED_STATE = { R.attr.state_task_focused };
+
+ private boolean mIsTaskFocused;
+
+ public WindowDecorLinearLayout(Context context) {
+ super(context);
+ }
+
+ public WindowDecorLinearLayout(Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public WindowDecorLinearLayout(Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public WindowDecorLinearLayout(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public void setTaskFocusState(boolean focused) {
+ mIsTaskFocused = focused;
+
+ refreshDrawableState();
+ }
+
+ @Override
+ protected int[] onCreateDrawableState(int extraSpace) {
+ if (!mIsTaskFocused) {
+ return super.onCreateDrawableState(extraSpace);
+ }
+
+ final int[] states = super.onCreateDrawableState(extraSpace + 1);
+ mergeDrawableStates(states, TASK_FOCUSED_STATE);
+ return states;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
new file mode 100644
index 000000000000..6f9ceff722ac
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.windowdecor;
+
+import android.app.ActivityManager;
+import android.view.SurfaceControl;
+
+/**
+ * The interface used by some {@link com.android.wm.shell.ShellTaskOrganizer.TaskListener} to help
+ * customize {@link WindowDecoration}. Its implementations are responsible to interpret user's
+ * interactions with UI widgets in window decorations and send corresponding requests to system
+ * servers.
+ *
+ * @param <T> The actual decoration type
+ */
+public interface WindowDecorViewModel<T extends AutoCloseable> {
+
+ /**
+ * Creates a window decoration for the given task.
+ *
+ * @param taskInfo the initial task info of the task
+ * @param taskSurface the surface of the task
+ * @return the window decoration object
+ */
+ T createWindowDecoration(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface);
+
+ /**
+ * Notifies a task info update on the given task, with the window decoration created previously
+ * for this task by {@link #createWindowDecoration}.
+ *
+ * @param taskInfo the new task info of the task
+ * @param windowDecoration the window decoration created for the task
+ */
+ void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo, T windowDecoration);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
new file mode 100644
index 000000000000..c19a33abf8a4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.windowdecor;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.InsetsState;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+import android.window.WindowContainerTransaction;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
+
+import java.util.function.Supplier;
+
+/**
+ * Manages a container surface and a windowless window to show window decoration. Responsible to
+ * update window decoration window state and layout parameters on task info changes and so that
+ * window decoration is in correct state and bounds.
+ *
+ * The container surface is a child of the task display area in the same display, so that window
+ * decorations can be drawn out of the task bounds and receive input events from out of the task
+ * bounds to support drag resizing.
+ *
+ * The windowless window that hosts window decoration is positioned in front of all activities, to
+ * allow the foreground activity to draw its own background behind window decorations, such as
+ * the window captions.
+ *
+ * @param <T> The type of the root view
+ */
+public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
+ implements AutoCloseable {
+ private static final int[] CAPTION_INSETS_TYPES = { InsetsState.ITYPE_CAPTION_BAR };
+
+ /**
+ * System-wide context. Only used to create context with overridden configurations.
+ */
+ final Context mContext;
+ final DisplayController mDisplayController;
+ final ShellTaskOrganizer mTaskOrganizer;
+ final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
+ final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
+ private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
+ new DisplayController.OnDisplaysChangedListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ if (mTaskInfo.displayId != displayId) {
+ return;
+ }
+
+ mDisplayController.removeDisplayWindowListener(this);
+ relayout(mTaskInfo);
+ }
+ };
+
+ RunningTaskInfo mTaskInfo;
+ final SurfaceControl mTaskSurface;
+
+ Display mDisplay;
+ Context mDecorWindowContext;
+ SurfaceControl mDecorationContainerSurface;
+ SurfaceControl mTaskBackgroundSurface;
+
+ private final CaptionWindowManager mCaptionWindowManager;
+ private SurfaceControlViewHost mViewHost;
+
+ private final Rect mCaptionInsetsRect = new Rect();
+ private final Rect mTaskSurfaceCrop = new Rect();
+ private final float[] mTmpColor = new float[3];
+
+ WindowDecoration(
+ Context context,
+ DisplayController displayController,
+ ShellTaskOrganizer taskOrganizer,
+ RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface) {
+ this(context, displayController, taskOrganizer, taskInfo, taskSurface,
+ SurfaceControl.Builder::new, new SurfaceControlViewHostFactory() {});
+ }
+
+ WindowDecoration(
+ Context context,
+ DisplayController displayController,
+ ShellTaskOrganizer taskOrganizer,
+ RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+ SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
+ mContext = context;
+ mDisplayController = displayController;
+ mTaskOrganizer = taskOrganizer;
+ mTaskInfo = taskInfo;
+ mTaskSurface = taskSurface;
+ mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
+ mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
+
+ mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
+ mDecorWindowContext = mContext.createConfigurationContext(mTaskInfo.getConfiguration());
+
+ // Put caption under task surface because ViewRootImpl sets the destination frame of
+ // windowless window layers and BLASTBufferQueue#update() doesn't support offset.
+ mCaptionWindowManager =
+ new CaptionWindowManager(mTaskInfo.getConfiguration(), mTaskSurface);
+ }
+
+ /**
+ * Used by {@link WindowDecoration} to trigger a new relayout because the requirements for a
+ * relayout weren't satisfied are satisfied now.
+ *
+ * @param taskInfo The previous {@link RunningTaskInfo} passed into {@link #relayout} or the
+ * constructor.
+ */
+ abstract void relayout(RunningTaskInfo taskInfo);
+
+ void relayout(RunningTaskInfo taskInfo, int layoutResId, T rootView, float captionHeightDp,
+ Rect outsetsDp, float shadowRadiusDp, SurfaceControl.Transaction t,
+ WindowContainerTransaction wct, RelayoutResult<T> outResult) {
+ outResult.reset();
+
+ final Configuration oldTaskConfig = mTaskInfo.getConfiguration();
+ if (taskInfo != null) {
+ mTaskInfo = taskInfo;
+ }
+
+ if (!mTaskInfo.isVisible) {
+ releaseViews();
+ t.hide(mTaskSurface);
+ return;
+ }
+
+ if (rootView == null && layoutResId == 0) {
+ throw new IllegalArgumentException("layoutResId and rootView can't both be invalid.");
+ }
+
+ outResult.mRootView = rootView;
+ rootView = null; // Clear it just in case we use it accidentally
+ final Configuration taskConfig = mTaskInfo.getConfiguration();
+ if (oldTaskConfig.densityDpi != taskConfig.densityDpi
+ || mDisplay == null
+ || mDisplay.getDisplayId() != mTaskInfo.displayId) {
+ releaseViews();
+
+ if (!obtainDisplayOrRegisterListener()) {
+ outResult.mRootView = null;
+ return;
+ }
+ mDecorWindowContext = mContext.createConfigurationContext(taskConfig);
+ if (layoutResId != 0) {
+ outResult.mRootView =
+ (T) LayoutInflater.from(mDecorWindowContext).inflate(layoutResId, null);
+ }
+ }
+
+ if (outResult.mRootView == null) {
+ outResult.mRootView =
+ (T) LayoutInflater.from(mDecorWindowContext).inflate(layoutResId, null);
+ }
+
+ // DecorationContainerSurface
+ if (mDecorationContainerSurface == null) {
+ final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
+ mDecorationContainerSurface = builder
+ .setName("Decor container of Task=" + mTaskInfo.taskId)
+ .setContainerLayer()
+ .setParent(mTaskSurface)
+ .build();
+
+ t.setTrustedOverlay(mDecorationContainerSurface, true);
+ }
+
+ final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
+ outResult.mDensity = taskConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ final int decorContainerOffsetX = -(int) (outsetsDp.left * outResult.mDensity);
+ final int decorContainerOffsetY = -(int) (outsetsDp.top * outResult.mDensity);
+ outResult.mWidth = taskBounds.width()
+ + (int) (outsetsDp.right * outResult.mDensity)
+ - decorContainerOffsetX;
+ outResult.mHeight = taskBounds.height()
+ + (int) (outsetsDp.bottom * outResult.mDensity)
+ - decorContainerOffsetY;
+ t.setPosition(mDecorationContainerSurface, decorContainerOffsetX, decorContainerOffsetY)
+ .setWindowCrop(mDecorationContainerSurface, outResult.mWidth, outResult.mHeight)
+ .setLayer(mDecorationContainerSurface, mTaskInfo.numActivities + 1)
+ .show(mDecorationContainerSurface);
+
+ // TaskBackgroundSurface
+ if (mTaskBackgroundSurface == null) {
+ final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
+ mTaskBackgroundSurface = builder
+ .setName("Background of Task=" + mTaskInfo.taskId)
+ .setEffectLayer()
+ .setParent(mTaskSurface)
+ .build();
+ }
+
+ float shadowRadius = outResult.mDensity * shadowRadiusDp;
+ int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor();
+ mTmpColor[0] = Color.red(backgroundColorInt);
+ mTmpColor[1] = Color.green(backgroundColorInt);
+ mTmpColor[2] = Color.blue(backgroundColorInt);
+ t.setCrop(mTaskBackgroundSurface, taskBounds)
+ .setShadowRadius(mTaskBackgroundSurface, shadowRadius)
+ .setColor(mTaskBackgroundSurface, mTmpColor);
+
+ // Caption view
+ mCaptionWindowManager.setConfiguration(taskConfig);
+ final int captionHeight = (int) Math.ceil(captionHeightDp * outResult.mDensity);
+ final WindowManager.LayoutParams lp =
+ new WindowManager.LayoutParams(taskBounds.width(), captionHeight,
+ WindowManager.LayoutParams.TYPE_APPLICATION,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
+ lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
+ lp.setTrustedOverlay();
+ if (mViewHost == null) {
+ mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
+ mCaptionWindowManager, true);
+ mViewHost.setView(outResult.mRootView, lp);
+ } else {
+ mViewHost.relayout(lp);
+ }
+
+ if (ViewRootImpl.CAPTION_ON_SHELL) {
+ outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
+
+ // Caption insets
+ mCaptionInsetsRect.set(taskBounds);
+ mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + captionHeight;
+ wct.addRectInsetsProvider(mTaskInfo.token, mCaptionInsetsRect, CAPTION_INSETS_TYPES);
+ } else {
+ outResult.mRootView.setVisibility(View.GONE);
+ }
+
+ // Task surface itself
+ Point taskPosition = mTaskInfo.positionInParent;
+ mTaskSurfaceCrop.set(
+ decorContainerOffsetX,
+ decorContainerOffsetY,
+ outResult.mWidth + decorContainerOffsetX,
+ outResult.mHeight + decorContainerOffsetY);
+ t.setPosition(mTaskSurface, taskPosition.x, taskPosition.y)
+ .setCrop(mTaskSurface, mTaskSurfaceCrop)
+ .show(mTaskSurface);
+ }
+
+ /**
+ * Obtains the {@link Display} instance for the display ID in {@link #mTaskInfo} if it exists or
+ * registers {@link #mOnDisplaysChangedListener} if it doesn't.
+ *
+ * @return {@code true} if the {@link Display} instance exists; or {@code false} otherwise
+ */
+ private boolean obtainDisplayOrRegisterListener() {
+ mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
+ if (mDisplay == null) {
+ mDisplayController.addDisplayWindowListener(mOnDisplaysChangedListener);
+ return false;
+ }
+ return true;
+ }
+
+ private void releaseViews() {
+ if (mViewHost != null) {
+ mViewHost.release();
+ mViewHost = null;
+ }
+
+ if (mDecorationContainerSurface != null) {
+ mDecorationContainerSurface.release();
+ mDecorationContainerSurface = null;
+ }
+
+ if (mTaskBackgroundSurface != null) {
+ mTaskBackgroundSurface.release();
+ mTaskBackgroundSurface = null;
+ }
+ }
+
+ @Override
+ public void close() {
+ mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener);
+ releaseViews();
+ }
+
+ static class RelayoutResult<T extends View & TaskFocusStateConsumer> {
+ int mWidth;
+ int mHeight;
+ float mDensity;
+ T mRootView;
+
+ void reset() {
+ mWidth = 0;
+ mHeight = 0;
+ mDensity = 0;
+ mRootView = null;
+ }
+ }
+
+ private static class CaptionWindowManager extends WindowlessWindowManager {
+ CaptionWindowManager(Configuration config, SurfaceControl rootSurface) {
+ super(config, rootSurface, null /* hostInputToken */);
+ }
+
+ @Override
+ public void setConfiguration(Configuration configuration) {
+ super.setConfiguration(configuration);
+ }
+ }
+
+ interface SurfaceControlViewHostFactory {
+ default SurfaceControlViewHost create(
+ Context c, Display d, WindowlessWindowManager wmm, boolean useSfChoreographer) {
+ return new SurfaceControlViewHost(c, d, wmm, useSfChoreographer);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index cb478c84c2b7..cba396a82a87 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -43,6 +43,84 @@ fun FlickerTestParameter.appPairsDividerBecomesVisible() {
}
}
+fun FlickerTestParameter.splitScreenDividerBecomesVisible() {
+ layerBecomesVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+}
+
+fun FlickerTestParameter.layerBecomesVisible(
+ component: FlickerComponentName
+) {
+ assertLayers {
+ this.isInvisible(component)
+ .then()
+ .isVisible(component)
+ }
+}
+
+fun FlickerTestParameter.layerIsVisibleAtEnd(
+ component: FlickerComponentName
+) {
+ assertLayersEnd {
+ this.isVisible(component)
+ }
+}
+
+fun FlickerTestParameter.splitAppLayerBoundsBecomesVisible(
+ rotation: Int,
+ component: FlickerComponentName,
+ splitLeftTop: Boolean
+) {
+ assertLayers {
+ val dividerRegion = this.last().layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
+ this.isInvisible(component)
+ .then()
+ .invoke("splitAppLayerBoundsBecomesVisible") {
+ it.visibleRegion(component).overlaps(
+ if (splitLeftTop) {
+ getSplitLeftTopRegion(dividerRegion, rotation)
+ } else {
+ getSplitRightBottomRegion(dividerRegion, rotation)
+ }
+ )
+ }
+ }
+}
+
+fun FlickerTestParameter.splitAppLayerBoundsIsVisibleAtEnd(
+ rotation: Int,
+ component: FlickerComponentName,
+ splitLeftTop: Boolean
+) {
+ assertLayersEnd {
+ val dividerRegion = layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(component).overlaps(
+ if (splitLeftTop) {
+ getSplitLeftTopRegion(dividerRegion, rotation)
+ } else {
+ getSplitRightBottomRegion(dividerRegion, rotation)
+ }
+ )
+ }
+}
+
+fun FlickerTestParameter.appWindowBecomesVisible(
+ component: FlickerComponentName
+) {
+ assertWm {
+ this.isAppWindowInvisible(component)
+ .then()
+ .isAppWindowVisible(component)
+ }
+}
+
+fun FlickerTestParameter.appWindowIsVisibleAtEnd(
+ component: FlickerComponentName
+) {
+ assertWmEnd {
+ this.isAppWindowVisible(component)
+ }
+}
+
fun FlickerTestParameter.dockedStackDividerIsVisibleAtEnd() {
assertLayersEnd {
this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
@@ -118,21 +196,53 @@ fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisibleAtEnd(
fun getPrimaryRegion(dividerRegion: Region, rotation: Int): Region {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
- Region.from(0, 0, displayBounds.bounds.right,
- dividerRegion.bounds.top + WindowUtils.dockedStackDividerInset)
+ Region.from(
+ 0, 0, displayBounds.bounds.right,
+ dividerRegion.bounds.top + WindowUtils.dockedStackDividerInset
+ )
} else {
- Region.from(0, 0, dividerRegion.bounds.left + WindowUtils.dockedStackDividerInset,
- displayBounds.bounds.bottom)
+ Region.from(
+ 0, 0, dividerRegion.bounds.left + WindowUtils.dockedStackDividerInset,
+ displayBounds.bounds.bottom
+ )
}
}
fun getSecondaryRegion(dividerRegion: Region, rotation: Int): Region {
val displayBounds = WindowUtils.getDisplayBounds(rotation)
return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
- Region.from(0, dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
- displayBounds.bounds.right, displayBounds.bounds.bottom)
+ Region.from(
+ 0, dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
+ displayBounds.bounds.right, displayBounds.bounds.bottom
+ )
} else {
- Region.from(dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset, 0,
- displayBounds.bounds.right, displayBounds.bounds.bottom)
+ Region.from(
+ dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset, 0,
+ displayBounds.bounds.right, displayBounds.bounds.bottom
+ )
}
-} \ No newline at end of file
+}
+
+fun getSplitLeftTopRegion(dividerRegion: Region, rotation: Int): Region {
+ val displayBounds = WindowUtils.getDisplayBounds(rotation)
+ return if (displayBounds.width > displayBounds.height) {
+ Region.from(0, 0, dividerRegion.bounds.left, displayBounds.bounds.bottom)
+ } else {
+ Region.from(0, 0, displayBounds.bounds.right, dividerRegion.bounds.top)
+ }
+}
+
+fun getSplitRightBottomRegion(dividerRegion: Region, rotation: Int): Region {
+ val displayBounds = WindowUtils.getDisplayBounds(rotation)
+ return if (displayBounds.width > displayBounds.height) {
+ Region.from(
+ dividerRegion.bounds.right, 0, displayBounds.bounds.right,
+ displayBounds.bounds.bottom
+ )
+ } else {
+ Region.from(
+ 0, dividerRegion.bounds.bottom, displayBounds.bounds.right,
+ displayBounds.bounds.bottom
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index 40891f36a5da..f56eb6e783aa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -21,4 +21,5 @@ import com.android.server.wm.traces.common.FlickerComponentName
const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
val APP_PAIR_SPLIT_DIVIDER_COMPONENT = FlickerComponentName("", "AppPairSplitDivider#")
-val DOCKED_STACK_DIVIDER_COMPONENT = FlickerComponentName("", "DockedStackDivider#") \ No newline at end of file
+val DOCKED_STACK_DIVIDER_COMPONENT = FlickerComponentName("", "DockedStackDivider#")
+val SPLIT_SCREEN_DIVIDER_COMPONENT = FlickerComponentName("", "StageCoordinatorSplitDivider#")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
deleted file mode 100644
index c9cab39b7d8b..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.apppairs
-
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
-import com.android.wm.shell.flicker.helpers.AppPairsHelper
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
-import org.junit.After
-import org.junit.Before
-import org.junit.FixMethodOrder
-import org.junit.Ignore
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test cold launch app from launcher. When the device doesn't support non-resizable in multi window
- * {@link Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}, app pairs should not pair
- * non-resizable apps.
- *
- * To run this test: `atest WMShellFlickerTests:AppPairsTestCannotPairNonResizeableApps`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-class AppPairsTestCannotPairNonResizeableApps(
- testSpec: FlickerTestParameter
-) : AppPairsTransition(testSpec) {
-
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- transitions {
- nonResizeableApp?.launchViaIntent(wmHelper)
- // TODO pair apps through normal UX flow
- executeShellCommand(
- composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
- nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) }
- }
- }
-
- @Before
- override fun setup() {
- super.setup()
- setSupportsNonResizableMultiWindow(instrumentation, -1)
- }
-
- @After
- override fun teardown() {
- super.teardown()
- resetMultiWindowConfig(instrumentation)
- }
-
- @Ignore
- @Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
-
- @Ignore
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
- @Ignore
- @Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
-
- @Ignore
- @Test
- fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd()
-
- @Ignore
- @Test
- fun onlyResizeableAppWindowVisible() {
- val nonResizeableApp = nonResizeableApp
- require(nonResizeableApp != null) {
- "Non resizeable app not initialized"
- }
- testSpec.assertWmEnd {
- isAppWindowVisible(nonResizeableApp.component)
- isAppWindowInvisible(primaryApp.component)
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = AppPairsHelper.TEST_REPETITIONS)
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
deleted file mode 100644
index 60c32c99d1ff..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.apppairs
-
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
-import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.AppPairsHelper
-import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
-import org.junit.FixMethodOrder
-import org.junit.Ignore
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test cold launch app from launcher.
- * To run this test: `atest WMShellFlickerTests:AppPairsTestPairPrimaryAndSecondaryApps`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-class AppPairsTestPairPrimaryAndSecondaryApps(
- testSpec: FlickerTestParameter
-) : AppPairsTransition(testSpec) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- transitions {
- // TODO pair apps through normal UX flow
- executeShellCommand(
- composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
- waitAppsShown(primaryApp, secondaryApp)
- }
- }
-
- @Ignore
- @Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
-
- @Ignore
- @Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
-
- @Ignore
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
- @Ignore
- @Test
- fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
-
- @Ignore
- @Test
- fun bothAppWindowsVisible() {
- testSpec.assertWmEnd {
- isAppWindowVisible(primaryApp.component)
- isAppWindowVisible(secondaryApp.component)
- }
- }
-
- @Ignore
- @Test
- fun appsEndingBounds() {
- testSpec.assertLayersEnd {
- val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
- visibleRegion(primaryApp.component)
- .coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
- visibleRegion(secondaryApp.component)
- .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = AppPairsHelper.TEST_REPETITIONS)
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
deleted file mode 100644
index 24869a802167..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.apppairs
-
-import android.view.Display
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.common.WindowManagerConditionsFactory
-import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.AppPairsHelper
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
-import org.junit.After
-import org.junit.Before
-import org.junit.FixMethodOrder
-import org.junit.Ignore
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test cold launch app from launcher. When the device supports non-resizable in multi window
- * {@link Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}, app pairs can pair
- * non-resizable apps.
- *
- * To run this test: `atest WMShellFlickerTests:AppPairsTestSupportPairNonResizeableApps`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-class AppPairsTestSupportPairNonResizeableApps(
- testSpec: FlickerTestParameter
-) : AppPairsTransition(testSpec) {
-
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- transitions {
- nonResizeableApp?.launchViaIntent(wmHelper)
- // TODO pair apps through normal UX flow
- executeShellCommand(
- composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
- val waitConditions = mutableListOf(
- WindowManagerConditionsFactory.isWindowVisible(primaryApp.component),
- WindowManagerConditionsFactory.isLayerVisible(primaryApp.component),
- WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
-
- nonResizeableApp?.let {
- waitConditions.add(
- WindowManagerConditionsFactory.isWindowVisible(nonResizeableApp.component))
- waitConditions.add(
- WindowManagerConditionsFactory.isLayerVisible(nonResizeableApp.component))
- }
- wmHelper.waitFor(*waitConditions.toTypedArray())
- }
- }
-
- @Before
- override fun setup() {
- super.setup()
- setSupportsNonResizableMultiWindow(instrumentation, 1)
- }
-
- @After
- override fun teardown() {
- super.teardown()
- resetMultiWindowConfig(instrumentation)
- }
-
- @Ignore
- @Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
-
- @Ignore
- @Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
-
- @Ignore
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
- @Ignore
- @Test
- fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
-
- @Ignore
- @Test
- fun bothAppWindowVisible() {
- val nonResizeableApp = nonResizeableApp
- require(nonResizeableApp != null) {
- "Non resizeable app not initialized"
- }
- testSpec.assertWmEnd {
- isAppWindowVisible(nonResizeableApp.component)
- isAppWindowVisible(primaryApp.component)
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = AppPairsHelper.TEST_REPETITIONS)
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
deleted file mode 100644
index 007415d19860..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.apppairs
-
-import android.os.SystemClock
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
-import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
-import com.android.wm.shell.flicker.helpers.AppPairsHelper
-import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
-import org.junit.FixMethodOrder
-import org.junit.Ignore
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test cold launch app from launcher.
- * To run this test: `atest WMShellFlickerTests:AppPairsTestUnpairPrimaryAndSecondaryApps`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-class AppPairsTestUnpairPrimaryAndSecondaryApps(
- testSpec: FlickerTestParameter
-) : AppPairsTransition(testSpec) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- setup {
- eachRun {
- executeShellCommand(
- composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
- waitAppsShown(primaryApp, secondaryApp)
- }
- }
- transitions {
- // TODO pair apps through normal UX flow
- executeShellCommand(
- composePairsCommand(primaryTaskId, secondaryTaskId, pair = false))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
- }
- }
-
- @Ignore
- @Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
-
- @Ignore
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
- @Ignore
- @Test
- fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd()
-
- @Ignore
- @Test
- fun bothAppWindowsInvisible() {
- testSpec.assertWmEnd {
- isAppWindowInvisible(primaryApp.component)
- isAppWindowInvisible(secondaryApp.component)
- }
- }
-
- @Ignore
- @Test
- fun appsStartingBounds() {
- testSpec.assertLayersStart {
- val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
- visibleRegion(primaryApp.component)
- .coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
- visibleRegion(secondaryApp.component)
- .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
- }
- }
-
- @Ignore
- @Test
- fun appsEndingBounds() {
- testSpec.assertLayersEnd {
- notContains(primaryApp.component)
- notContains(secondaryApp.component)
- }
- }
-
- @Ignore
- @Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = AppPairsHelper.TEST_REPETITIONS)
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
deleted file mode 100644
index 3e17948b4a84..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.apppairs
-
-import android.app.Instrumentation
-import android.content.Context
-import android.system.helpers.ActivityHelper
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.wm.shell.flicker.helpers.AppPairsHelper
-import com.android.wm.shell.flicker.helpers.BaseAppHelper
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import com.android.wm.shell.flicker.testapp.Components
-import org.junit.After
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Test
-
-abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) {
- protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- protected val context: Context = instrumentation.context
- protected val activityHelper = ActivityHelper.getInstance()
- protected val appPairsHelper = AppPairsHelper(instrumentation,
- Components.SplitScreenActivity.LABEL,
- Components.SplitScreenActivity.COMPONENT.toFlickerComponent())
-
- protected val primaryApp = SplitScreenHelper.getPrimary(instrumentation)
- protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
- protected open val nonResizeableApp: SplitScreenHelper? =
- SplitScreenHelper.getNonResizeable(instrumentation)
- protected var primaryTaskId = ""
- protected var secondaryTaskId = ""
- protected var nonResizeableTaskId = ""
- private var prevDevEnableNonResizableMultiWindow = 0
-
- @Before
- open fun setup() {
- prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context)
- if (prevDevEnableNonResizableMultiWindow != 0) {
- // Turn off the development option
- setDevEnableNonResizableMultiWindow(context, 0)
- }
- }
-
- @After
- open fun teardown() {
- setDevEnableNonResizableMultiWindow(context, prevDevEnableNonResizableMultiWindow)
- }
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- transition(this)
- }
- }
-
- internal open val transition: FlickerBuilder.() -> Unit
- get() = {
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- }
- eachRun {
- this.setRotation(testSpec.startRotation)
- primaryApp.launchViaIntent(wmHelper)
- secondaryApp.launchViaIntent(wmHelper)
- nonResizeableApp?.launchViaIntent(wmHelper)
- updateTasksId()
- }
- }
- teardown {
- eachRun {
- executeShellCommand(composePairsCommand(
- primaryTaskId, secondaryTaskId, pair = false))
- executeShellCommand(composePairsCommand(
- primaryTaskId, nonResizeableTaskId, pair = false))
- primaryApp.exit(wmHelper)
- secondaryApp.exit(wmHelper)
- nonResizeableApp?.exit(wmHelper)
- }
- }
- }
-
- protected fun updateTasksId() {
- primaryTaskId = getTaskIdForActivity(
- primaryApp.component.packageName, primaryApp.component.className).toString()
- secondaryTaskId = getTaskIdForActivity(
- secondaryApp.component.packageName, secondaryApp.component.className).toString()
- val nonResizeableApp = nonResizeableApp
- if (nonResizeableApp != null) {
- nonResizeableTaskId = getTaskIdForActivity(
- nonResizeableApp.component.packageName,
- nonResizeableApp.component.className).toString()
- }
- }
-
- private fun getTaskIdForActivity(pkgName: String, activityName: String): Int {
- return activityHelper.getTaskIdForActivity(pkgName, activityName)
- }
-
- internal fun executeShellCommand(cmd: String) {
- BaseAppHelper.executeShellCommand(instrumentation, cmd)
- }
-
- internal fun composePairsCommand(
- primaryApp: String,
- secondaryApp: String,
- pair: Boolean
- ): String = buildString {
- // dumpsys activity service SystemUIService WMShell {pair|unpair} ${TASK_ID_1} ${TASK_ID_2}
- append("dumpsys activity service SystemUIService WMShell ")
- if (pair) {
- append("pair ")
- } else {
- append("unpair ")
- }
- append("$primaryApp $secondaryApp")
- }
-
- @Ignore
- @Test
- open fun navBarLayerIsVisible() {
- testSpec.navBarLayerIsVisible()
- }
-
- @Ignore
- @Test
- open fun statusBarLayerIsVisible() {
- testSpec.statusBarLayerIsVisible()
- }
-
- @Ignore
- @Test
- open fun navBarWindowIsVisible() {
- testSpec.navBarWindowIsVisible()
- }
-
- @Ignore
- @Test
- open fun statusBarWindowIsVisible() {
- testSpec.statusBarWindowIsVisible()
- }
-
- @Ignore
- @Test
- open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- @Ignore
- @Test
- open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS
deleted file mode 100644
index 8446b37dbf06..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# window manager > wm shell > Split Screen
-# Bug component: 928697
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
deleted file mode 100644
index b0c3ba20d948..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.apppairs
-
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
-import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
-import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Ignore
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open apps to app pairs and rotate.
- * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsInAppPairsMode`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-class RotateTwoLaunchedAppsInAppPairsMode(
- testSpec: FlickerTestParameter
-) : RotateTwoLaunchedAppsTransition(testSpec) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- transitions {
- executeShellCommand(composePairsCommand(
- primaryTaskId, secondaryTaskId, true /* pair */))
- waitAppsShown(primaryApp, secondaryApp)
- setRotation(testSpec.endRotation)
- }
- }
-
- @Ignore
- @Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
-
- @Ignore
- @Test
- override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
-
- @Ignore
- @Test
- fun bothAppWindowsVisible() {
- testSpec.assertWmEnd {
- isAppWindowVisible(primaryApp.component)
- isAppWindowVisible(secondaryApp.component)
- }
- }
-
- @Ignore
- @Test
- fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
-
- @Ignore
- @Test
- fun appPairsPrimaryBoundsIsVisibleAtEnd() =
- testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.endRotation,
- primaryApp.component)
-
- @Ignore
- @Test
- fun appPairsSecondaryBoundsIsVisibleAtEnd() =
- testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.endRotation,
- secondaryApp.component)
-
- @Ignore
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270)
- )
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
deleted file mode 100644
index ae56c7732a4d..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.apppairs
-
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
-import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
-import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Ignore
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open apps to app pairs and rotate.
- * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsRotateAndEnterAppPairsMode`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
- testSpec: FlickerTestParameter
-) : RotateTwoLaunchedAppsTransition(testSpec) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- transitions {
- this.setRotation(testSpec.endRotation)
- executeShellCommand(
- composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
- waitAppsShown(primaryApp, secondaryApp)
- }
- }
-
- @Ignore
- @Test
- fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
-
- @Ignore
- @Test
- override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
-
- @Ignore
- @Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
-
- @Ignore
- @Test
- override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
-
- @Ignore
- @Test
- override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
-
- @Ignore
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
- @Ignore
- @Test
- fun bothAppWindowsVisible() {
- testSpec.assertWmEnd {
- isAppWindowVisible(primaryApp.component)
- isAppWindowVisible(secondaryApp.component)
- }
- }
-
- @Ignore
- @Test
- fun appPairsPrimaryBoundsIsVisibleAtEnd() =
- testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.endRotation,
- primaryApp.component)
-
- @Ignore
- @Test
- fun appPairsSecondaryBoundsIsVisibleAtEnd() =
- testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.endRotation,
- secondaryApp.component)
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270)
- )
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
deleted file mode 100644
index b1f1c9e539df..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.apppairs
-
-import android.view.Surface
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume.assumeFalse
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Test
-
-abstract class RotateTwoLaunchedAppsTransition(
- testSpec: FlickerTestParameter
-) : AppPairsTransition(testSpec) {
- override val nonResizeableApp: SplitScreenHelper?
- get() = null
-
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(Surface.ROTATION_0)
- primaryApp.launchViaIntent(wmHelper)
- secondaryApp.launchViaIntent(wmHelper)
- updateTasksId()
- }
- }
- teardown {
- eachRun {
- executeShellCommand(composePairsCommand(
- primaryTaskId, secondaryTaskId, pair = false))
- primaryApp.exit(wmHelper)
- secondaryApp.exit(wmHelper)
- }
- }
- }
-
- @Before
- override fun setup() {
- // AppPairs hasn't been updated to Shell Transition. There will be conflict on rotation.
- assumeFalse(isShellTransitionsEnabled())
- super.setup()
- }
-
- @Ignore
- @Test
- override fun navBarLayerIsVisible() {
- super.navBarLayerIsVisible()
- }
-
- @Ignore
- @Test
- override fun navBarLayerRotatesAndScales() {
- super.navBarLayerRotatesAndScales()
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index e9d438a569d5..8157a4e453af 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -39,7 +39,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
) {
private val mediaSessionManager: MediaSessionManager
get() = context.getSystemService(MediaSessionManager::class.java)
- ?: error("Could not get MediaSessionManager")
+ ?: error("Could not get MediaSessionManager")
private val mediaController: MediaController?
get() = mediaSessionManager.getActiveSessions(null).firstOrNull {
@@ -69,8 +69,10 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
action: String? = null,
stringExtras: Map<String, String>
) {
- launchViaIntentAndWaitShown(wmHelper, expectedWindowName, action, stringExtras,
- waitConditions = arrayOf(WindowManagerStateHelper.pipShownCondition))
+ launchViaIntentAndWaitShown(
+ wmHelper, expectedWindowName, action, stringExtras,
+ waitConditions = arrayOf(WindowManagerStateHelper.pipShownCondition)
+ )
}
/**
@@ -85,7 +87,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
// from "the bottom".
repeat(FOCUS_ATTEMPTS) {
uiDevice.findObject(selector)?.apply { if (isFocusedOrHasFocusedChild) return true }
- ?: error("The object we try to focus on is gone.")
+ ?: error("The object we try to focus on is gone.")
uiDevice.pressDPadDown()
uiDevice.waitForIdle()
@@ -100,29 +102,39 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
// Wait on WMHelper or simply wait for 3 seconds
wmHelper?.waitPipShown() ?: SystemClock.sleep(3_000)
// when entering pip, the dismiss button is visible at the start. to ensure the pip
- // animation is complete, wait until the pip dismiss button is no longer visible.
+ // animation is complete, wait until the pip dismiss button is no longer visible.
// b/176822698: dismiss-only state will be removed in the future
uiDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "dismiss")), FIND_TIMEOUT)
}
+ fun enableEnterPipOnUserLeaveHint() {
+ clickObject(ENTER_PIP_ON_USER_LEAVE_HINT)
+ }
+
+ fun enableAutoEnterForPipActivity() {
+ clickObject(ENTER_PIP_AUTOENTER)
+ }
+
fun clickStartMediaSessionButton() {
clickObject(MEDIA_SESSION_START_RADIO_BUTTON_ID)
}
fun checkWithCustomActionsCheckbox() = uiDevice
- .findObject(By.res(component.packageName, WITH_CUSTOM_ACTIONS_BUTTON_ID))
- ?.takeIf { it.isCheckable }
- ?.apply { if (!isChecked) clickObject(WITH_CUSTOM_ACTIONS_BUTTON_ID) }
- ?: error("'With custom actions' checkbox not found")
+ .findObject(By.res(component.packageName, WITH_CUSTOM_ACTIONS_BUTTON_ID))
+ ?.takeIf { it.isCheckable }
+ ?.apply { if (!isChecked) clickObject(WITH_CUSTOM_ACTIONS_BUTTON_ID) }
+ ?: error("'With custom actions' checkbox not found")
fun pauseMedia() = mediaController?.transportControls?.pause()
- ?: error("No active media session found")
+ ?: error("No active media session found")
fun stopMedia() = mediaController?.transportControls?.stop()
- ?: error("No active media session found")
+ ?: error("No active media session found")
- @Deprecated("Use PipAppHelper.closePipWindow(wmHelper) instead",
- ReplaceWith("closePipWindow(wmHelper)"))
+ @Deprecated(
+ "Use PipAppHelper.closePipWindow(wmHelper) instead",
+ ReplaceWith("closePipWindow(wmHelper)")
+ )
fun closePipWindow() {
if (isTelevision) {
uiDevice.closeTvPipWindow()
@@ -152,7 +164,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
val dismissSelector = By.res(SYSTEMUI_PACKAGE, "dismiss")
uiDevice.wait(Until.hasObject(dismissSelector), FIND_TIMEOUT)
val dismissPipObject = uiDevice.findObject(dismissSelector)
- ?: error("PIP window dismiss button not found")
+ ?: error("PIP window dismiss button not found")
val dismissButtonBounds = dismissPipObject.visibleBounds
uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY())
}
@@ -172,7 +184,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
val expandSelector = By.res(SYSTEMUI_PACKAGE, "expand_button")
uiDevice.wait(Until.hasObject(expandSelector), FIND_TIMEOUT)
val expandPipObject = uiDevice.findObject(expandSelector)
- ?: error("PIP window expand button not found")
+ ?: error("PIP window expand button not found")
val expandButtonBounds = expandPipObject.visibleBounds
uiDevice.click(expandButtonBounds.centerX(), expandButtonBounds.centerY())
wmHelper.waitPipGone()
@@ -194,5 +206,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
private const val ENTER_PIP_BUTTON_ID = "enter_pip"
private const val WITH_CUSTOM_ACTIONS_BUTTON_ID = "with_custom_actions"
private const val MEDIA_SESSION_START_RADIO_BUTTON_ID = "media_session_start"
+ private const val ENTER_PIP_ON_USER_LEAVE_HINT = "enter_pip_on_leave_manual"
+ private const val ENTER_PIP_AUTOENTER = "enter_pip_on_leave_autoenter"
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index 0ec9b2d869a8..49eca63a23ec 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -17,10 +17,21 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.content.res.Resources
+import android.graphics.Point
+import android.os.SystemClock
+import android.view.InputDevice
+import android.view.MotionEvent
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.BySelector
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.server.wm.traces.parser.toFlickerComponent
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
import com.android.wm.shell.flicker.testapp.Components
+import org.junit.Assert
class SplitScreenHelper(
instrumentation: Instrumentation,
@@ -31,25 +42,170 @@ class SplitScreenHelper(
companion object {
const val TEST_REPETITIONS = 1
const val TIMEOUT_MS = 3_000L
+ const val DRAG_DURATION_MS = 1_000L
+ const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
+ const val GESTURE_STEP_MS = 16L
- // TODO: remove all legacy split screen flicker tests when legacy split screen is fully
- // deprecated.
- fun isUsingLegacySplit(): Boolean =
- Resources.getSystem().getBoolean(com.android.internal.R.bool.config_useLegacySplit)
+ private val notificationScrollerSelector: BySelector
+ get() = By.res(SYSTEM_UI_PACKAGE_NAME, NOTIFICATION_SCROLLER)
+ private val notificationContentSelector: BySelector
+ get() = By.text("Notification content")
fun getPrimary(instrumentation: Instrumentation): SplitScreenHelper =
- SplitScreenHelper(instrumentation,
+ SplitScreenHelper(
+ instrumentation,
Components.SplitScreenActivity.LABEL,
- Components.SplitScreenActivity.COMPONENT.toFlickerComponent())
+ Components.SplitScreenActivity.COMPONENT.toFlickerComponent()
+ )
fun getSecondary(instrumentation: Instrumentation): SplitScreenHelper =
- SplitScreenHelper(instrumentation,
+ SplitScreenHelper(
+ instrumentation,
Components.SplitScreenSecondaryActivity.LABEL,
- Components.SplitScreenSecondaryActivity.COMPONENT.toFlickerComponent())
+ Components.SplitScreenSecondaryActivity.COMPONENT.toFlickerComponent()
+ )
fun getNonResizeable(instrumentation: Instrumentation): SplitScreenHelper =
- SplitScreenHelper(instrumentation,
+ SplitScreenHelper(
+ instrumentation,
Components.NonResizeableActivity.LABEL,
- Components.NonResizeableActivity.COMPONENT.toFlickerComponent())
+ Components.NonResizeableActivity.COMPONENT.toFlickerComponent()
+ )
+
+ fun getSendNotification(instrumentation: Instrumentation): SplitScreenHelper =
+ SplitScreenHelper(
+ instrumentation,
+ Components.SendNotificationActivity.LABEL,
+ Components.SendNotificationActivity.COMPONENT.toFlickerComponent()
+ )
+
+ fun dragFromNotificationToSplit(
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ wmHelper: WindowManagerStateHelper
+ ) {
+ val displayBounds = wmHelper.currentState.layerState
+ .displays.firstOrNull { !it.isVirtual }
+ ?.layerStackSpace
+ ?: error("Display not found")
+
+ // Pull down the notifications
+ device.swipe(
+ displayBounds.centerX(), 5,
+ displayBounds.centerX(), displayBounds.bottom, 20 /* steps */
+ )
+ SystemClock.sleep(TIMEOUT_MS)
+
+ // Find the target notification
+ val notificationScroller = device.wait(
+ Until.findObject(notificationScrollerSelector), TIMEOUT_MS
+ )
+ var notificationContent = notificationScroller.findObject(notificationContentSelector)
+
+ while (notificationContent == null) {
+ device.swipe(
+ displayBounds.centerX(), displayBounds.centerY(),
+ displayBounds.centerX(), displayBounds.centerY() - 150, 20 /* steps */
+ )
+ notificationContent = notificationScroller.findObject(notificationContentSelector)
+ }
+
+ // Drag to split
+ var dragStart = notificationContent.visibleCenter
+ var dragMiddle = Point(dragStart.x + 50, dragStart.y)
+ var dragEnd = Point(displayBounds.width / 4, displayBounds.width / 4)
+ val downTime = SystemClock.uptimeMillis()
+
+ touch(
+ instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime,
+ TIMEOUT_MS, dragStart
+ )
+ // It needs a horizontal movement to trigger the drag
+ touchMove(
+ instrumentation, downTime, SystemClock.uptimeMillis(),
+ DRAG_DURATION_MS, dragStart, dragMiddle
+ )
+ touchMove(
+ instrumentation, downTime, SystemClock.uptimeMillis(),
+ DRAG_DURATION_MS, dragMiddle, dragEnd
+ )
+ // Wait for a while to start splitting
+ SystemClock.sleep(TIMEOUT_MS)
+ touch(
+ instrumentation, MotionEvent.ACTION_UP, downTime, SystemClock.uptimeMillis(),
+ GESTURE_STEP_MS, dragEnd
+ )
+ SystemClock.sleep(TIMEOUT_MS)
+ }
+
+ fun touch(
+ instrumentation: Instrumentation,
+ action: Int,
+ downTime: Long,
+ eventTime: Long,
+ duration: Long,
+ point: Point
+ ) {
+ val motionEvent = MotionEvent.obtain(
+ downTime, eventTime, action, point.x.toFloat(), point.y.toFloat(), 0
+ )
+ motionEvent.source = InputDevice.SOURCE_TOUCHSCREEN
+ instrumentation.uiAutomation.injectInputEvent(motionEvent, true)
+ motionEvent.recycle()
+ SystemClock.sleep(duration)
+ }
+
+ fun touchMove(
+ instrumentation: Instrumentation,
+ downTime: Long,
+ eventTime: Long,
+ duration: Long,
+ from: Point,
+ to: Point
+ ) {
+ val steps: Long = duration / GESTURE_STEP_MS
+ var currentTime = eventTime
+ var currentX = from.x.toFloat()
+ var currentY = from.y.toFloat()
+ val stepX = (to.x.toFloat() - from.x.toFloat()) / steps.toFloat()
+ val stepY = (to.y.toFloat() - from.y.toFloat()) / steps.toFloat()
+
+ for (i in 1..steps) {
+ val motionMove = MotionEvent.obtain(
+ downTime, currentTime, MotionEvent.ACTION_MOVE, currentX, currentY, 0
+ )
+ motionMove.source = InputDevice.SOURCE_TOUCHSCREEN
+ instrumentation.uiAutomation.injectInputEvent(motionMove, true)
+ motionMove.recycle()
+
+ currentTime += GESTURE_STEP_MS
+ if (i == steps - 1) {
+ currentX = to.x.toFloat()
+ currentY = to.y.toFloat()
+ } else {
+ currentX += stepX
+ currentY += stepY
+ }
+ SystemClock.sleep(GESTURE_STEP_MS)
+ }
+ }
+
+ fun createShortcutOnHotseatIfNotExist(
+ taplInstrumentation: LauncherInstrumentation,
+ appName: String
+ ) {
+ taplInstrumentation.workspace
+ .deleteAppIcon(taplInstrumentation.workspace.getHotseatAppIcon(0))
+ val allApps = taplInstrumentation.workspace.switchToAllApps()
+ allApps.freeze()
+ try {
+ val appIconSrc = allApps.getAppIcon(appName)
+ Assert.assertNotNull("Unable to find app icon", appIconSrc)
+ val appIconDest = appIconSrc.dragToHotseat(0)
+ Assert.assertNotNull("Unable to drag app icon on hotseat", appIconDest)
+ } finally {
+ allApps.unfreeze()
+ }
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
deleted file mode 100644
index c86a1229d8d8..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open activity and dock to primary split screen
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenDockActivity`
- */
-@Presubmit
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group4
-class EnterSplitScreenDockActivity(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- transitions {
- device.launchSplitScreen(wmHelper)
- }
- }
-
- override val ignoredWindows: List<FlickerComponentName>
- get() = listOf(LAUNCHER_COMPONENT, LIVE_WALLPAPER_COMPONENT,
- splitScreenApp.component, FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT, LAUNCHER_COMPONENT)
-
- @Presubmit
- @Test
- fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
- splitScreenApp.component)
-
- @Presubmit
- @Test
- fun dockedStackDividerBecomesVisible() = testSpec.dockedStackDividerBecomesVisible()
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun appWindowIsVisible() {
- testSpec.assertWmEnd {
- isAppWindowVisible(splitScreenApp.component)
- }
- }
-
- @FlakyTest
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0), // bugId = 179116910
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
- )
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
deleted file mode 100644
index 2f9244be9c18..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test enter split screen from a detached recent task
- *
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenFromDetachedRecentTask`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@Group4
-class EnterSplitScreenFromDetachedRecentTask(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
-
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- cleanSetup(this)
- setup {
- eachRun {
- splitScreenApp.launchViaIntent(wmHelper)
- // Press back to remove the task, but it should still be shown in recent.
- device.pressBack()
- }
- }
- transitions {
- device.launchSplitScreen(wmHelper)
- }
- }
-
- override val ignoredWindows: List<FlickerComponentName>
- get() = listOf(LAUNCHER_COMPONENT,
- FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT,
- splitScreenApp.component)
-
- @Presubmit
- @Test
- fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
-
- @Presubmit
- @Test
- fun appWindowIsVisible() {
- testSpec.assertWmEnd {
- isAppWindowVisible(splitScreenApp.component)
- }
- }
-
- @Presubmit
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
- )
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
deleted file mode 100644
index 1740c3ec24ca..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open activity to primary split screen and dock secondary activity to side
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenLaunchToSide`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group4
-class EnterSplitScreenLaunchToSide(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- transitions {
- device.launchSplitScreen(wmHelper)
- device.reopenAppFromOverview(wmHelper)
- }
- }
-
- override val ignoredWindows: List<FlickerComponentName>
- get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
- secondaryApp.component, FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT)
-
- @Presubmit
- @Test
- fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
- splitScreenApp.component)
-
- @Presubmit
- @Test
- fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.startRotation,
- secondaryApp.component)
-
- @Presubmit
- @Test
- fun dockedStackDividerBecomesVisible() = testSpec.dockedStackDividerBecomesVisible()
-
- @Presubmit
- @Test
- fun appWindowBecomesVisible() {
- testSpec.assertWm {
- // when the app is launched, first the activity becomes visible, then the
- // SnapshotStartingWindow appears and then the app window becomes visible.
- // Because we log WM once per frame, sometimes the activity and the window
- // become visible in the same entry, sometimes not, thus it is not possible to
- // assert the visibility of the activity here
- this.isAppWindowInvisible(secondaryApp.component)
- .then()
- // during re-parenting, the window may disappear and reappear from the
- // trace, this occurs because we log only 1x per frame
- .notContains(secondaryApp.component, isOptional = true)
- .then()
- .isAppWindowVisible(secondaryApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0) // bugId = 175687842
- )
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
deleted file mode 100644
index 4c063b918e96..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.canSplitScreen
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.After
-import org.junit.Assert
-import org.junit.Before
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test enter split screen from non-resizable activity. When the device doesn't support
- * non-resizable in multi window, there should be no button to enter split screen for non-resizable
- * activity.
- *
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenNotSupportNonResizable`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@Group4
-class EnterSplitScreenNotSupportNonResizable(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
-
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- cleanSetup(this)
- setup {
- eachRun {
- nonResizeableApp.launchViaIntent(wmHelper)
- }
- }
- transitions {
- if (device.canSplitScreen(wmHelper)) {
- Assert.fail("Non-resizeable app should not enter split screen")
- }
- }
- }
-
- override val ignoredWindows: List<FlickerComponentName>
- get() = listOf(LAUNCHER_COMPONENT,
- FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT,
- nonResizeableApp.component,
- splitScreenApp.component)
-
- @Before
- override fun setup() {
- super.setup()
- setSupportsNonResizableMultiWindow(instrumentation, -1)
- }
-
- @After
- override fun teardown() {
- super.teardown()
- resetMultiWindowConfig(instrumentation)
- }
-
- @Presubmit
- @Test
- fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
deleted file mode 100644
index f75dee619564..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.After
-import org.junit.Before
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test enter split screen from non-resizable activity. When the device supports
- * non-resizable in multi window, there should be a button to enter split screen for non-resizable
- * activity.
- *
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenSupportNonResizable`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@Group2
-class EnterSplitScreenSupportNonResizable(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
-
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- cleanSetup(this)
- setup {
- eachRun {
- nonResizeableApp.launchViaIntent(wmHelper)
- }
- }
- transitions {
- device.launchSplitScreen(wmHelper)
- }
- }
-
- override val ignoredWindows: List<FlickerComponentName>
- get() = listOf(LAUNCHER_COMPONENT,
- FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT,
- nonResizeableApp.component,
- splitScreenApp.component)
-
- @Before
- override fun setup() {
- super.setup()
- setSupportsNonResizableMultiWindow(instrumentation, 1)
- }
-
- @After
- override fun teardown() {
- super.teardown()
- resetMultiWindowConfig(instrumentation)
- }
-
- @Presubmit
- @Test
- fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
-
- @Presubmit
- @Test
- fun appWindowIsVisible() {
- testSpec.assertWmEnd {
- isAppWindowVisible(nonResizeableApp.component)
- }
- }
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
deleted file mode 100644
index ef7d65e8a732..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open resizeable activity split in primary, and drag divider to bottom exit split screen
- * To run this test: `atest WMShellFlickerTests:ExitLegacySplitScreenFromBottom`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class ExitLegacySplitScreenFromBottom(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- setup {
- eachRun {
- splitScreenApp.launchViaIntent(wmHelper)
- device.launchSplitScreen(wmHelper)
- }
- }
- teardown {
- eachRun {
- splitScreenApp.exit(wmHelper)
- }
- }
- transitions {
- device.exitSplitScreenFromBottom(wmHelper)
- }
- }
-
- override val ignoredWindows: List<FlickerComponentName>
- get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN,
- splitScreenApp.component, secondaryApp.component,
- FlickerComponentName.SNAPSHOT)
-
- @FlakyTest
- @Test
- fun layerBecomesInvisible() {
- testSpec.assertLayers {
- this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
- .then()
- .isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
- }
- }
-
- @FlakyTest
- @Test
- fun appWindowBecomesInVisible() {
- testSpec.assertWm {
- this.isAppWindowVisible(secondaryApp.component)
- .then()
- .isAppWindowInvisible(secondaryApp.component)
- }
- }
-
- @FlakyTest
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @FlakyTest
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @FlakyTest
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @FlakyTest
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0) // b/175687842
- )
- }
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
deleted file mode 100644
index d913a6d85d3d..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test dock activity to primary split screen, and open secondary to side, exit primary split
- * and test secondary activity become full screen.
- * To run this test: `atest WMShellFlickerTests:ExitPrimarySplitScreenShowSecondaryFullscreen`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class ExitPrimarySplitScreenShowSecondaryFullscreen(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- teardown {
- eachRun {
- secondaryApp.exit(wmHelper)
- }
- }
- transitions {
- splitScreenApp.launchViaIntent(wmHelper)
- secondaryApp.launchViaIntent(wmHelper)
- device.launchSplitScreen(wmHelper)
- device.reopenAppFromOverview(wmHelper)
- // TODO(b/175687842) Can not find Split screen divider, use exit() instead
- splitScreenApp.exit(wmHelper)
- }
- }
-
- override val ignoredWindows: List<FlickerComponentName>
- get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN,
- splitScreenApp.component, secondaryApp.component,
- FlickerComponentName.SNAPSHOT)
-
- @Presubmit
- @Test
- fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
-
- @FlakyTest
- @Test
- fun layerBecomesInvisible() {
- testSpec.assertLayers {
- this.isVisible(splitScreenApp.component)
- .then()
- .isInvisible(splitScreenApp.component)
- }
- }
-
- @FlakyTest
- @Test
- fun appWindowBecomesInVisible() {
- testSpec.assertWm {
- this.isAppWindowVisible(splitScreenApp.component)
- .then()
- .isAppWindowInvisible(splitScreenApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
- )
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
deleted file mode 100644
index f3ff7b156aaf..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
-import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.After
-import org.junit.Before
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test launch non-resizable activity via intent in split screen mode. When the device does not
- * support non-resizable in multi window, it should trigger exit split screen.
- * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromIntentNotSupportNonResizable`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class LegacySplitScreenFromIntentNotSupportNonResizable(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
-
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- cleanSetup(this)
- setup {
- eachRun {
- splitScreenApp.launchViaIntent(wmHelper)
- device.launchSplitScreen(wmHelper)
- }
- }
- transitions {
- nonResizeableApp.launchViaIntent(wmHelper)
- wmHelper.waitForAppTransitionIdle()
- }
- }
-
- override val ignoredWindows: List<FlickerComponentName>
- get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
- nonResizeableApp.component, splitScreenApp.component,
- FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT)
-
- @Before
- override fun setup() {
- super.setup()
- setSupportsNonResizableMultiWindow(instrumentation, -1)
- }
-
- @After
- override fun teardown() {
- super.teardown()
- resetMultiWindowConfig(instrumentation)
- }
-
- @Presubmit
- @Test
- fun resizableAppLayerBecomesInvisible() {
- testSpec.assertLayers {
- this.isVisible(splitScreenApp.component)
- .then()
- .isInvisible(splitScreenApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun nonResizableAppLayerBecomesVisible() {
- testSpec.assertLayers {
- this.notContains(nonResizeableApp.component)
- .then()
- .isInvisible(nonResizeableApp.component)
- .then()
- .isVisible(nonResizeableApp.component)
- }
- }
-
- /**
- * Assets that [splitScreenApp] exists at the start of the trace and, once it becomes
- * invisible, it remains invisible until the end of the trace.
- */
- @Presubmit
- @Test
- fun resizableAppWindowBecomesInvisible() {
- testSpec.assertWm {
- // when the activity gets PAUSED the window may still be marked as visible
- // it will be updated in the next log entry. This occurs because we record 1x
- // per frame, thus ignore activity check here
- this.isAppWindowVisible(splitScreenApp.component)
- .then()
- // immediately after the window (after onResume and before perform relayout)
- // the activity is invisible. This may or not be logged, since we record 1x
- // per frame, thus ignore activity check here
- .isAppWindowInvisible(splitScreenApp.component)
- }
- }
-
- /**
- * Assets that [nonResizeableApp] doesn't exist at the start of the trace, then
- * [nonResizeableApp] is created (visible or not) and, once [nonResizeableApp] becomes
- * visible, it remains visible until the end of the trace.
- */
- @Presubmit
- @Test
- fun nonResizableAppWindowBecomesVisible() {
- testSpec.assertWm {
- this.notContains(nonResizeableApp.component)
- .then()
- // we log once per frame, upon logging, window may be visible or not depending
- // on what was processed until that moment. Both behaviors are correct
- .isAppWindowInvisible(nonResizeableApp.component, isOptional = true)
- .then()
- // immediately after the window (after onResume and before perform relayout)
- // the activity is invisible. This may or not be logged, since we record 1x
- // per frame, thus ignore activity check here
- .isAppWindowVisible(nonResizeableApp.component)
- }
- }
-
- /**
- * Asserts that both the app window and the activity are visible at the end of the trace
- */
- @Presubmit
- @Test
- fun nonResizableAppWindowBecomesVisibleAtEnd() {
- testSpec.assertWmEnd {
- isAppWindowVisible(nonResizeableApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
-
- @Presubmit
- @Test
- fun onlyNonResizableAppWindowIsVisibleAtEnd() {
- testSpec.assertWmEnd {
- isAppWindowInvisible(splitScreenApp.component)
- isAppWindowVisible(nonResizeableApp.component)
- }
- }
-
- @Presubmit
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
deleted file mode 100644
index 42e707ab0850..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
-import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.After
-import org.junit.Before
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test launch non-resizable activity via intent in split screen mode. When the device supports
- * non-resizable in multi window, it should show the non-resizable app in split screen.
- * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromIntentSupportNonResizable`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class LegacySplitScreenFromIntentSupportNonResizable(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
-
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- cleanSetup(this)
- setup {
- eachRun {
- splitScreenApp.launchViaIntent(wmHelper)
- device.launchSplitScreen(wmHelper)
- }
- }
- transitions {
- nonResizeableApp.launchViaIntent(wmHelper)
- wmHelper.waitForAppTransitionIdle()
- }
- }
-
- override val ignoredWindows: List<FlickerComponentName>
- get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
- nonResizeableApp.component, splitScreenApp.component,
- FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT)
-
- @Before
- override fun setup() {
- super.setup()
- setSupportsNonResizableMultiWindow(instrumentation, 1)
- }
-
- @After
- override fun teardown() {
- super.teardown()
- resetMultiWindowConfig(instrumentation)
- }
-
- @Presubmit
- @Test
- fun nonResizableAppLayerBecomesVisible() {
- testSpec.assertLayers {
- this.isInvisible(nonResizeableApp.component)
- .then()
- .isVisible(nonResizeableApp.component)
- }
- }
-
- /**
- * Assets that [nonResizeableApp] doesn't exist at the start of the trace, then
- * [nonResizeableApp] is created (visible or not) and, once [nonResizeableApp] becomes
- * visible, it remains visible until the end of the trace.
- */
- @Presubmit
- @Test
- fun nonResizableAppWindowBecomesVisible() {
- testSpec.assertWm {
- this.notContains(nonResizeableApp.component)
- .then()
- // we log once per frame, upon logging, window may be visible or not depending
- // on what was processed until that moment. Both behaviors are correct
- .isAppWindowInvisible(nonResizeableApp.component, isOptional = true)
- .then()
- // immediately after the window (after onResume and before perform relayout)
- // the activity is invisible. This may or not be logged, since we record 1x
- // per frame, thus ignore activity check here
- .isAppWindowVisible(nonResizeableApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
-
- @Presubmit
- @Test
- fun bothAppsWindowsAreVisibleAtEnd() {
- testSpec.assertWmEnd {
- isAppWindowVisible(splitScreenApp.component)
- isAppWindowVisible(nonResizeableApp.component)
- }
- }
-
- @Presubmit
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
deleted file mode 100644
index c1fba7d1530c..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
-import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.After
-import org.junit.Before
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test launch non-resizable activity via recent overview in split screen mode. When the device does
- * not support non-resizable in multi window, it should trigger exit split screen.
- * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromRecentNotSupportNonResizable`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class LegacySplitScreenFromRecentNotSupportNonResizable(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
-
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- cleanSetup(this)
- setup {
- eachRun {
- nonResizeableApp.launchViaIntent(wmHelper)
- splitScreenApp.launchViaIntent(wmHelper)
- device.launchSplitScreen(wmHelper)
- }
- }
- transitions {
- device.reopenAppFromOverview(wmHelper)
- }
- }
-
- override val ignoredWindows: List<FlickerComponentName>
- get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
- TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
- FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT)
-
- @Before
- override fun setup() {
- super.setup()
- setSupportsNonResizableMultiWindow(instrumentation, -1)
- }
-
- @After
- override fun teardown() {
- super.teardown()
- resetMultiWindowConfig(instrumentation)
- }
-
- @Presubmit
- @Test
- fun resizableAppLayerBecomesInvisible() {
- testSpec.assertLayers {
- this.isVisible(splitScreenApp.component)
- .then()
- .isInvisible(splitScreenApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun nonResizableAppLayerBecomesVisible() {
- testSpec.assertLayers {
- this.isInvisible(nonResizeableApp.component)
- .then()
- .isVisible(nonResizeableApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun resizableAppWindowBecomesInvisible() {
- testSpec.assertWm {
- // when the activity gets PAUSED the window may still be marked as visible
- // it will be updated in the next log entry. This occurs because we record 1x
- // per frame, thus ignore activity check here
- this.isAppWindowVisible(splitScreenApp.component)
- .then()
- // immediately after the window (after onResume and before perform relayout)
- // the activity is invisible. This may or not be logged, since we record 1x
- // per frame, thus ignore activity check here
- .isAppWindowInvisible(splitScreenApp.component)
- }
- }
-
- @FlakyTest
- @Test
- fun nonResizableAppWindowBecomesVisible() {
- testSpec.assertWm {
- this.isAppWindowInvisible(nonResizeableApp.component)
- .then()
- .isAppWindowVisible(nonResizeableApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
-
- @Presubmit
- @Test
- fun onlyNonResizableAppWindowIsVisibleAtEnd() {
- testSpec.assertWmEnd {
- isAppWindowInvisible(splitScreenApp.component)
- isAppWindowVisible(nonResizeableApp.component)
- }
- }
-
- @Presubmit
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
deleted file mode 100644
index 6ac8683ac054..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
-import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.After
-import org.junit.Before
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test launch non-resizable activity via recent overview in split screen mode. When the device
- * supports non-resizable in multi window, it should show the non-resizable app in split screen.
- * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromRecentSupportNonResizable`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class LegacySplitScreenFromRecentSupportNonResizable(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
-
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- cleanSetup(this)
- setup {
- eachRun {
- nonResizeableApp.launchViaIntent(wmHelper)
- splitScreenApp.launchViaIntent(wmHelper)
- device.launchSplitScreen(wmHelper)
- }
- }
- transitions {
- device.reopenAppFromOverview(wmHelper)
- }
- }
-
- override val ignoredWindows: List<FlickerComponentName>
- get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
- TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
- FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT)
-
- @Before
- override fun setup() {
- super.setup()
- setSupportsNonResizableMultiWindow(instrumentation, 1)
- }
-
- @After
- override fun teardown() {
- super.teardown()
- resetMultiWindowConfig(instrumentation)
- }
-
- @Presubmit
- @Test
- fun nonResizableAppLayerBecomesVisible() {
- testSpec.assertLayers {
- this.isInvisible(nonResizeableApp.component)
- .then()
- .isVisible(nonResizeableApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun nonResizableAppWindowBecomesVisible() {
- testSpec.assertWm {
- // when the app is launched, first the activity becomes visible, then the
- // SnapshotStartingWindow appears and then the app window becomes visible.
- // Because we log WM once per frame, sometimes the activity and the window
- // become visible in the same entry, sometimes not, thus it is not possible to
- // assert the visibility of the activity here
- this.isAppWindowInvisible(nonResizeableApp.component)
- .then()
- // during re-parenting, the window may disappear and reappear from the
- // trace, this occurs because we log only 1x per frame
- .notContains(nonResizeableApp.component, isOptional = true)
- .then()
- // if the window reappears after re-parenting it will most likely not
- // be visible in the first log entry (because we log only 1x per frame)
- .isAppWindowInvisible(nonResizeableApp.component, isOptional = true)
- .then()
- .isAppWindowVisible(nonResizeableApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
-
- @Presubmit
- @Test
- fun bothAppsWindowsAreVisibleAtEnd() {
- testSpec.assertWmEnd {
- isAppWindowVisible(splitScreenApp.component)
- isAppWindowVisible(nonResizeableApp.component)
- }
- }
-
- @Presubmit
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
deleted file mode 100644
index b01f41c9e2ec..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.view.Surface
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-
-abstract class LegacySplitScreenRotateTransition(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
- device.openQuickStepAndClearRecentAppsFromOverview(wmHelper)
- secondaryApp.launchViaIntent(wmHelper)
- splitScreenApp.launchViaIntent(wmHelper)
- }
- }
- teardown {
- eachRun {
- splitScreenApp.exit(wmHelper)
- secondaryApp.exit(wmHelper)
- this.setRotation(Surface.ROTATION_0)
- }
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
deleted file mode 100644
index fb1004bda0cb..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.helpers.exitSplitScreen
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
-import com.android.wm.shell.flicker.helpers.SimpleAppHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:LegacySplitScreenToLauncher`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class LegacySplitScreenToLauncher(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
- private val testApp = SimpleAppHelper(instrumentation)
-
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- device.openQuickStepAndClearRecentAppsFromOverview(wmHelper)
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- this.setRotation(testSpec.endRotation)
- device.launchSplitScreen(wmHelper)
- device.waitForIdle()
- }
- }
- teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
- }
- transitions {
- device.exitSplitScreen()
- }
- }
-
- override val ignoredWindows: List<FlickerComponentName>
- get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT)
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- @FlakyTest(bugId = 206753786)
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- @Presubmit
- @Test
- fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
-
- @FlakyTest
- @Test
- fun dockedStackDividerBecomesInvisible() = testSpec.dockedStackDividerBecomesInvisible()
-
- @FlakyTest
- @Test
- fun layerBecomesInvisible() {
- testSpec.assertLayers {
- this.isVisible(testApp.component)
- .then()
- .isInvisible(testApp.component)
- }
- }
-
- @FlakyTest
- @Test
- fun focusDoesNotChange() {
- testSpec.assertEventLog {
- this.focusDoesNotChange()
- }
- }
-
- @Presubmit
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- // b/161435597 causes the test not to work on 90 degrees
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0))
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
deleted file mode 100644
index a4a1f617e497..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.app.Instrumentation
-import android.content.Context
-import android.support.test.launcherhelper.LauncherStrategyFactory
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
-import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.After
-import org.junit.Assume.assumeFalse
-import org.junit.Assume.assumeTrue
-import org.junit.Before
-import org.junit.Test
-
-abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestParameter) {
- protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- protected val context: Context = instrumentation.context
- protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
- protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
- protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
- protected val LAUNCHER_COMPONENT = FlickerComponentName("",
- LauncherStrategyFactory.getInstance(instrumentation)
- .launcherStrategy.supportedLauncherPackage)
- private var prevDevEnableNonResizableMultiWindow = 0
-
- @Before
- open fun setup() {
- // Only run legacy split tests when the system is using legacy split screen.
- assumeTrue(SplitScreenHelper.isUsingLegacySplit())
- // Legacy split is having some issue with Shell transition, and will be deprecated soon.
- assumeFalse(isShellTransitionsEnabled())
- prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context)
- if (prevDevEnableNonResizableMultiWindow != 0) {
- // Turn off the development option
- setDevEnableNonResizableMultiWindow(context, 0)
- }
- }
-
- @After
- open fun teardown() {
- setDevEnableNonResizableMultiWindow(context, prevDevEnableNonResizableMultiWindow)
- }
-
- /**
- * List of windows that are ignored when verifying that visible elements appear on 2
- * consecutive entries in the trace.
- *
- * b/182720234
- */
- open val ignoredWindows: List<FlickerComponentName> = listOf(
- FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT)
-
- protected open val transition: FlickerBuilder.() -> Unit
- get() = {
- setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
- device.openQuickStepAndClearRecentAppsFromOverview(wmHelper)
- secondaryApp.launchViaIntent(wmHelper)
- splitScreenApp.launchViaIntent(wmHelper)
- this.setRotation(testSpec.startRotation)
- }
- }
- teardown {
- eachRun {
- secondaryApp.exit(wmHelper)
- splitScreenApp.exit(wmHelper)
- this.setRotation(Surface.ROTATION_0)
- }
- }
- }
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- transition(this)
- }
- }
-
- internal open val cleanSetup: FlickerBuilder.() -> Unit
- get() = {
- setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
- device.openQuickStepAndClearRecentAppsFromOverview(wmHelper)
- this.setRotation(testSpec.startRotation)
- }
- }
- teardown {
- eachRun {
- nonResizeableApp.exit(wmHelper)
- splitScreenApp.exit(wmHelper)
- device.pressHome()
- this.setRotation(Surface.ROTATION_0)
- }
- }
- }
-
- @FlakyTest(bugId = 178447631)
- @Test
- open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoredWindows)
- }
- }
-
- @FlakyTest(bugId = 178447631)
- @Test
- open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoredWindows)
- }
- }
-
- companion object {
- internal val LIVE_WALLPAPER_COMPONENT = FlickerComponentName("",
- "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2")
- internal val LETTERBOX_COMPONENT = FlickerComponentName("", "Letterbox")
- internal val TOAST_COMPONENT = FlickerComponentName("", "Toast")
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS
deleted file mode 100644
index 8446b37dbf06..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# window manager > wm shell > Split Screen
-# Bug component: 928697
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
deleted file mode 100644
index 087b21c544c5..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:OpenAppToLegacySplitScreen`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class OpenAppToLegacySplitScreen(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- transitions {
- device.launchSplitScreen(wmHelper)
- wmHelper.waitForAppTransitionIdle()
- }
- }
-
- override val ignoredWindows: List<FlickerComponentName>
- get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
- FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT)
-
- @FlakyTest
- @Test
- fun appWindowBecomesVisible() {
- testSpec.assertWm {
- this.isAppWindowInvisible(splitScreenApp.component)
- .then()
- .isAppWindowVisible(splitScreenApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- @Presubmit
- @Test
- fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun appPairsDividerBecomesVisible() = testSpec.appPairsDividerBecomesVisible()
-
- @FlakyTest
- @Test
- fun layerBecomesVisible() {
- testSpec.assertLayers {
- this.isInvisible(splitScreenApp.component)
- .then()
- .isVisible(splitScreenApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun focusChanges() {
- testSpec.assertEventLog {
- this.focusChanges(splitScreenApp.`package`,
- "recents_animation_input_consumer", "NexusLauncherActivity")
- }
- }
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
- )
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
deleted file mode 100644
index e2da1a4565c0..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.util.Rational
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import androidx.test.uiautomator.By
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.resizeSplitScreen
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.region.Region
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
-import com.android.wm.shell.flicker.helpers.SimpleAppHelper
-import com.android.wm.shell.flicker.testapp.Components
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test split screen resizing window transitions.
- * To run this test: `atest WMShellFlickerTests:ResizeLegacySplitScreen`
- *
- * Currently it runs only in 0 degrees because of b/156100803
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 159096424)
-@Group2
-class ResizeLegacySplitScreen(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenTransition(testSpec) {
- private val testAppTop = SimpleAppHelper(instrumentation)
- private val testAppBottom = ImeAppHelper(instrumentation)
-
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(testSpec.startRotation)
- this.launcherStrategy.clearRecentAppsFromOverview()
- testAppBottom.launchViaIntent(wmHelper)
- device.pressHome()
- testAppTop.launchViaIntent(wmHelper)
- device.waitForIdle()
- device.launchSplitScreen(wmHelper)
- val snapshot =
- device.findObject(By.res(device.launcherPackageName, "snapshot"))
- snapshot.click()
- testAppBottom.openIME(device)
- device.pressBack()
- device.resizeSplitScreen(startRatio)
- }
- }
- teardown {
- eachRun {
- testAppTop.exit(wmHelper)
- testAppBottom.exit(wmHelper)
- }
- }
- transitions {
- device.resizeSplitScreen(stopRatio)
- }
- }
-
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @FlakyTest(bugId = 156223549)
- @Test
- fun topAppWindowIsAlwaysVisible() {
- testSpec.assertWm {
- this.isAppWindowVisible(Components.SimpleActivity.COMPONENT.toFlickerComponent())
- }
- }
-
- @FlakyTest(bugId = 156223549)
- @Test
- fun bottomAppWindowIsAlwaysVisible() {
- testSpec.assertWm {
- this.isAppWindowVisible(Components.ImeActivity.COMPONENT.toFlickerComponent())
- }
- }
-
- @Test
- fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
-
- @Test
- fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
-
- @Test
- fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- @FlakyTest(bugId = 206753786)
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- @Test
- fun topAppLayerIsAlwaysVisible() {
- testSpec.assertLayers {
- this.isVisible(Components.SimpleActivity.COMPONENT.toFlickerComponent())
- }
- }
-
- @Test
- fun bottomAppLayerIsAlwaysVisible() {
- testSpec.assertLayers {
- this.isVisible(Components.ImeActivity.COMPONENT.toFlickerComponent())
- }
- }
-
- @Test
- fun dividerLayerIsAlwaysVisible() {
- testSpec.assertLayers {
- this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
- }
- }
-
- @FlakyTest
- @Test
- fun appsStartingBounds() {
- testSpec.assertLayersStart {
- val displayBounds = WindowUtils.displayBounds
- val dividerBounds =
- layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds
-
- val topAppBounds = Region.from(0, 0, dividerBounds.right,
- dividerBounds.top + WindowUtils.dockedStackDividerInset)
- val bottomAppBounds = Region.from(0,
- dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
- displayBounds.right,
- displayBounds.bottom - WindowUtils.navigationBarFrameHeight)
- visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent())
- .coversExactly(topAppBounds)
- visibleRegion(Components.ImeActivity.COMPONENT.toFlickerComponent())
- .coversExactly(bottomAppBounds)
- }
- }
-
- @FlakyTest
- @Test
- fun appsEndingBounds() {
- testSpec.assertLayersStart {
- val displayBounds = WindowUtils.displayBounds
- val dividerBounds =
- layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds
-
- val topAppBounds = Region.from(0, 0, dividerBounds.right,
- dividerBounds.top + WindowUtils.dockedStackDividerInset)
- val bottomAppBounds = Region.from(0,
- dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
- displayBounds.right,
- displayBounds.bottom - WindowUtils.navigationBarFrameHeight)
-
- visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent())
- .coversExactly(topAppBounds)
- visibleRegion(Components.ImeActivity.COMPONENT.toFlickerComponent())
- .coversExactly(bottomAppBounds)
- }
- }
-
- @Test
- fun focusDoesNotChange() {
- testSpec.assertEventLog {
- focusDoesNotChange()
- }
- }
-
- companion object {
- private val startRatio = Rational(1, 3)
- private val stopRatio = Rational(2, 3)
-
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0))
- .map {
- val description = (startRatio.toString().replace("/", "-") + "_to_" +
- stopRatio.toString().replace("/", "-"))
- val newName = "${FlickerTestParameter.defaultName(it)}_$description"
- FlickerTestParameter(it.config, nameOverride = newName)
- }
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
deleted file mode 100644
index d703ea082c87..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test dock activity to primary split screen and rotate
- * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppAndEnterSplitScreen`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class RotateOneLaunchedAppAndEnterSplitScreen(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenRotateTransition(testSpec) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- transitions {
- device.launchSplitScreen(wmHelper)
- this.setRotation(testSpec.startRotation)
- }
- }
-
- @Presubmit
- @Test
- fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
-
- @Presubmit
- @Test
- fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
- splitScreenApp.component)
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- @FlakyTest(bugId = 206753786)
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @FlakyTest
- @Test
- fun appWindowBecomesVisible() {
- testSpec.assertWm {
- this.isAppWindowInvisible(splitScreenApp.component)
- .then()
- .isAppWindowVisible(splitScreenApp.component)
- }
- }
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
deleted file mode 100644
index 6b1883914e59..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Rotate
- * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppInSplitScreenMode`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class RotateOneLaunchedAppInSplitScreenMode(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenRotateTransition(testSpec) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- transitions {
- this.setRotation(testSpec.startRotation)
- device.launchSplitScreen(wmHelper)
- }
- }
-
- @Presubmit
- @Test
- fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
-
- @Presubmit
- @Test
- fun dockedStackPrimaryBoundsIsVisibleAtEnd() = testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(
- testSpec.startRotation, splitScreenApp.component)
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- @FlakyTest(bugId = 206753786)
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @FlakyTest
- @Test
- fun appWindowBecomesVisible() {
- testSpec.assertWm {
- this.isAppWindowInvisible(splitScreenApp.component)
- .then()
- .isAppWindowVisible(splitScreenApp.component)
- }
- }
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
deleted file mode 100644
index acd658b5ba56..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppAndEnterSplitScreen`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class RotateTwoLaunchedAppAndEnterSplitScreen(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenRotateTransition(testSpec) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- transitions {
- this.setRotation(testSpec.startRotation)
- device.launchSplitScreen(wmHelper)
- device.reopenAppFromOverview(wmHelper)
- }
- }
-
- @Presubmit
- @Test
- fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
-
- @Presubmit
- @Test
- fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
- splitScreenApp.component)
-
- @Presubmit
- @Test
- fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.startRotation,
- secondaryApp.component)
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- @FlakyTest(bugId = 206753786)
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- @Presubmit
- @Test
- fun appWindowBecomesVisible() {
- testSpec.assertWm {
- // when the app is launched, first the activity becomes visible, then the
- // SnapshotStartingWindow appears and then the app window becomes visible.
- // Because we log WM once per frame, sometimes the activity and the window
- // become visible in the same entry, sometimes not, thus it is not possible to
- // assert the visibility of the activity here
- this.isAppWindowInvisible(secondaryApp.component)
- .then()
- // during re-parenting, the window may disappear and reappear from the
- // trace, this occurs because we log only 1x per frame
- .notContains(secondaryApp.component, isOptional = true)
- .then()
- // if the window reappears after re-parenting it will most likely not
- // be visible in the first log entry (because we log only 1x per frame)
- .isAppWindowInvisible(secondaryApp.component, isOptional = true)
- .then()
- .isAppWindowVisible(secondaryApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
deleted file mode 100644
index b40be8b5f401..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.legacysplitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test open app to split screen.
- * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppInSplitScreenMode`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class RotateTwoLaunchedAppInSplitScreenMode(
- testSpec: FlickerTestParameter
-) : LegacySplitScreenRotateTransition(testSpec) {
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- setup {
- eachRun {
- device.launchSplitScreen(wmHelper)
- device.reopenAppFromOverview(wmHelper)
- this.setRotation(testSpec.startRotation)
- }
- }
- transitions {
- this.setRotation(testSpec.startRotation)
- }
- }
-
- @Presubmit
- @Test
- fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
-
- @Presubmit
- @Test
- fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
- splitScreenApp.component)
-
- @Presubmit
- @Test
- fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.startRotation,
- secondaryApp.component)
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- @FlakyTest(bugId = 206753786)
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- @FlakyTest
- @Test
- fun appWindowBecomesVisible() {
- testSpec.assertWm {
- this.isAppWindowInvisible(secondaryApp.component)
- .then()
- .isAppWindowVisible(secondaryApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
new file mode 100644
index 000000000000..ce624f2b5bbe
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.flicker.pip
+
+import android.platform.test.annotations.FlakyTest
+import androidx.test.filters.RequiresDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import org.junit.Assume
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test entering pip from an app via auto-enter property when navigating to home.
+ *
+ * To run this test: `atest WMShellFlickerTests:AutoEnterPipOnGoToHomeTest`
+ *
+ * Actions:
+ * Launch an app in full screen
+ * Select "Auto-enter PiP" radio button
+ * Press Home button or swipe up to go Home and put [pipApp] in pip mode
+ *
+ * Notes:
+ * 1. All assertions are inherited from [EnterPipTest]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 238367575)
+@Group3
+class AutoEnterPipOnGoToHomeTest(testSpec: FlickerTestParameter) : EnterPipTest(testSpec) {
+ protected val taplInstrumentation = LauncherInstrumentation()
+ /**
+ * Defines the transition used to run the test
+ */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setupAndTeardown(this)
+ setup {
+ eachRun {
+ pipApp.launchViaIntent(wmHelper)
+ pipApp.enableAutoEnterForPipActivity()
+ }
+ }
+ teardown {
+ eachRun {
+ // close gracefully so that onActivityUnpinned() can be called before force exit
+ pipApp.closePipWindow(wmHelper)
+ pipApp.exit(wmHelper)
+ }
+ }
+ transitions {
+ taplInstrumentation.goHome()
+ }
+ }
+
+ override fun pipLayerReduces() {
+ val layerName = pipApp.component.toLayerName()
+ testSpec.assertLayers {
+ val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
+ pipLayerList.zipWithNext { previous, current ->
+ current.visibleRegion.notBiggerThan(previous.visibleRegion.region)
+ }
+ }
+ }
+
+ /**
+ * Checks that [pipApp] window is animated towards default position in right bottom corner
+ */
+ @Test
+ fun pipLayerMovesTowardsRightBottomCorner() {
+ // in gestural nav the swipe makes PiP first go upwards
+ Assume.assumeFalse(testSpec.isGesturalNavigation)
+ val layerName = pipApp.component.toLayerName()
+ testSpec.assertLayers {
+ val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
+ // Pip animates towards the right bottom corner, but because it is being resized at the
+ // same time, it is possible it shrinks first quickly below the default position and get
+ // moved up after that in just few last frames
+ pipLayerList.zipWithNext { previous, current ->
+ current.visibleRegion.isToTheRightBottom(previous.visibleRegion.region, 3)
+ }
+ }
+ }
+
+ override fun focusChanges() {
+ // in gestural nav the focus goes to different activity on swipe up
+ Assume.assumeFalse(testSpec.isGesturalNavigation)
+ super.focusChanges()
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
new file mode 100644
index 000000000000..953f59a1f70b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.flicker.pip
+
+import androidx.test.filters.RequiresDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import org.junit.Assume
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test entering pip from an app via [onUserLeaveHint] and by navigating to home.
+ *
+ * To run this test: `atest WMShellFlickerTests:EnterPipOnUserLeaveHintTest`
+ *
+ * Actions:
+ * Launch an app in full screen
+ * Select "Via code behind" radio button
+ * Press Home button or swipe up to go Home and put [pipApp] in pip mode
+ *
+ * Notes:
+ * 1. All assertions are inherited from [EnterPipTest]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group3
+class EnterPipOnUserLeaveHintTest(testSpec: FlickerTestParameter) : EnterPipTest(testSpec) {
+ protected val taplInstrumentation = LauncherInstrumentation()
+ /**
+ * Defines the transition used to run the test
+ */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setupAndTeardown(this)
+ setup {
+ eachRun {
+ pipApp.launchViaIntent(wmHelper)
+ pipApp.enableEnterPipOnUserLeaveHint()
+ }
+ }
+ teardown {
+ eachRun {
+ pipApp.exit(wmHelper)
+ }
+ }
+ transitions {
+ taplInstrumentation.goHome()
+ }
+ }
+
+ override fun pipAppLayerAlwaysVisible() {
+ if (!testSpec.isGesturalNavigation) super.pipAppLayerAlwaysVisible() else {
+ // pip layer in gesture nav will disappear during transition
+ testSpec.assertLayers {
+ this.isVisible(pipApp.component)
+ .then().isInvisible(pipApp.component)
+ .then().isVisible(pipApp.component)
+ }
+ }
+ }
+
+ override fun pipLayerReduces() {
+ // in gestural nav the pip enters through alpha animation
+ Assume.assumeFalse(testSpec.isGesturalNavigation)
+ super.pipLayerReduces()
+ }
+
+ override fun focusChanges() {
+ // in gestural nav the focus goes to different activity on swipe up
+ Assume.assumeFalse(testSpec.isGesturalNavigation)
+ super.focusChanges()
+ }
+
+ override fun pipLayerRemainInsideVisibleBounds() {
+ if (!testSpec.isGesturalNavigation) super.pipLayerRemainInsideVisibleBounds() else {
+ // pip layer in gesture nav will disappear during transition
+ testSpec.assertLayersStart {
+ this.visibleRegion(pipApp.component).coversAtMost(displayBounds)
+ }
+ testSpec.assertLayersEnd {
+ this.visibleRegion(pipApp.component).coversAtMost(displayBounds)
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 0640ac526bd0..9ba51661aa5f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -18,7 +18,6 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -43,7 +42,7 @@ import org.junit.runners.Parameterized
*
* Notes:
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
- * are inherited [PipTransition]
+ * are inherited from [PipTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
@@ -54,7 +53,7 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
-class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
/**
* Defines the transition used to run the test
@@ -77,11 +76,6 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
}
}
- /** {@inheritDoc} */
- @FlakyTest(bugId = 206753786)
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
/**
* Checks [pipApp] window remains visible throughout the animation
*/
@@ -98,7 +92,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
*/
@Presubmit
@Test
- fun pipAppLayerAlwaysVisible() {
+ open fun pipAppLayerAlwaysVisible() {
testSpec.assertLayers {
this.isVisible(pipApp.component)
}
@@ -122,7 +116,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
*/
@Presubmit
@Test
- fun pipLayerRemainInsideVisibleBounds() {
+ open fun pipLayerRemainInsideVisibleBounds() {
testSpec.assertLayersVisibleRegion(pipApp.component) {
coversAtMost(displayBounds)
}
@@ -133,7 +127,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
*/
@Presubmit
@Test
- fun pipLayerReduces() {
+ open fun pipLayerReduces() {
val layerName = pipApp.component.toLayerName()
testSpec.assertLayers {
val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
@@ -175,7 +169,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
*/
@Presubmit
@Test
- fun focusChanges() {
+ open fun focusChanges() {
testSpec.assertEventLog {
this.focusChanges(pipApp.`package`, "NexusLauncherActivity")
}
@@ -192,8 +186,10 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 3)
+ .getConfigNonRotationTests(
+ supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 3
+ )
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index accb524d3de1..f50097d4ce84 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -28,13 +28,12 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
+import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -114,14 +113,6 @@ class EnterPipToOtherOrientationTest(
override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
/**
- * Checks that the [FlickerComponentName.STATUS_BAR] has the correct position at
- * the start and end of the transition
- */
- @FlakyTest(bugId = 206753786)
- @Test
- override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- /**
* Checks that all parts of the screen are covered at the start and end of the transition
*
* TODO b/197726599 Prevents all states from being checked
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index a3ed79bf0409..0768e82e491c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -79,11 +79,6 @@ class ExitPipViaExpandButtonClickTest(
}
/** {@inheritDoc} */
- @FlakyTest(bugId = 206753786)
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
- /** {@inheritDoc} */
@FlakyTest(bugId = 197726610)
@Test
override fun pipLayerExpands() = super.pipLayerExpands()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index 37e9344348d9..c6a705dacb8d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -80,7 +80,17 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit
/** {@inheritDoc} */
@FlakyTest(bugId = 206753786)
@Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ override fun statusBarLayerRotatesScales() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales_ShellTransit() {
+ Assume.assumeTrue(isShellTransitionsEnabled)
+ super.statusBarLayerRotatesScales()
+ }
/** {@inheritDoc} */
@FlakyTest(bugId = 197726610)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index ab07ede5bb32..128703ad332c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -25,7 +25,6 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -78,10 +77,6 @@ class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransiti
@Test
override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible()
- @FlakyTest(bugId = 206753786)
- @Test
- override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
/**
* Checks that the focus doesn't change between windows during the transition
*/
@@ -108,4 +103,4 @@ class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransiti
repetitions = 3)
}
}
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt
index 1a21d32f568c..fe51228230cb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.flicker.pip
-import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -35,7 +35,6 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group4
-@FlakyTest(bugId = 217777115)
class PipKeyboardTestShellTransit(testSpec: FlickerTestParameter) : PipKeyboardTest(testSpec) {
@Before
@@ -43,7 +42,7 @@ class PipKeyboardTestShellTransit(testSpec: FlickerTestParameter) : PipKeyboardT
Assume.assumeTrue(isShellTransitionsEnabled)
}
- @FlakyTest(bugId = 214452854)
+ @Presubmit
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
deleted file mode 100644
index 21175a0767a5..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.flicker.pip
-
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
-import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.wm.shell.flicker.helpers.ImeAppHelper
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
-import org.junit.Assume.assumeFalse
-import org.junit.Assume.assumeTrue
-import org.junit.Before
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test Pip with split-screen.
- * To run this test: `atest WMShellFlickerTests:PipLegacySplitScreenTest`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group4
-class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
- private val imeApp = ImeAppHelper(instrumentation)
- private val testApp = FixedAppHelper(instrumentation)
-
- @Before
- open fun setup() {
- // Only run legacy split tests when the system is using legacy split screen.
- assumeTrue(SplitScreenHelper.isUsingLegacySplit())
- // Legacy split is having some issue with Shell transition, and will be deprecated soon.
- assumeFalse(isShellTransitionsEnabled())
- }
-
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- setup {
- test {
- removeAllTasksButHome()
- device.wakeUpAndGoToHomeScreen()
- pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true"),
- wmHelper = wmHelper)
- }
- }
- transitions {
- testApp.launchViaIntent(wmHelper)
- device.launchSplitScreen(wmHelper)
- imeApp.launchViaIntent(wmHelper)
- }
- teardown {
- eachRun {
- imeApp.exit(wmHelper)
- testApp.exit(wmHelper)
- }
- test {
- removeAllTasksButHome()
- }
- }
- }
-
- /** {@inheritDoc} */
- @FlakyTest(bugId = 206753786)
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
- @FlakyTest(bugId = 161435597)
- @Test
- fun pipWindowInsideDisplayBounds() {
- testSpec.assertWmVisibleRegion(pipApp.component) {
- coversAtMost(displayBounds)
- }
- }
-
- @Presubmit
- @Test
- fun bothAppWindowsVisible() {
- testSpec.assertWmEnd {
- isAppWindowVisible(testApp.component)
- isAppWindowVisible(imeApp.component)
- doNotOverlap(testApp.component, imeApp.component)
- }
- }
-
- @FlakyTest(bugId = 161435597)
- @Test
- fun pipLayerInsideDisplayBounds() {
- testSpec.assertLayersVisibleRegion(pipApp.component) {
- coversAtMost(displayBounds)
- }
- }
-
- @Presubmit
- @Test
- fun bothAppLayersVisible() {
- testSpec.assertLayersEnd {
- visibleRegion(testApp.component).coversAtMost(displayBounds)
- visibleRegion(imeApp.component).coversAtMost(displayBounds)
- }
- }
-
- @FlakyTest(bugId = 161435597)
- @Test
- override fun entireScreenCovered() = super.entireScreenCovered()
-
- companion object {
- const val TEST_REPETITIONS = 2
-
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = TEST_REPETITIONS
- )
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index c1ee1a7cbb35..9fad4997e63a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -27,12 +27,9 @@ import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -98,13 +95,6 @@ open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testS
override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
/**
- * Checks the position of the status bar at the start and end of the transition
- */
- @FlakyTest(bugId = 206753786)
- @Test
- override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- /**
* Checks that [fixedApp] layer is within [screenBoundsStart] at the start of the transition
*/
@Presubmit
@@ -141,14 +131,6 @@ open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testS
@Presubmit
@Test
fun pipLayerRotates_StartingBounds() {
- Assume.assumeFalse(isShellTransitionsEnabled)
- pipLayerRotates_StartingBounds_internal()
- }
-
- @FlakyTest(bugId = 228024285)
- @Test
- fun pipLayerRotates_StartingBounds_ShellTransit() {
- Assume.assumeTrue(isShellTransitionsEnabled)
pipLayerRotates_StartingBounds_internal()
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index e40f2bc1ed5a..51339a1deb4b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -25,9 +25,9 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.testapp.Components
@@ -113,10 +113,6 @@ open class SetRequestedOrientationWhilePinnedTest(
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @FlakyTest(bugId = 206753786)
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
@Presubmit
@Test
fun pipWindowInsideDisplay() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
new file mode 100644
index 000000000000..702710caded7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.appWindowBecomesVisible
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test enter split screen by dragging app icon from all apps.
+ * This test is only for large screen devices.
+ *
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromAllApps`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class EnterSplitScreenByDragFromAllApps(
+ testSpec: FlickerTestParameter
+) : SplitScreenBase(testSpec) {
+
+ @Before
+ open fun before() {
+ Assume.assumeTrue(taplInstrumentation.isTablet)
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ eachRun {
+ taplInstrumentation.goHome()
+ primaryApp.launchViaIntent(wmHelper)
+ }
+ }
+ transitions {
+ taplInstrumentation.launchedAppState.taskbar
+ .openAllApps()
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.component.packageName,
+ primaryApp.component.packageName)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun dividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp.component)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp.component)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ testSpec.endRotation, primaryApp.component, false /* splitLeftTop */)
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
+ testSpec.endRotation, secondaryApp.component, true /* splitLeftTop */)
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp.component)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() =
+ testSpec.appWindowBecomesVisible(secondaryApp.component)
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes =
+ listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
new file mode 100644
index 000000000000..7323d992ecd4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test enter split screen by dragging app icon from notification.
+ * This test is only for large screen devices.
+ *
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromNotification`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class EnterSplitScreenByDragFromNotification(
+ testSpec: FlickerTestParameter
+) : SplitScreenBase(testSpec) {
+
+ private val sendNotificationApp = SplitScreenHelper.getSendNotification(instrumentation)
+
+ @Before
+ fun before() {
+ Assume.assumeTrue(taplInstrumentation.isTablet)
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ eachRun {
+ // Send a notification
+ sendNotificationApp.launchViaIntent(wmHelper)
+ val sendNotification = device.wait(
+ Until.findObject(By.text("Send Notification")),
+ SplitScreenHelper.TIMEOUT_MS
+ )
+ sendNotification?.click() ?: error("Send notification button not found")
+
+ taplInstrumentation.goHome()
+ primaryApp.launchViaIntent(wmHelper)
+ }
+ }
+ transitions {
+ SplitScreenHelper.dragFromNotificationToSplit(instrumentation, device, wmHelper)
+ }
+ teardown {
+ eachRun {
+ sendNotificationApp.exit(wmHelper)
+ }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun dividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp.component)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible() =
+ testSpec.layerBecomesVisible(sendNotificationApp.component)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ testSpec.endRotation, primaryApp.component, false /* splitLeftTop */
+ )
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
+ testSpec.endRotation, sendNotificationApp.component, true /* splitLeftTop */
+ )
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp.component)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowIsVisibleAtEnd() =
+ testSpec.appWindowIsVisibleAtEnd(sendNotificationApp.component)
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes =
+ listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
new file mode 100644
index 000000000000..05c6e24ee89d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.appWindowBecomesVisible
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test enter split screen by dragging app icon from taskbar.
+ * This test is only for large screen devices.
+ *
+ * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromTaskbar`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class EnterSplitScreenByDragFromTaskbar(
+ testSpec: FlickerTestParameter
+) : SplitScreenBase(testSpec) {
+
+ @Before
+ fun before() {
+ Assume.assumeTrue(taplInstrumentation.isTablet)
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ eachRun {
+ taplInstrumentation.goHome()
+ SplitScreenHelper.createShortcutOnHotseatIfNotExist(
+ taplInstrumentation, secondaryApp.appName
+ )
+ primaryApp.launchViaIntent(wmHelper)
+ }
+ }
+ transitions {
+ taplInstrumentation.launchedAppState.taskbar
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(
+ secondaryApp.component.packageName,
+ primaryApp.component.packageName
+ )
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun dividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp.component)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp.component)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ testSpec.endRotation, primaryApp.component, false /* splitLeftTop */
+ )
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
+ testSpec.endRotation, secondaryApp.component, true /* splitLeftTop */
+ )
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp.component)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() =
+ testSpec.appWindowBecomesVisible(secondaryApp.component)
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedNavigationModes =
+ listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
new file mode 100644
index 000000000000..52c2daf96a3c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.flicker.splitscreen
+
+import android.app.Instrumentation
+import android.content.Context
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+
+abstract class SplitScreenBase(protected val testSpec: FlickerTestParameter) {
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ protected val taplInstrumentation = LauncherInstrumentation()
+ protected val context: Context = instrumentation.context
+ protected val primaryApp = SplitScreenHelper.getPrimary(instrumentation)
+ protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ transition(this)
+ }
+ }
+
+ protected open val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ test {
+ taplInstrumentation.setEnableRotation(true)
+ setRotation(testSpec.startRotation)
+ taplInstrumentation.setExpectedRotation(testSpec.startRotation)
+ }
+ }
+ teardown {
+ eachRun {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
index bd98585b67ec..bc0b0b6292b4 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
@@ -92,6 +92,17 @@
</intent-filter>
</activity>
+ <activity android:name=".SendNotificationActivity"
+ android:taskAffinity="com.android.wm.shell.flicker.testapp.SendNotificationActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:label="SendNotificationApp"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+
<activity android:name=".NonResizeableActivity"
android:resizeableActivity="false"
android:taskAffinity="com.android.wm.shell.flicker.testapp.NonResizeableActivity"
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_notification.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_notification.xml
new file mode 100644
index 000000000000..8d59b567e59b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_notification.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/black">
+
+ <Button
+ android:id="@+id/button_send_notification"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:text="Send Notification" />
+</RelativeLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml
index 909b77c87894..229098313afa 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml
@@ -44,6 +44,39 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
+ android:checkedButton="@id/enter_pip_on_leave_disabled">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Enter PiP on home press"/>
+
+ <RadioButton
+ android:id="@+id/enter_pip_on_leave_disabled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Disabled"
+ android:onClick="onAutoPipSelected"/>
+
+ <RadioButton
+ android:id="@+id/enter_pip_on_leave_manual"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Via code behind"
+ android:onClick="onAutoPipSelected"/>
+
+ <RadioButton
+ android:id="@+id/enter_pip_on_leave_autoenter"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Auto-enter PiP"
+ android:onClick="onAutoPipSelected"/>
+ </RadioGroup>
+
+ <RadioGroup
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
android:checkedButton="@id/ratio_default">
<TextView
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java
index 0ed59bdafd1d..a2b580da5898 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java
@@ -88,6 +88,12 @@ public class Components {
PACKAGE_NAME + ".SplitScreenSecondaryActivity");
}
+ public static class SendNotificationActivity {
+ public static final String LABEL = "SendNotificationApp";
+ public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
+ PACKAGE_NAME + ".SendNotificationActivity");
+ }
+
public static class LaunchBubbleActivity {
public static final String LABEL = "LaunchBubbleApp";
public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java
index a6ba7823e22d..615b1730579c 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java
@@ -48,6 +48,7 @@ import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.CheckBox;
+import android.widget.RadioButton;
import java.util.ArrayList;
import java.util.Arrays;
@@ -201,6 +202,17 @@ public class PipActivity extends Activity {
super.onDestroy();
}
+ @Override
+ protected void onUserLeaveHint() {
+ // Only used when auto PiP is disabled. This is to simulate the behavior that an app
+ // supports regular PiP but not auto PiP.
+ final boolean manuallyEnterPip =
+ ((RadioButton) findViewById(R.id.enter_pip_on_leave_manual)).isChecked();
+ if (manuallyEnterPip) {
+ enterPictureInPictureMode();
+ }
+ }
+
private RemoteAction buildRemoteAction(Icon icon, String label, String action) {
final Intent intent = new Intent(action);
final PendingIntent pendingIntent =
@@ -216,6 +228,21 @@ public class PipActivity extends Activity {
enterPictureInPictureMode(mPipParamsBuilder.build());
}
+ public void onAutoPipSelected(View v) {
+ switch (v.getId()) {
+ case R.id.enter_pip_on_leave_manual:
+ // disable auto enter PiP
+ case R.id.enter_pip_on_leave_disabled:
+ mPipParamsBuilder.setAutoEnterEnabled(false);
+ setPictureInPictureParams(mPipParamsBuilder.build());
+ break;
+ case R.id.enter_pip_on_leave_autoenter:
+ mPipParamsBuilder.setAutoEnterEnabled(true);
+ setPictureInPictureParams(mPipParamsBuilder.build());
+ break;
+ }
+ }
+
public void onRatioSelected(View v) {
switch (v.getId()) {
case R.id.ratio_default:
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SendNotificationActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SendNotificationActivity.java
new file mode 100644
index 000000000000..8020ef2270a0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SendNotificationActivity.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.flicker.testapp;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+public class SendNotificationActivity extends Activity {
+ private NotificationManager mNotificationManager;
+ private String mChannelId = "Channel id";
+ private String mChannelName = "Channel name";
+ private NotificationChannel mChannel;
+ private int mNotifyId = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_notification);
+ findViewById(R.id.button_send_notification).setOnClickListener(this::sendNotification);
+
+ mChannel = new NotificationChannel(mChannelId, mChannelName,
+ NotificationManager.IMPORTANCE_DEFAULT);
+ mNotificationManager = getSystemService(NotificationManager.class);
+ mNotificationManager.createNotificationChannel(mChannel);
+ }
+
+ private void sendNotification(View v) {
+ PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
+ new Intent(this, SendNotificationActivity.class),
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ Notification notification = new Notification.Builder(this, mChannelId)
+ .setContentTitle("Notification App")
+ .setContentText("Notification content")
+ .setWhen(System.currentTimeMillis())
+ .setSmallIcon(R.drawable.ic_message)
+ .setContentIntent(pendingIntent)
+ .build();
+
+ mNotificationManager.notify(mNotifyId, notification);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index ea10be564351..1a8b9540cbd0 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -28,6 +28,9 @@ android_test {
"**/*.java",
"**/*.kt",
],
+ resource_dirs: [
+ "res",
+ ],
static_libs: [
"WindowManager-Shell",
@@ -65,4 +68,9 @@ android_test {
optimize: {
enabled: false,
},
+
+ aaptflags: [
+ "--extra-packages",
+ "com.android.wm.shell.tests",
+ ],
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java
new file mode 100644
index 000000000000..49228720b81d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell;
+
+import static org.mockito.Mockito.RETURNS_SELF;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.view.SurfaceControl;
+
+/**
+ * Helper class to provide mocks for {@link SurfaceControl.Builder} and
+ * {@link SurfaceControl.Transaction} with method chaining support.
+ */
+public class MockSurfaceControlHelper {
+ private MockSurfaceControlHelper() {}
+
+ /**
+ * Creates a mock {@link SurfaceControl.Builder} that supports method chaining and return the
+ * given {@link SurfaceControl} when calling {@link SurfaceControl.Builder#build()}.
+ *
+ * @param mockSurfaceControl the first {@link SurfaceControl} to return
+ * @param mockSurfaceControls following {@link SurfaceControl} to return
+ * @return the mock of {@link SurfaceControl.Builder}
+ */
+ public static SurfaceControl.Builder createMockSurfaceControlBuilder(
+ SurfaceControl mockSurfaceControl, SurfaceControl... mockSurfaceControls) {
+ final SurfaceControl.Builder mockBuilder = mock(SurfaceControl.Builder.class, RETURNS_SELF);
+ doReturn(mockSurfaceControl, (Object[]) mockSurfaceControls)
+ .when(mockBuilder)
+ .build();
+ return mockBuilder;
+ }
+
+ /**
+ * Creates a mock {@link SurfaceControl.Transaction} that supports method chaining.
+ * @return the mock of {@link SurfaceControl.Transaction}
+ */
+ public static SurfaceControl.Transaction createMockSurfaceControlTransaction() {
+ return mock(SurfaceControl.Transaction.class, RETURNS_SELF);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitTest.java
new file mode 100644
index 000000000000..219e5ab6c651
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
+import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.draganddrop.DragAndDropController;
+import com.android.wm.shell.freeform.FreeformTaskListener;
+import com.android.wm.shell.fullscreen.FullscreenTaskListener;
+import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
+import com.android.wm.shell.pip.phone.PipTouchHandler;
+import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.startingsurface.StartingWindowController;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.unfold.UnfoldAnimationController;
+import com.android.wm.shell.unfold.UnfoldTransitionHandler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Optional;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class ShellInitTest extends ShellTestCase {
+
+ @Mock private ShellController mShellController;
+ @Mock private DisplayController mDisplayController;
+ @Mock private DisplayImeController mDisplayImeController;
+ @Mock private DisplayInsetsController mDisplayInsetsController;
+ @Mock private DragAndDropController mDragAndDropController;
+ @Mock private ShellTaskOrganizer mShellTaskOrganizer;
+ @Mock private KidsModeTaskOrganizer mKidsModeTaskOrganizer;
+ @Mock private Optional<BubbleController> mBubblesOptional;
+ @Mock private Optional<SplitScreenController> mSplitScreenOptional;
+ @Mock private Optional<PipTouchHandler> mPipTouchHandlerOptional;
+ @Mock private FullscreenTaskListener mFullscreenTaskListener;
+ @Mock private Optional<UnfoldAnimationController> mUnfoldAnimationController;
+ @Mock private Optional<UnfoldTransitionHandler> mUnfoldTransitionHandler;
+ @Mock private Optional<FreeformTaskListener<?>> mFreeformTaskListenerOptional;
+ @Mock private Optional<RecentTasksController> mRecentTasks;
+ @Mock private Optional<ActivityEmbeddingController> mActivityEmbeddingController;
+ @Mock private Transitions mTransitions;
+ @Mock private StartingWindowController mStartingWindow;
+ @Mock private ShellExecutor mMainExecutor;
+
+ private ShellInit mImpl;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mImpl = new ShellInit(mShellController, mDisplayController, mDisplayImeController,
+ mDisplayInsetsController, mDragAndDropController, mShellTaskOrganizer,
+ mKidsModeTaskOrganizer, mBubblesOptional, mSplitScreenOptional,
+ mPipTouchHandlerOptional, mFullscreenTaskListener, mUnfoldAnimationController,
+ mUnfoldTransitionHandler, mFreeformTaskListenerOptional, mRecentTasks,
+ mActivityEmbeddingController, mTransitions, mStartingWindow, mMainExecutor);
+ }
+
+ @Test
+ public void testAddInitCallbacks_expectCalledInOrder() {
+ ArrayList<Integer> results = new ArrayList<>();
+ mImpl.addInitCallback(() -> {
+ results.add(1);
+ }, new Object());
+ mImpl.addInitCallback(() -> {
+ results.add(2);
+ }, new Object());
+ mImpl.addInitCallback(() -> {
+ results.add(3);
+ }, new Object());
+ mImpl.init();
+ assertTrue(results.get(0) == 1);
+ assertTrue(results.get(1) == 2);
+ assertTrue(results.get(2) == 3);
+ }
+
+ @Test
+ public void testNoInitCallbacksAfterInit_expectException() {
+ mImpl.init();
+ try {
+ mImpl.addInitCallback(() -> {}, new Object());
+ fail("Expected exception when adding callback after init");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+ }
+
+ @Test
+ public void testDoubleInit_expectNoOp() {
+ ArrayList<Integer> results = new ArrayList<>();
+ mImpl.addInitCallback(() -> {
+ results.add(1);
+ }, new Object());
+ mImpl.init();
+ assertTrue(results.size() == 1);
+ mImpl.init();
+ assertTrue(results.size() == 1);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index a6caefe6d3e7..3dd00329253c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -26,11 +26,13 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
+import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
@@ -46,6 +48,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.SparseArray;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.window.ITaskOrganizer;
import android.window.ITaskOrganizerController;
import android.window.TaskAppearedInfo;
@@ -76,7 +79,7 @@ import java.util.Optional;
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ShellTaskOrganizerTests {
+public class ShellTaskOrganizerTests extends ShellTestCase {
@Mock
private ITaskOrganizerController mTaskOrganizerController;
@@ -133,17 +136,30 @@ public class ShellTaskOrganizerTests {
.when(mTaskOrganizerController).registerTaskOrganizer(any());
} catch (RemoteException e) {}
mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext,
- mCompatUI, Optional.empty()));
+ mCompatUI, Optional.empty(), Optional.empty()));
}
@Test
- public void registerOrganizer_sendRegisterTaskOrganizer() throws RemoteException {
+ public void testRegisterOrganizer_sendRegisterTaskOrganizer() throws RemoteException {
mOrganizer.registerOrganizer();
verify(mTaskOrganizerController).registerTaskOrganizer(any(ITaskOrganizer.class));
}
@Test
+ public void testTaskLeashReleasedAfterVanished() throws RemoteException {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
+ RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
+ SurfaceControl taskLeash = new SurfaceControl.Builder(new SurfaceSession())
+ .setName("task").build();
+ mOrganizer.registerOrganizer();
+ mOrganizer.onTaskAppeared(taskInfo, taskLeash);
+ assertTrue(taskLeash.isValid());
+ mOrganizer.onTaskVanished(taskInfo);
+ assertTrue(!taskLeash.isValid());
+ }
+
+ @Test
public void testOneListenerPerType() {
mOrganizer.addListenerForType(new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW);
try {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
index 403dbf9d9554..b5ee037892ba 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
@@ -24,6 +24,8 @@ import android.testing.TestableContext;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.protolog.common.ProtoLog;
+
import org.junit.After;
import org.junit.Before;
import org.mockito.MockitoAnnotations;
@@ -37,6 +39,9 @@ public abstract class ShellTestCase {
@Before
public void shellSetup() {
+ // Disable protolog tool when running the tests from studio
+ ProtoLog.REQUIRE_PROTOLOGTOOL = false;
+
MockitoAnnotations.initMocks(this);
final Context context =
InstrumentationRegistry.getInstrumentation().getTargetContext();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index 51eec27cfc0e..c0720cf04028 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -25,8 +25,10 @@ import static org.mockito.Mockito.mock;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
+import android.view.Display;
import android.window.IWindowContainerToken;
import android.window.WindowContainerToken;
@@ -38,6 +40,10 @@ public final class TestRunningTaskInfoBuilder {
private int mParentTaskId = INVALID_TASK_ID;
private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD;
private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+ private int mDisplayId = Display.DEFAULT_DISPLAY;
+ private ActivityManager.TaskDescription.Builder mTaskDescriptionBuilder = null;
+ private final Point mPositionInParent = new Point();
+ private boolean mIsVisible = false;
public static WindowContainerToken createMockWCToken() {
final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
@@ -68,17 +74,42 @@ public final class TestRunningTaskInfoBuilder {
return this;
}
+ public TestRunningTaskInfoBuilder setDisplayId(int displayId) {
+ mDisplayId = displayId;
+ return this;
+ }
+
+ public TestRunningTaskInfoBuilder setTaskDescriptionBuilder(
+ ActivityManager.TaskDescription.Builder builder) {
+ mTaskDescriptionBuilder = builder;
+ return this;
+ }
+
+ public TestRunningTaskInfoBuilder setPositionInParent(int x, int y) {
+ mPositionInParent.set(x, y);
+ return this;
+ }
+
+ public TestRunningTaskInfoBuilder setVisible(boolean isVisible) {
+ mIsVisible = isVisible;
+ return this;
+ }
+
public ActivityManager.RunningTaskInfo build() {
final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
- info.parentTaskId = INVALID_TASK_ID;
info.taskId = sNextTaskId++;
info.parentTaskId = mParentTaskId;
+ info.displayId = mDisplayId;
info.configuration.windowConfiguration.setBounds(mBounds);
info.configuration.windowConfiguration.setActivityType(mActivityType);
info.configuration.windowConfiguration.setWindowingMode(mWindowingMode);
info.token = mToken;
info.isResizeable = true;
info.supportsMultiWindow = true;
+ info.taskDescription =
+ mTaskDescriptionBuilder != null ? mTaskDescriptionBuilder.build() : null;
+ info.positionInParent = mPositionInParent;
+ info.isVisible = mIsVisible;
return info;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
deleted file mode 100644
index e73d9aaf190a..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.apppairs;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.hardware.display.DisplayManager;
-
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link AppPair}
- * Build/Install/Run:
- * atest WMShellUnitTests:AppPairTests
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class AppPairTests extends ShellTestCase {
-
- private AppPairsController mController;
- @Mock private SyncTransactionQueue mSyncQueue;
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Mock private DisplayController mDisplayController;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
- when(mDisplayController.getDisplay(anyInt())).thenReturn(
- mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
- mController = new TestAppPairsController(
- mTaskOrganizer,
- mSyncQueue,
- mDisplayController);
- spyOn(mController);
- }
-
- @After
- public void tearDown() {}
-
- @Test
- @UiThreadTest
- public void testContains() {
- final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
- final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
-
- final AppPair pair = mController.pairInner(task1, task2);
- assertThat(pair.contains(task1.taskId)).isTrue();
- assertThat(pair.contains(task2.taskId)).isTrue();
-
- pair.unpair();
- assertThat(pair.contains(task1.taskId)).isFalse();
- assertThat(pair.contains(task2.taskId)).isFalse();
- }
-
- @Test
- @UiThreadTest
- public void testVanishUnpairs() {
- final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
- final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
-
- final AppPair pair = mController.pairInner(task1, task2);
- assertThat(pair.contains(task1.taskId)).isTrue();
- assertThat(pair.contains(task2.taskId)).isTrue();
-
- pair.onTaskVanished(task1);
- assertThat(pair.contains(task1.taskId)).isFalse();
- assertThat(pair.contains(task2.taskId)).isFalse();
- }
-
- @Test
- @UiThreadTest
- public void testOnTaskInfoChanged_notSupportsMultiWindow() {
- final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
- final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
-
- final AppPair pair = mController.pairInner(task1, task2);
- assertThat(pair.contains(task1.taskId)).isTrue();
- assertThat(pair.contains(task2.taskId)).isTrue();
-
- task1.supportsMultiWindow = false;
- pair.onTaskInfoChanged(task1);
- verify(mController).unpair(pair.getRootTaskId());
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
deleted file mode 100644
index 505c153eff9c..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.apppairs;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.hardware.display.DisplayManager;
-
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/** Tests for {@link AppPairsController} */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class AppPairsControllerTests extends ShellTestCase {
- private TestAppPairsController mController;
- private TestAppPairsPool mPool;
- @Mock private SyncTransactionQueue mSyncQueue;
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Mock private DisplayController mDisplayController;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
- when(mDisplayController.getDisplay(anyInt())).thenReturn(
- mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
- mController = new TestAppPairsController(
- mTaskOrganizer,
- mSyncQueue,
- mDisplayController);
- mPool = mController.getPool();
- }
-
- @After
- public void tearDown() {}
-
- @Test
- @UiThreadTest
- public void testPairUnpair() {
- final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
- final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
-
- final AppPair pair = mController.pairInner(task1, task2);
- assertThat(pair.contains(task1.taskId)).isTrue();
- assertThat(pair.contains(task2.taskId)).isTrue();
- assertThat(mPool.poolSize()).isGreaterThan(0);
-
- mController.unpair(task2.taskId);
- assertThat(pair.contains(task1.taskId)).isFalse();
- assertThat(pair.contains(task2.taskId)).isFalse();
- assertThat(mPool.poolSize()).isGreaterThan(1);
- }
-
- @Test
- @UiThreadTest
- public void testUnpair_DontReleaseToPool() {
- final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
- final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
-
- final AppPair pair = mController.pairInner(task1, task2);
- assertThat(pair.contains(task1.taskId)).isTrue();
- assertThat(pair.contains(task2.taskId)).isTrue();
-
- mController.unpair(task2.taskId, false /* releaseToPool */);
- assertThat(pair.contains(task1.taskId)).isFalse();
- assertThat(pair.contains(task2.taskId)).isFalse();
- assertThat(mPool.poolSize()).isEqualTo(1);
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
deleted file mode 100644
index a3f134ee97ed..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.apppairs;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.when;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/** Tests for {@link AppPairsPool} */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class AppPairsPoolTests extends ShellTestCase {
- private TestAppPairsController mController;
- private TestAppPairsPool mPool;
- @Mock private SyncTransactionQueue mSyncQueue;
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Mock private DisplayController mDisplayController;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
- mController = new TestAppPairsController(
- mTaskOrganizer,
- mSyncQueue,
- mDisplayController);
- mPool = mController.getPool();
- }
-
- @After
- public void tearDown() {}
-
- @Test
- public void testInitialState() {
- // Pool should always start off with at least 1 entry.
- assertThat(mPool.poolSize()).isGreaterThan(0);
- }
-
- @Test
- public void testAcquireRelease() {
- assertThat(mPool.poolSize()).isGreaterThan(0);
- final AppPair appPair = mPool.acquire();
- assertThat(mPool.poolSize()).isGreaterThan(0);
- mPool.release(appPair);
- assertThat(mPool.poolSize()).isGreaterThan(1);
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
deleted file mode 100644
index 294bc1276291..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.apppairs;
-
-import static org.mockito.Mockito.mock;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.DisplayInsetsController;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-public class TestAppPairsController extends AppPairsController {
- private TestAppPairsPool mPool;
-
- public TestAppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
- DisplayController displayController) {
- super(organizer, syncQueue, displayController, mock(ShellExecutor.class),
- mock(DisplayImeController.class), mock(DisplayInsetsController.class));
- mPool = new TestAppPairsPool(this);
- setPairsPool(mPool);
- }
-
- TestAppPairsPool getPool() {
- return mPool;
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsPool.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsPool.java
deleted file mode 100644
index 1ee7fff44892..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsPool.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.apppairs;
-
-import android.app.ActivityManager;
-
-import com.android.wm.shell.TestRunningTaskInfoBuilder;
-
-public class TestAppPairsPool extends AppPairsPool{
- TestAppPairsPool(AppPairsController controller) {
- super(controller);
- }
-
- @Override
- void incrementPool() {
- final AppPair entry = new AppPair(mController);
- final ActivityManager.RunningTaskInfo info =
- new TestRunningTaskInfoBuilder().build();
- entry.onTaskAppeared(info, null /* leash */);
- release(entry);
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index e7c5cb2183db..31e55e7116f0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -57,6 +57,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import org.junit.Before;
@@ -74,7 +75,7 @@ import org.mockito.MockitoAnnotations;
@TestableLooper.RunWithLooper
@SmallTest
@RunWith(AndroidTestingRunner.class)
-public class BackAnimationControllerTest {
+public class BackAnimationControllerTest extends ShellTestCase {
private static final String ANIMATION_ENABLED = "1";
private final TestShellExecutor mShellExecutor = new TestShellExecutor();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
new file mode 100644
index 000000000000..44ff35466ae2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.bubbles;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.floatThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.os.SystemClock;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.bubbles.BubblesNavBarMotionEventHandler.MotionEventListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Test {@link MotionEvent} handling in {@link BubblesNavBarMotionEventHandler}.
+ * Verifies that swipe events
+ */
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidTestingRunner.class)
+public class BubblesNavBarMotionEventHandlerTest extends ShellTestCase {
+
+ private BubblesNavBarMotionEventHandler mMotionEventHandler;
+ @Mock
+ private WindowManager mWindowManager;
+ @Mock
+ private Runnable mInterceptTouchRunnable;
+ @Mock
+ private MotionEventListener mMotionEventListener;
+ private long mMotionEventTime;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ TestableBubblePositioner positioner = new TestableBubblePositioner(getContext(),
+ mWindowManager);
+ mMotionEventHandler = new BubblesNavBarMotionEventHandler(getContext(), positioner,
+ mInterceptTouchRunnable, mMotionEventListener);
+ mMotionEventTime = SystemClock.uptimeMillis();
+ }
+
+ @Test
+ public void testMotionEvent_swipeUpInGestureZone_handled() {
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_DOWN, 0, 990));
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 690));
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 490));
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 390));
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_UP, 0, 390));
+
+ verify(mMotionEventListener).onDown(0, 990);
+ verify(mMotionEventListener).onMove(0, -300);
+ verify(mMotionEventListener).onMove(0, -500);
+ verify(mMotionEventListener).onMove(0, -600);
+ // Check that velocity up is about 5000
+ verify(mMotionEventListener).onUp(eq(0f), floatThat(f -> Math.round(f) == -5000));
+ verifyZeroInteractions(mMotionEventListener);
+ verify(mInterceptTouchRunnable).run();
+ }
+
+ @Test
+ public void testMotionEvent_swipeUpOutsideGestureZone_ignored() {
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_DOWN, 0, 500));
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 100));
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_UP, 0, 100));
+
+ verifyZeroInteractions(mMotionEventListener);
+ verifyZeroInteractions(mInterceptTouchRunnable);
+ }
+
+ @Test
+ public void testMotionEvent_horizontalMoveMoreThanTouchSlop_handled() {
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_DOWN, 0, 990));
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 100, 990));
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_UP, 100, 990));
+
+ verify(mMotionEventListener).onDown(0, 990);
+ verify(mMotionEventListener).onMove(100, 0);
+ verify(mMotionEventListener).onUp(0, 0);
+ verifyZeroInteractions(mMotionEventListener);
+ verify(mInterceptTouchRunnable).run();
+ }
+
+ @Test
+ public void testMotionEvent_moveLessThanTouchSlop_ignored() {
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_DOWN, 0, 990));
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 989));
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_UP, 0, 989));
+
+ verify(mMotionEventListener).onDown(0, 990);
+ verifyNoMoreInteractions(mMotionEventListener);
+ verifyZeroInteractions(mInterceptTouchRunnable);
+ }
+
+ @Test
+ public void testMotionEvent_actionCancel_listenerNotified() {
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_DOWN, 0, 990));
+ mMotionEventHandler.onMotionEvent(newEvent(ACTION_CANCEL, 0, 990));
+ verify(mMotionEventListener).onDown(0, 990);
+ verify(mMotionEventListener).onCancel();
+ verifyNoMoreInteractions(mMotionEventListener);
+ verifyZeroInteractions(mInterceptTouchRunnable);
+ }
+
+ private MotionEvent newEvent(int actionDown, float x, float y) {
+ MotionEvent event = MotionEvent.obtain(0L, mMotionEventTime, actionDown, x, y, 0);
+ mMotionEventTime += 10;
+ return event;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTestActivity.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTestActivity.java
index d5fbe556045a..0537d0ea2404 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTestActivity.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTestActivity.java
@@ -20,7 +20,7 @@ import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-import com.android.wm.shell.R;
+import com.android.wm.shell.tests.R;
/**
* Referenced by NotificationTestHelper#makeBubbleMetadata
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java
new file mode 100644
index 000000000000..991913afbb90
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.bubbles.animation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.bubbles.BubbleExpandedView;
+import com.android.wm.shell.bubbles.TestableBubblePositioner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class ExpandedViewAnimationControllerTest extends ShellTestCase {
+
+ private ExpandedViewAnimationController mController;
+
+ @Mock
+ private WindowManager mWindowManager;
+
+ @Mock
+ private BubbleExpandedView mMockExpandedView;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ TestableBubblePositioner positioner = new TestableBubblePositioner(getContext(),
+ mWindowManager);
+ mController = new ExpandedViewAnimationControllerImpl(getContext(), positioner);
+
+ mController.setExpandedView(mMockExpandedView);
+ when(mMockExpandedView.getContentHeight()).thenReturn(1000);
+ }
+
+ @Test
+ public void testUpdateDrag_expandedViewMovesUpAndClipped() {
+ // Drag by 50 pixels which corresponds to 10 pixels with overscroll
+ int dragDistance = 50;
+ int dampenedDistance = 10;
+
+ mController.updateDrag(dragDistance);
+
+ verify(mMockExpandedView).setTopClip(dampenedDistance);
+ verify(mMockExpandedView).setContentTranslationY(-dampenedDistance);
+ verify(mMockExpandedView).setManageButtonTranslationY(-dampenedDistance);
+ }
+
+ @Test
+ public void testUpdateDrag_zOrderUpdates() {
+ mController.updateDrag(10);
+ mController.updateDrag(20);
+
+ verify(mMockExpandedView, times(1)).setSurfaceZOrderedOnTop(true);
+ verify(mMockExpandedView, times(1)).setAnimating(true);
+ }
+
+ @Test
+ public void testUpdateDrag_moveBackToZero_zOrderRestored() {
+ mController.updateDrag(50);
+ reset(mMockExpandedView);
+ mController.updateDrag(0);
+ mController.updateDrag(0);
+
+ verify(mMockExpandedView, times(1)).setSurfaceZOrderedOnTop(false);
+ verify(mMockExpandedView, times(1)).setAnimating(false);
+ }
+
+ @Test
+ public void testUpdateDrag_hapticFeedbackOnlyOnce() {
+ // Drag by 10 which is below the collapse threshold - no feedback
+ mController.updateDrag(10);
+ verify(mMockExpandedView, times(0)).performHapticFeedback(anyInt());
+ // 150 takes it over the threshold - perform feedback
+ mController.updateDrag(150);
+ verify(mMockExpandedView, times(1)).performHapticFeedback(anyInt());
+ // Continue dragging, no more feedback
+ mController.updateDrag(200);
+ verify(mMockExpandedView, times(1)).performHapticFeedback(anyInt());
+ // Drag below threshold and over again - no more feedback
+ mController.updateDrag(10);
+ mController.updateDrag(150);
+ verify(mMockExpandedView, times(1)).performHapticFeedback(anyInt());
+ }
+
+ @Test
+ public void testShouldCollapse_doNotCollapseIfNotDragged() {
+ assertThat(mController.shouldCollapse()).isFalse();
+ }
+
+ @Test
+ public void testShouldCollapse_doNotCollapseIfVelocityDown() {
+ assumeTrue("Min fling velocity should be > 1 for this test", getMinFlingVelocity() > 1);
+ mController.setSwipeVelocity(getVelocityAboveMinFling());
+ assertThat(mController.shouldCollapse()).isFalse();
+ }
+
+ @Test
+ public void tesShouldCollapse_doNotCollapseIfVelocityUpIsSmall() {
+ assumeTrue("Min fling velocity should be > 1 for this test", getMinFlingVelocity() > 1);
+ mController.setSwipeVelocity(-getVelocityBelowMinFling());
+ assertThat(mController.shouldCollapse()).isFalse();
+ }
+
+ @Test
+ public void testShouldCollapse_collapseIfVelocityUpIsLarge() {
+ assumeTrue("Min fling velocity should be > 1 for this test", getMinFlingVelocity() > 1);
+ mController.setSwipeVelocity(-getVelocityAboveMinFling());
+ assertThat(mController.shouldCollapse()).isTrue();
+ }
+
+ @Test
+ public void testShouldCollapse_collapseIfPastThreshold() {
+ mController.updateDrag(500);
+ assertThat(mController.shouldCollapse()).isTrue();
+ }
+
+ @Test
+ public void testReset() {
+ mController.updateDrag(100);
+ reset(mMockExpandedView);
+ mController.reset();
+ verify(mMockExpandedView, atLeastOnce()).setAnimating(false);
+ verify(mMockExpandedView).setContentAlpha(1);
+ verify(mMockExpandedView).setBackgroundAlpha(1);
+ verify(mMockExpandedView).setManageButtonAlpha(1);
+ verify(mMockExpandedView).setManageButtonAlpha(1);
+ verify(mMockExpandedView).setTopClip(0);
+ verify(mMockExpandedView).setContentTranslationY(-0f);
+ verify(mMockExpandedView).setManageButtonTranslationY(-0f);
+ verify(mMockExpandedView).setBottomClip(0);
+ verify(mMockExpandedView).movePointerBy(0, 0);
+ assertThat(mController.shouldCollapse()).isFalse();
+ }
+
+ private int getVelocityBelowMinFling() {
+ return getMinFlingVelocity() - 1;
+ }
+
+ private int getVelocityAboveMinFling() {
+ return getMinFlingVelocity() + 1;
+ }
+
+ private int getMinFlingVelocity() {
+ return ViewConfiguration.get(getContext()).getScaledMinimumFlingVelocity();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index b88845044263..587782cb79ad 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -39,6 +39,7 @@ import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
import com.android.internal.view.IInputMethodManager;
+import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
import org.junit.Test;
@@ -46,7 +47,7 @@ import org.junit.Test;
import java.util.concurrent.Executor;
@SmallTest
-public class DisplayImeControllerTest {
+public class DisplayImeControllerTest extends ShellTestCase {
private SurfaceControl.Transaction mT;
private DisplayImeController.PerDisplay mPerDisplay;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
index 3bf06cc0ede3..4a7fd3d259da 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
@@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.content.ComponentName;
import android.os.RemoteException;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
@@ -34,6 +35,7 @@ import android.view.InsetsVisibilities;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import org.junit.Before;
@@ -45,7 +47,7 @@ import org.mockito.MockitoAnnotations;
import java.util.List;
@SmallTest
-public class DisplayInsetsControllerTest {
+public class DisplayInsetsControllerTest extends ShellTestCase {
private static final int SECOND_DISPLAY = DEFAULT_DISPLAY + 10;
@@ -164,7 +166,7 @@ public class DisplayInsetsControllerTest {
int hideInsetsCount = 0;
@Override
- public void topFocusedWindowChanged(String packageName,
+ public void topFocusedWindowChanged(ComponentName component,
InsetsVisibilities requestedVisibilities) {
topFocusedWindowChangedCount++;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
index 0ffa5b35331d..514390fa52f9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
@@ -41,6 +41,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.policy.SystemBarUtils;
+import com.android.wm.shell.ShellTestCase;
import org.junit.After;
import org.junit.Before;
@@ -54,7 +55,7 @@ import org.mockito.MockitoSession;
* atest WMShellUnitTests:DisplayLayoutTest
*/
@SmallTest
-public class DisplayLayoutTest {
+public class DisplayLayoutTest extends ShellTestCase {
private MockitoSession mMockitoSession;
@Before
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
index 96938ebc27df..1347e061eb45 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java
@@ -35,6 +35,8 @@ import android.window.TaskSnapshot;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.ShellTestCase;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,7 +49,7 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
-public class TaskStackListenerImplTest {
+public class TaskStackListenerImplTest extends ShellTestCase {
@Mock
private IActivityTaskManager mActivityTaskManager;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index f1e602fcf778..95725bbfd855 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -133,7 +133,7 @@ public class SplitLayoutTests extends ShellTestCase {
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
waitDividerFlingFinished();
- verify(mSplitLayoutHandler).onSnappedToDismiss(eq(false));
+ verify(mSplitLayoutHandler).onSnappedToDismiss(eq(false), anyInt());
}
@Test
@@ -145,7 +145,7 @@ public class SplitLayoutTests extends ShellTestCase {
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
waitDividerFlingFinished();
- verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true));
+ verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true), anyInt());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 596100dcdead..828c13ecfda6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -53,6 +53,7 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -78,6 +79,7 @@ public class CompatUIControllerTest extends ShellTestCase {
private static final int TASK_ID = 12;
private CompatUIController mController;
+ private @Mock ShellController mMockShellController;
private @Mock DisplayController mMockDisplayController;
private @Mock DisplayInsetsController mMockDisplayInsetsController;
private @Mock DisplayLayout mMockDisplayLayout;
@@ -105,7 +107,7 @@ public class CompatUIControllerTest extends ShellTestCase {
doReturn(TASK_ID).when(mMockLetterboxEduLayout).getTaskId();
doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
- mController = new CompatUIController(mContext, mMockDisplayController,
+ mController = new CompatUIController(mContext, mMockShellController, mMockDisplayController,
mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor,
mMockTransitionsLazy) {
@Override
@@ -124,6 +126,11 @@ public class CompatUIControllerTest extends ShellTestCase {
}
@Test
+ public void instantiateController_registerKeyguardChangeListener() {
+ verify(mMockShellController, times(1)).addKeyguardChangeListener(any());
+ }
+
+ @Test
public void testListenerRegistered() {
verify(mMockDisplayController).addDisplayWindowListener(mController);
verify(mMockImeController).addPositionProcessor(mController);
@@ -324,7 +331,7 @@ public class CompatUIControllerTest extends ShellTestCase {
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
// Verify that the restart button is hidden after keyguard becomes showing.
- mController.onKeyguardShowingChanged(true);
+ mController.onKeyguardVisibilityChanged(true, false, false);
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
@@ -340,7 +347,7 @@ public class CompatUIControllerTest extends ShellTestCase {
false);
// Verify button is shown after keyguard becomes not showing.
- mController.onKeyguardShowingChanged(false);
+ mController.onKeyguardVisibilityChanged(false, false, false);
verify(mMockCompatLayout).updateVisibility(true);
verify(mMockLetterboxEduLayout).updateVisibility(true);
@@ -352,7 +359,7 @@ public class CompatUIControllerTest extends ShellTestCase {
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
- mController.onKeyguardShowingChanged(true);
+ mController.onKeyguardVisibilityChanged(true, false, false);
verify(mMockCompatLayout, times(2)).updateVisibility(false);
verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
@@ -360,7 +367,7 @@ public class CompatUIControllerTest extends ShellTestCase {
clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout);
// Verify button remains hidden after keyguard becomes not showing since IME is showing.
- mController.onKeyguardShowingChanged(false);
+ mController.onKeyguardVisibilityChanged(false, false, false);
verify(mMockCompatLayout).updateVisibility(false);
verify(mMockLetterboxEduLayout).updateVisibility(false);
@@ -378,7 +385,7 @@ public class CompatUIControllerTest extends ShellTestCase {
/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
- mController.onKeyguardShowingChanged(true);
+ mController.onKeyguardVisibilityChanged(true, false, false);
verify(mMockCompatLayout, times(2)).updateVisibility(false);
verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
@@ -392,7 +399,7 @@ public class CompatUIControllerTest extends ShellTestCase {
verify(mMockLetterboxEduLayout).updateVisibility(false);
// Verify button is shown after keyguard becomes not showing.
- mController.onKeyguardShowingChanged(false);
+ mController.onKeyguardVisibilityChanged(false, false, false);
verify(mMockCompatLayout).updateVisibility(true);
verify(mMockLetterboxEduLayout).updateVisibility(true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
index aaeebef03d0f..e209971998c8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
@@ -21,6 +21,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DragEvent.ACTION_DRAG_STARTED;
import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -44,9 +45,11 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.sysui.ShellController;
import org.junit.Before;
import org.junit.Test;
@@ -61,28 +64,38 @@ import java.util.Optional;
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class DragAndDropControllerTest {
+public class DragAndDropControllerTest extends ShellTestCase {
@Mock
private Context mContext;
-
+ @Mock
+ private ShellController mShellController;
@Mock
private DisplayController mDisplayController;
-
@Mock
private UiEventLogger mUiEventLogger;
-
@Mock
private DragAndDropController.DragAndDropListener mDragAndDropListener;
+ @Mock
+ private IconProvider mIconProvider;
+ @Mock
+ private ShellExecutor mMainExecutor;
+ @Mock
+ private SplitScreenController mSplitScreenController;
private DragAndDropController mController;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mController = new DragAndDropController(mContext, mDisplayController, mUiEventLogger,
- mock(IconProvider.class), mock(ShellExecutor.class));
- mController.initialize(Optional.of(mock(SplitScreenController.class)));
+ mController = new DragAndDropController(mContext, mShellController, mDisplayController,
+ mUiEventLogger, mIconProvider, mMainExecutor);
+ mController.initialize(Optional.of(mSplitScreenController));
+ }
+
+ @Test
+ public void instantiateController_registerConfigChangeListener() {
+ verify(mShellController, times(1)).addConfigurationChangeListener(any());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index bb6026c36c97..9e988e8e8726 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -34,7 +34,6 @@ import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPL
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
-import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -57,7 +56,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
@@ -68,6 +66,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceId;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -87,7 +86,7 @@ import java.util.HashSet;
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class DragAndDropPolicyTest {
+public class DragAndDropPolicyTest extends ShellTestCase {
@Mock
private Context mContext;
@@ -182,8 +181,10 @@ public class DragAndDropPolicyTest {
info.configuration.windowConfiguration.setActivityType(actType);
info.configuration.windowConfiguration.setWindowingMode(winMode);
info.isResizeable = true;
- info.baseActivity = new ComponentName(getInstrumentation().getContext().getPackageName(),
+ info.baseActivity = new ComponentName(getInstrumentation().getContext(),
".ActivityWithMode" + winMode);
+ info.baseIntent = new Intent();
+ info.baseIntent.setComponent(info.baseActivity);
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.packageName = info.baseActivity.getPackageName();
activityInfo.name = info.baseActivity.getClassName();
@@ -263,62 +264,6 @@ public class DragAndDropPolicyTest {
}
}
- @Test
- public void testLaunchMultipleTask_differentActivity() {
- setRunningTask(mFullscreenAppTask);
- mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
- Intent fillInIntent = mPolicy.getStartIntentFillInIntent(mock(PendingIntent.class), 0);
- assertNull(fillInIntent);
- }
-
- @Test
- public void testLaunchMultipleTask_differentActivity_inSplitscreen() {
- setRunningTask(mFullscreenAppTask);
- doReturn(true).when(mSplitScreenStarter).isSplitScreenVisible();
- doReturn(mFullscreenAppTask).when(mSplitScreenStarter).getTaskInfo(anyInt());
- mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
- Intent fillInIntent = mPolicy.getStartIntentFillInIntent(mock(PendingIntent.class), 0);
- assertNull(fillInIntent);
- }
-
- @Test
- public void testLaunchMultipleTask_sameActivity() {
- setRunningTask(mFullscreenAppTask);
-
- // Replace the mocked drag pending intent and ensure it resolves to the same activity
- PendingIntent launchIntent = mock(PendingIntent.class);
- ResolveInfo launchInfo = new ResolveInfo();
- launchInfo.activityInfo = mFullscreenAppTask.topActivityInfo;
- doReturn(Collections.singletonList(launchInfo))
- .when(launchIntent).queryIntentComponents(anyInt());
- mActivityClipData.getItemAt(0).getIntent().putExtra(ClipDescription.EXTRA_PENDING_INTENT,
- launchIntent);
-
- mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
- Intent fillInIntent = mPolicy.getStartIntentFillInIntent(launchIntent, 0);
- assertTrue((fillInIntent.getFlags() & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0);
- }
-
- @Test
- public void testLaunchMultipleTask_sameActivity_inSplitScreen() {
- setRunningTask(mFullscreenAppTask);
-
- // Replace the mocked drag pending intent and ensure it resolves to the same activity
- PendingIntent launchIntent = mock(PendingIntent.class);
- ResolveInfo launchInfo = new ResolveInfo();
- launchInfo.activityInfo = mFullscreenAppTask.topActivityInfo;
- doReturn(Collections.singletonList(launchInfo))
- .when(launchIntent).queryIntentComponents(anyInt());
- mActivityClipData.getItemAt(0).getIntent().putExtra(ClipDescription.EXTRA_PENDING_INTENT,
- launchIntent);
-
- doReturn(true).when(mSplitScreenStarter).isSplitScreenVisible();
- doReturn(mFullscreenAppTask).when(mSplitScreenStarter).getTaskInfo(anyInt());
- mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
- Intent fillInIntent = mPolicy.getStartIntentFillInIntent(launchIntent, 0);
- assertTrue((fillInIntent.getFlags() & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0);
- }
-
private Target filterTargetByType(ArrayList<Target> targets, int type) {
for (Target t : targets) {
if (type == t.type) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
deleted file mode 100644
index 4523e2c9cba5..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.fullscreen;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-
-import static org.junit.Assume.assumeFalse;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.WindowConfiguration;
-import android.content.res.Configuration;
-import android.graphics.Point;
-import android.os.SystemProperties;
-import android.view.SurfaceControl;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.recents.RecentTasksController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-@SmallTest
-public class FullscreenTaskListenerTest {
- private static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
-
- @Mock
- private SyncTransactionQueue mSyncQueue;
- @Mock
- private FullscreenUnfoldController mUnfoldController;
- @Mock
- private RecentTasksController mRecentTasksController;
- @Mock
- private SurfaceControl mSurfaceControl;
-
- private Optional<FullscreenUnfoldController> mFullscreenUnfoldController;
-
- private FullscreenTaskListener mListener;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mFullscreenUnfoldController = Optional.of(mUnfoldController);
- mListener = new FullscreenTaskListener(mSyncQueue, mFullscreenUnfoldController,
- Optional.empty());
- }
-
- @Test
- public void testAnimatableTaskAppeared_notifiesUnfoldController() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- RunningTaskInfo info = createTaskInfo(/* visible */ true, /* taskId */ 0);
-
- mListener.onTaskAppeared(info, mSurfaceControl);
-
- verify(mUnfoldController).onTaskAppeared(eq(info), any());
- }
-
- @Test
- public void testMultipleAnimatableTasksAppeared_notifiesUnfoldController() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- RunningTaskInfo animatable1 = createTaskInfo(/* visible */ true, /* taskId */ 0);
- RunningTaskInfo animatable2 = createTaskInfo(/* visible */ true, /* taskId */ 1);
-
- mListener.onTaskAppeared(animatable1, mSurfaceControl);
- mListener.onTaskAppeared(animatable2, mSurfaceControl);
-
- InOrder order = inOrder(mUnfoldController);
- order.verify(mUnfoldController).onTaskAppeared(eq(animatable1), any());
- order.verify(mUnfoldController).onTaskAppeared(eq(animatable2), any());
- }
-
- @Test
- public void testNonAnimatableTaskAppeared_doesNotNotifyUnfoldController() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
-
- mListener.onTaskAppeared(info, mSurfaceControl);
-
- verifyNoMoreInteractions(mUnfoldController);
- }
-
- @Test
- public void testNonAnimatableTaskChanged_doesNotNotifyUnfoldController() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
- mListener.onTaskAppeared(info, mSurfaceControl);
-
- mListener.onTaskInfoChanged(info);
-
- verifyNoMoreInteractions(mUnfoldController);
- }
-
- @Test
- public void testNonAnimatableTaskVanished_doesNotNotifyUnfoldController() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
- mListener.onTaskAppeared(info, mSurfaceControl);
-
- mListener.onTaskVanished(info);
-
- verifyNoMoreInteractions(mUnfoldController);
- }
-
- @Test
- public void testAnimatableTaskBecameInactive_notifiesUnfoldController() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- RunningTaskInfo animatableTask = createTaskInfo(/* visible */ true, /* taskId */ 0);
- mListener.onTaskAppeared(animatableTask, mSurfaceControl);
- RunningTaskInfo notAnimatableTask = createTaskInfo(/* visible */ false, /* taskId */ 0);
-
- mListener.onTaskInfoChanged(notAnimatableTask);
-
- verify(mUnfoldController).onTaskVanished(eq(notAnimatableTask));
- }
-
- @Test
- public void testAnimatableTaskVanished_notifiesUnfoldController() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- RunningTaskInfo taskInfo = createTaskInfo(/* visible */ true, /* taskId */ 0);
- mListener.onTaskAppeared(taskInfo, mSurfaceControl);
-
- mListener.onTaskVanished(taskInfo);
-
- verify(mUnfoldController).onTaskVanished(eq(taskInfo));
- }
-
- private RunningTaskInfo createTaskInfo(boolean visible, int taskId) {
- final RunningTaskInfo info = spy(new RunningTaskInfo());
- info.isVisible = visible;
- info.positionInParent = new Point();
- when(info.getWindowingMode()).thenReturn(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
- final Configuration configuration = new Configuration();
- configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
- when(info.getConfiguration()).thenReturn(configuration);
- info.taskId = taskId;
- return info;
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
index b976c1287aca..dcc504ad0cdb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java
@@ -16,10 +16,11 @@
package com.android.wm.shell.hidedisplaycutout;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.platform.test.annotations.Presubmit;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
@@ -27,7 +28,8 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.sysui.ShellController;
import org.junit.Before;
import org.junit.Test;
@@ -35,25 +37,30 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@Presubmit
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class HideDisplayCutoutControllerTest {
+public class HideDisplayCutoutControllerTest extends ShellTestCase {
private TestableContext mContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
- private HideDisplayCutoutController mHideDisplayCutoutController;
@Mock
- private HideDisplayCutoutOrganizer mMockDisplayAreaOrganizer;
+ private ShellController mShellController;
@Mock
- private ShellExecutor mMockMainExecutor;
+ private HideDisplayCutoutOrganizer mMockDisplayAreaOrganizer;
+
+ private HideDisplayCutoutController mHideDisplayCutoutController;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mHideDisplayCutoutController = new HideDisplayCutoutController(
- mContext, mMockDisplayAreaOrganizer, mMockMainExecutor);
+ mContext, mShellController, mMockDisplayAreaOrganizer);
+ }
+
+ @Test
+ public void instantiateController_registerConfigChangeListener() {
+ verify(mShellController, times(1)).addConfigurationChangeListener(any());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java
index 16e92395c85e..49521cfbb34d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java
@@ -32,7 +32,6 @@ import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Binder;
-import android.platform.test.annotations.Presubmit;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
@@ -48,6 +47,7 @@ import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -61,11 +61,10 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
-@Presubmit
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class HideDisplayCutoutOrganizerTest {
+public class HideDisplayCutoutOrganizerTest extends ShellTestCase {
private TestableContext mContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
index 440a6f8fb59a..184a8dfecff9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
@@ -44,6 +44,7 @@ import android.window.WindowContainerTransaction;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
@@ -61,7 +62,7 @@ import java.util.Optional;
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class KidsModeTaskOrganizerTest {
+public class KidsModeTaskOrganizerTest extends ShellTestCase {
@Mock private ITaskOrganizerController mTaskOrganizerController;
@Mock private Context mContext;
@Mock private Handler mHandler;
@@ -88,7 +89,7 @@ public class KidsModeTaskOrganizerTest {
// NOTE: KidsModeTaskOrganizer should have a null CompatUIController.
mOrganizer = spy(new KidsModeTaskOrganizer(mTaskOrganizerController, mTestExecutor,
mHandler, mContext, mSyncTransactionQueue, mDisplayController,
- mDisplayInsetsController, Optional.empty(), mObserver));
+ mDisplayInsetsController, Optional.empty(), Optional.empty(), mObserver));
mOrganizer.initialize(mStartingWindowController);
doReturn(mTransaction).when(mOrganizer).getWindowContainerTransaction();
doReturn(new InsetsState()).when(mDisplayController).getInsetsState(DEFAULT_DISPLAY);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index ecf1c5d41864..dbf93ae35c18 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -30,10 +30,10 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.om.IOverlayManager;
import android.graphics.Rect;
import android.os.Handler;
import android.os.UserHandle;
@@ -41,16 +41,15 @@ import android.testing.AndroidTestingRunner;
import android.util.ArrayMap;
import android.view.Display;
import android.view.Surface;
-import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
-import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.sysui.ShellController;
import org.junit.Before;
import org.junit.Test;
@@ -71,6 +70,8 @@ public class OneHandedControllerTest extends OneHandedTestCase {
OneHandedState mSpiedTransitionState;
@Mock
+ ShellController mMockShellController;
+ @Mock
DisplayLayout mDisplayLayout;
@Mock
DisplayController mMockDisplayController;
@@ -87,16 +88,10 @@ public class OneHandedControllerTest extends OneHandedTestCase {
@Mock
OneHandedUiEventLogger mMockUiEventLogger;
@Mock
- InteractionJankMonitor mMockJankMonitor;
- @Mock
- IOverlayManager mMockOverlayManager;
- @Mock
TaskStackListenerImpl mMockTaskStackListener;
@Mock
ShellExecutor mMockShellMainExecutor;
@Mock
- SurfaceControl mMockLeash;
- @Mock
Handler mMockShellMainHandler;
final boolean mDefaultEnabled = true;
@@ -132,6 +127,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mOneHandedAccessibilityUtil = new OneHandedAccessibilityUtil(mContext);
mSpiedOneHandedController = spy(new OneHandedController(
mContext,
+ mMockShellController,
mMockDisplayController,
mMockDisplayAreaOrganizer,
mMockTouchHandler,
@@ -140,9 +136,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mOneHandedAccessibilityUtil,
mSpiedTimeoutHandler,
mSpiedTransitionState,
- mMockJankMonitor,
mMockUiEventLogger,
- mMockOverlayManager,
mMockTaskStackListener,
mMockShellMainExecutor,
mMockShellMainHandler)
@@ -150,6 +144,16 @@ public class OneHandedControllerTest extends OneHandedTestCase {
}
@Test
+ public void testControllerRegistersConfigChangeListener() {
+ verify(mMockShellController, times(1)).addConfigurationChangeListener(any());
+ }
+
+ @Test
+ public void testControllerRegistersKeyguardChangeListener() {
+ verify(mMockShellController, times(1)).addKeyguardChangeListener(any());
+ }
+
+ @Test
public void testDefaultShouldNotInOneHanded() {
// Assert default transition state is STATE_NONE
assertThat(mSpiedTransitionState.getState()).isEqualTo(STATE_NONE);
@@ -345,8 +349,8 @@ public class OneHandedControllerTest extends OneHandedTestCase {
when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn(
false);
final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
- mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0,
- Surface.ROTATION_90, handlerWCT);
+ mSpiedOneHandedController.onDisplayChange(mDisplay.getDisplayId(), Surface.ROTATION_0,
+ Surface.ROTATION_90, null /* newDisplayAreaInfo */, handlerWCT);
verify(mMockDisplayAreaOrganizer, atLeastOnce()).onRotateDisplay(eq(mContext),
eq(Surface.ROTATION_90), any(WindowContainerTransaction.class));
@@ -358,8 +362,8 @@ public class OneHandedControllerTest extends OneHandedTestCase {
when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn(
false);
final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
- mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0,
- Surface.ROTATION_90, handlerWCT);
+ mSpiedOneHandedController.onDisplayChange(mDisplay.getDisplayId(), Surface.ROTATION_0,
+ Surface.ROTATION_90, null /* newDisplayAreaInfo */, handlerWCT);
verify(mMockDisplayAreaOrganizer, never()).onRotateDisplay(eq(mContext),
eq(Surface.ROTATION_90), any(WindowContainerTransaction.class));
@@ -371,8 +375,8 @@ public class OneHandedControllerTest extends OneHandedTestCase {
when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn(
true);
final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
- mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0,
- Surface.ROTATION_90, handlerWCT);
+ mSpiedOneHandedController.onDisplayChange(mDisplay.getDisplayId(), Surface.ROTATION_0,
+ Surface.ROTATION_90, null /* newDisplayAreaInfo */, handlerWCT);
verify(mMockDisplayAreaOrganizer, never()).onRotateDisplay(eq(mContext),
eq(Surface.ROTATION_90), any(WindowContainerTransaction.class));
@@ -384,8 +388,8 @@ public class OneHandedControllerTest extends OneHandedTestCase {
when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn(
false);
final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
- mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0,
- Surface.ROTATION_90, handlerWCT);
+ mSpiedOneHandedController.onDisplayChange(mDisplay.getDisplayId(), Surface.ROTATION_0,
+ Surface.ROTATION_90, null /* newDisplayAreaInfo */, handlerWCT);
verify(mMockDisplayAreaOrganizer, atLeastOnce()).onRotateDisplay(eq(mContext),
eq(Surface.ROTATION_90), any(WindowContainerTransaction.class));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
index dba1b8b86261..e6a8220e081b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
@@ -29,22 +29,19 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.om.IOverlayManager;
import android.graphics.Rect;
import android.os.Handler;
-import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.util.ArrayMap;
import android.view.Display;
-import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
-import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.sysui.ShellController;
import org.junit.Before;
import org.junit.Test;
@@ -55,7 +52,6 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class OneHandedStateTest extends OneHandedTestCase {
- private int mCurrentUser = UserHandle.myUserId();
Display mDisplay;
DisplayLayout mDisplayLayout;
@@ -65,6 +61,8 @@ public class OneHandedStateTest extends OneHandedTestCase {
OneHandedState mSpiedState;
@Mock
+ ShellController mMockShellController;
+ @Mock
DisplayController mMockDisplayController;
@Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@@ -77,16 +75,10 @@ public class OneHandedStateTest extends OneHandedTestCase {
@Mock
OneHandedUiEventLogger mMockUiEventLogger;
@Mock
- InteractionJankMonitor mMockJankMonitor;
- @Mock
- IOverlayManager mMockOverlayManager;
- @Mock
TaskStackListenerImpl mMockTaskStackListener;
@Mock
ShellExecutor mMockShellMainExecutor;
@Mock
- SurfaceControl mMockLeash;
- @Mock
Handler mMockShellMainHandler;
final boolean mDefaultEnabled = true;
@@ -119,6 +111,7 @@ public class OneHandedStateTest extends OneHandedTestCase {
mOneHandedAccessibilityUtil = new OneHandedAccessibilityUtil(mContext);
mSpiedOneHandedController = spy(new OneHandedController(
mContext,
+ mMockShellController,
mMockDisplayController,
mMockDisplayAreaOrganizer,
mMockTouchHandler,
@@ -127,9 +120,7 @@ public class OneHandedStateTest extends OneHandedTestCase {
mOneHandedAccessibilityUtil,
mSpiedTimeoutHandler,
mSpiedState,
- mMockJankMonitor,
mMockUiEventLogger,
- mMockOverlayManager,
mMockTaskStackListener,
mMockShellMainExecutor,
mMockShellMainHandler)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java
index 8b03dc58c3bf..808ab2167dc7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java
@@ -30,6 +30,8 @@ import android.view.WindowManager;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.wm.shell.ShellTestCase;
+
import org.junit.Before;
import org.junit.Rule;
import org.mockito.Answers;
@@ -38,7 +40,7 @@ import org.mockito.Mock;
/**
* Base class that does One Handed specific setup.
*/
-public abstract class OneHandedTestCase {
+public abstract class OneHandedTestCase extends ShellTestCase {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
protected Context mContext;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index c685fdc1f09c..52d78ca7a004 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -21,6 +21,7 @@ import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
@@ -37,6 +38,7 @@ import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.MockSurfaceControlHelper;
import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
@@ -103,7 +105,8 @@ public class PipAnimationControllerTest extends ShellTestCase {
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
- oldAnimator.setSurfaceControlTransactionFactory(PipDummySurfaceControlTx::new);
+ oldAnimator.setSurfaceControlTransactionFactory(
+ MockSurfaceControlHelper::createMockSurfaceControlTransaction);
oldAnimator.start();
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
@@ -133,7 +136,7 @@ public class PipAnimationControllerTest extends ShellTestCase {
@Test
public void pipTransitionAnimator_rotatedEndValue() {
- final PipDummySurfaceControlTx tx = new PipDummySurfaceControlTx();
+ final SurfaceControl.Transaction tx = createMockSurfaceControlTransaction();
final Rect startBounds = new Rect(200, 700, 400, 800);
final Rect endBounds = new Rect(0, 0, 500, 1000);
// Fullscreen to PiP.
@@ -183,7 +186,8 @@ public class PipAnimationControllerTest extends ShellTestCase {
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
- animator.setSurfaceControlTransactionFactory(PipDummySurfaceControlTx::new);
+ animator.setSurfaceControlTransactionFactory(
+ MockSurfaceControlHelper::createMockSurfaceControlTransaction);
animator.setPipAnimationCallback(mPipAnimationCallback);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipDummySurfaceControlTx.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipDummySurfaceControlTx.java
deleted file mode 100644
index ccf8f6e03844..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipDummySurfaceControlTx.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.pip;
-
-import android.graphics.Matrix;
-import android.view.SurfaceControl;
-
-/**
- * A dummy {@link SurfaceControl.Transaction} class for testing purpose and supports
- * method chaining.
- */
-public class PipDummySurfaceControlTx extends SurfaceControl.Transaction {
- @Override
- public SurfaceControl.Transaction setAlpha(SurfaceControl leash, float alpha) {
- return this;
- }
-
- @Override
- public SurfaceControl.Transaction setPosition(SurfaceControl leash, float x, float y) {
- return this;
- }
-
- @Override
- public SurfaceControl.Transaction setWindowCrop(SurfaceControl leash, int w, int h) {
- return this;
- }
-
- @Override
- public SurfaceControl.Transaction setCornerRadius(SurfaceControl leash, float radius) {
- return this;
- }
-
- @Override
- public SurfaceControl.Transaction setShadowRadius(SurfaceControl leash, float radius) {
- return this;
- }
-
- @Override
- public SurfaceControl.Transaction setMatrix(SurfaceControl leash, Matrix matrix,
- float[] float9) {
- return this;
- }
-
- @Override
- public SurfaceControl.Transaction setFrameTimelineVsync(long frameTimelineVsyncId) {
- return this;
- }
-
- @Override
- public void apply() {}
-}
-
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index e8e6254697c2..b351f8fcf838 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -44,6 +44,7 @@ import android.util.Size;
import android.view.DisplayInfo;
import android.window.WindowContainerToken;
+import com.android.wm.shell.MockSurfaceControlHelper;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
@@ -246,7 +247,8 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mPipBoundsState.setDisplayLayout(new DisplayLayout(info,
mContext.getResources(), true, true));
mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
- mSpiedPipTaskOrganizer.setSurfaceControlTransactionFactory(PipDummySurfaceControlTx::new);
+ mSpiedPipTaskOrganizer.setSurfaceControlTransactionFactory(
+ MockSurfaceControlHelper::createMockSurfaceControlTransaction);
doNothing().when(mSpiedPipTaskOrganizer).enterPipWithAlphaAnimation(any(), anyLong());
doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any());
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index df18133adcfb..f192514c37ab 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -53,6 +54,8 @@ import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipTransitionState;
+import com.android.wm.shell.sysui.ShellController;
import org.junit.Before;
import org.junit.Test;
@@ -72,13 +75,16 @@ import java.util.Set;
public class PipControllerTest extends ShellTestCase {
private PipController mPipController;
+ @Mock private ShellController mMockShellController;
@Mock private DisplayController mMockDisplayController;
@Mock private PhonePipMenuController mMockPhonePipMenuController;
@Mock private PipAppOpsListener mMockPipAppOpsListener;
@Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
+ @Mock private PipKeepClearAlgorithm mMockPipKeepClearAlgorithm;
@Mock private PipSnapAlgorithm mMockPipSnapAlgorithm;
@Mock private PipMediaController mMockPipMediaController;
@Mock private PipTaskOrganizer mMockPipTaskOrganizer;
+ @Mock private PipTransitionState mMockPipTransitionState;
@Mock private PipTransitionController mMockPipTransitionController;
@Mock private PipTouchHandler mMockPipTouchHandler;
@Mock private PipMotionHelper mMockPipMotionHelper;
@@ -99,11 +105,12 @@ public class PipControllerTest extends ShellTestCase {
((Runnable) invocation.getArgument(0)).run();
return null;
}).when(mMockExecutor).execute(any());
- mPipController = new PipController(mContext, mMockDisplayController,
+ mPipController = new PipController(mContext, mMockShellController, mMockDisplayController,
mMockPipAppOpsListener, mMockPipBoundsAlgorithm,
- mMockPipBoundsState, mMockPipMediaController,
- mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler,
- mMockPipTransitionController, mMockWindowManagerShellWrapper,
+ mMockPipKeepClearAlgorithm,
+ mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
+ mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
+ mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
mMockTaskStackListener, mPipParamsChangedForwarder,
mMockOneHandedController, mMockExecutor);
when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
@@ -111,6 +118,16 @@ public class PipControllerTest extends ShellTestCase {
}
@Test
+ public void instantiatePipController_registerConfigChangeListener() {
+ verify(mMockShellController, times(1)).addConfigurationChangeListener(any());
+ }
+
+ @Test
+ public void instantiatePipController_registerKeyguardChangeListener() {
+ verify(mMockShellController, times(1)).addKeyguardChangeListener(any());
+ }
+
+ @Test
public void instantiatePipController_registersPipTransitionCallback() {
verify(mMockPipTransitionController).registerPipTransitionCallback(any());
}
@@ -132,11 +149,12 @@ public class PipControllerTest extends ShellTestCase {
when(mockPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
when(spyContext.getPackageManager()).thenReturn(mockPackageManager);
- assertNull(PipController.create(spyContext, mMockDisplayController,
+ assertNull(PipController.create(spyContext, mMockShellController, mMockDisplayController,
mMockPipAppOpsListener, mMockPipBoundsAlgorithm,
- mMockPipBoundsState, mMockPipMediaController,
- mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler,
- mMockPipTransitionController, mMockWindowManagerShellWrapper,
+ mMockPipKeepClearAlgorithm,
+ mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
+ mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
+ mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
mMockTaskStackListener, mPipParamsChangedForwarder,
mMockOneHandedController, mMockExecutor));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java
new file mode 100644
index 000000000000..e0f7e35f8d02
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.pip.phone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+
+/**
+ * Unit tests against {@link PipKeepClearAlgorithm}.
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipKeepClearAlgorithmTest extends ShellTestCase {
+
+ private PipKeepClearAlgorithm mPipKeepClearAlgorithm;
+ private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 1000, 1000);
+
+ @Before
+ public void setUp() throws Exception {
+ mPipKeepClearAlgorithm = new PipKeepClearAlgorithm();
+ }
+
+ @Test
+ public void adjust_withCollidingRestrictedKeepClearAreas_movesBounds() {
+ final Rect inBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(50, 50, 150, 150);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect),
+ Set.of(), DISPLAY_BOUNDS);
+
+ assertFalse(outBounds.contains(keepClearRect));
+ }
+
+ @Test
+ public void adjust_withNonCollidingRestrictedKeepClearAreas_boundsDoNotChange() {
+ final Rect inBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(100, 100, 150, 150);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect),
+ Set.of(), DISPLAY_BOUNDS);
+
+ assertEquals(inBounds, outBounds);
+ }
+
+ @Test
+ public void adjust_withCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() {
+ // TODO(b/183746978): update this test to accommodate for the updated algorithm
+ final Rect inBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(50, 50, 150, 150);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(),
+ Set.of(keepClearRect), DISPLAY_BOUNDS);
+
+ assertEquals(inBounds, outBounds);
+ }
+
+ @Test
+ public void adjust_withNonCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() {
+ final Rect inBounds = new Rect(0, 0, 100, 100);
+ final Rect keepClearRect = new Rect(100, 100, 150, 150);
+
+ final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(),
+ Set.of(keepClearRect), DISPLAY_BOUNDS);
+
+ assertEquals(inBounds, outBounds);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 50f6bd7b4927..2b4d1a6390c9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -47,7 +47,7 @@ import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
-import com.android.wm.shell.util.StagedSplitBounds;
+import com.android.wm.shell.util.SplitBounds;
import org.junit.Before;
import org.junit.Test;
@@ -80,7 +80,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
mRecentTasksController = spy(new RecentTasksController(mContext, mTaskStackListener,
mMainExecutor));
mShellTaskOrganizer = new ShellTaskOrganizer(mMainExecutor, mContext,
- null /* sizeCompatUI */, Optional.of(mRecentTasksController));
+ null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController));
}
@Test
@@ -89,7 +89,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
setRawList(t1, t2);
- mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, mock(StagedSplitBounds.class));
+ mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, mock(SplitBounds.class));
verify(mRecentTasksController).notifyRecentTasksChanged();
reset(mRecentTasksController);
@@ -104,10 +104,10 @@ public class RecentTasksControllerTest extends ShellTestCase {
setRawList(t1, t2);
// Verify only one update if the split info is the same
- StagedSplitBounds bounds1 = new StagedSplitBounds(new Rect(0, 0, 50, 50),
+ SplitBounds bounds1 = new SplitBounds(new Rect(0, 0, 50, 50),
new Rect(50, 50, 100, 100), t1.taskId, t2.taskId);
mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds1);
- StagedSplitBounds bounds2 = new StagedSplitBounds(new Rect(0, 0, 50, 50),
+ SplitBounds bounds2 = new SplitBounds(new Rect(0, 0, 50, 50),
new Rect(50, 50, 100, 100), t1.taskId, t2.taskId);
mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, bounds2);
verify(mRecentTasksController, times(1)).notifyRecentTasksChanged();
@@ -139,8 +139,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
setRawList(t1, t2, t3, t4, t5, t6);
// Mark a couple pairs [t2, t4], [t3, t5]
- StagedSplitBounds pair1Bounds = new StagedSplitBounds(new Rect(), new Rect(), 2, 4);
- StagedSplitBounds pair2Bounds = new StagedSplitBounds(new Rect(), new Rect(), 3, 5);
+ SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4);
+ SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5);
mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
@@ -162,7 +162,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
setRawList(t1, t2, t3);
// Add a pair
- StagedSplitBounds pair1Bounds = new StagedSplitBounds(new Rect(), new Rect(), 2, 3);
+ SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 3);
mRecentTasksController.addSplitPair(t2.taskId, t3.taskId, pair1Bounds);
reset(mRecentTasksController);
@@ -245,15 +245,15 @@ public class RecentTasksControllerTest extends ShellTestCase {
: -1;
if (pair.mTaskInfo2 != null) {
- assertNotNull(pair.mStagedSplitBounds);
- int leftTopTaskId = pair.mStagedSplitBounds.leftTopTaskId;
- int bottomRightTaskId = pair.mStagedSplitBounds.rightBottomTaskId;
+ assertNotNull(pair.mSplitBounds);
+ int leftTopTaskId = pair.mSplitBounds.leftTopTaskId;
+ int bottomRightTaskId = pair.mSplitBounds.rightBottomTaskId;
// Unclear if pairs are ordered by split position, most likely not.
assertTrue(leftTopTaskId == taskId1 || leftTopTaskId == pair.mTaskInfo2.taskId);
assertTrue(bottomRightTaskId == taskId1
|| bottomRightTaskId == pair.mTaskInfo2.taskId);
} else {
- assertNull(pair.mStagedSplitBounds);
+ assertNull(pair.mSplitBounds);
}
}
assertTrue("Expected: " + Arrays.toString(expectedTaskIds)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/StagedSplitBoundsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java
index ad73c56950bd..50d02ae0dccd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/StagedSplitBoundsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java
@@ -9,7 +9,8 @@ import android.graphics.Rect;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.wm.shell.util.StagedSplitBounds;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.util.SplitBounds;
import org.junit.Before;
import org.junit.Test;
@@ -17,7 +18,7 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class StagedSplitBoundsTest {
+public class SplitBoundsTest extends ShellTestCase {
private static final int DEVICE_WIDTH = 100;
private static final int DEVICE_LENGTH = 200;
private static final int DIVIDER_SIZE = 20;
@@ -42,21 +43,21 @@ public class StagedSplitBoundsTest {
@Test
public void testVerticalStacked() {
- StagedSplitBounds ssb = new StagedSplitBounds(mTopRect, mBottomRect,
+ SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect,
TASK_ID_1, TASK_ID_2);
assertTrue(ssb.appsStackedVertically);
}
@Test
public void testHorizontalStacked() {
- StagedSplitBounds ssb = new StagedSplitBounds(mLeftRect, mRightRect,
+ SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect,
TASK_ID_1, TASK_ID_2);
assertFalse(ssb.appsStackedVertically);
}
@Test
public void testHorizontalDividerBounds() {
- StagedSplitBounds ssb = new StagedSplitBounds(mTopRect, mBottomRect,
+ SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect,
TASK_ID_1, TASK_ID_2);
Rect dividerBounds = ssb.visualDividerBounds;
assertEquals(0, dividerBounds.left);
@@ -67,7 +68,7 @@ public class StagedSplitBoundsTest {
@Test
public void testVerticalDividerBounds() {
- StagedSplitBounds ssb = new StagedSplitBounds(mLeftRect, mRightRect,
+ SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect,
TASK_ID_1, TASK_ID_2);
Rect dividerBounds = ssb.visualDividerBounds;
assertEquals(DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2, dividerBounds.left);
@@ -78,7 +79,7 @@ public class StagedSplitBoundsTest {
@Test
public void testEqualVerticalTaskPercent() {
- StagedSplitBounds ssb = new StagedSplitBounds(mTopRect, mBottomRect,
+ SplitBounds ssb = new SplitBounds(mTopRect, mBottomRect,
TASK_ID_1, TASK_ID_2);
float topPercentSpaceTaken = (float) (DEVICE_LENGTH / 2 - DIVIDER_SIZE / 2) / DEVICE_LENGTH;
assertEquals(topPercentSpaceTaken, ssb.topTaskPercent, 0.01);
@@ -86,7 +87,7 @@ public class StagedSplitBoundsTest {
@Test
public void testEqualHorizontalTaskPercent() {
- StagedSplitBounds ssb = new StagedSplitBounds(mLeftRect, mRightRect,
+ SplitBounds ssb = new SplitBounds(mLeftRect, mRightRect,
TASK_ID_1, TASK_ID_2);
float leftPercentSpaceTaken = (float) (DEVICE_WIDTH / 2 - DIVIDER_SIZE / 2) / DEVICE_WIDTH;
assertEquals(leftPercentSpaceTaken, ssb.leftTaskPercent, 0.01);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
index 0639ad5d0a62..68cb57c14d8c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
@@ -61,7 +61,7 @@ public class MainStageTests extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession, mIconProvider, null);
+ mSyncQueue, mSurfaceSession, mIconProvider);
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
index a31aa58bdc26..3b42a48b5a40 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
@@ -66,7 +66,7 @@ public class SideStageTests extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mRootTask = new TestRunningTaskInfoBuilder().build();
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession, mIconProvider, null);
+ mSyncQueue, mSurfaceSession, mIconProvider);
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
new file mode 100644
index 000000000000..c7a261f32e43
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.splitscreen;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.transition.Transitions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+/**
+ * Tests for {@link SplitScreenController}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SplitScreenControllerTests extends ShellTestCase {
+
+ @Mock ShellController mShellController;
+ @Mock ShellTaskOrganizer mTaskOrganizer;
+ @Mock SyncTransactionQueue mSyncQueue;
+ @Mock RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
+ @Mock ShellExecutor mMainExecutor;
+ @Mock DisplayController mDisplayController;
+ @Mock DisplayImeController mDisplayImeController;
+ @Mock DisplayInsetsController mDisplayInsetsController;
+ @Mock Transitions mTransitions;
+ @Mock TransactionPool mTransactionPool;
+ @Mock IconProvider mIconProvider;
+ @Mock Optional<RecentTasksController> mRecentTasks;
+
+ private SplitScreenController mSplitScreenController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mSplitScreenController = spy(new SplitScreenController(mShellController, mTaskOrganizer,
+ mSyncQueue, mContext, mRootTDAOrganizer, mMainExecutor, mDisplayController,
+ mDisplayImeController, mDisplayInsetsController, mTransitions, mTransactionPool,
+ mIconProvider, mRecentTasks));
+ }
+
+ @Test
+ public void testControllerRegistersKeyguardChangeListener() {
+ when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
+ mSplitScreenController.onOrganizerRegistered();
+ verify(mShellController, times(1)).addKeyguardChangeListener(any());
+ }
+
+ @Test
+ public void testIsLaunchingAdjacently_notInSplitScreen() {
+ doReturn(false).when(mSplitScreenController).isSplitScreenVisible();
+ doReturn(true).when(mSplitScreenController).isValidToEnterSplitScreen(any());
+
+ // Verify launching the same activity returns true.
+ Intent startIntent = createStartIntent("startActivity");
+ ActivityManager.RunningTaskInfo focusTaskInfo =
+ createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
+ doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
+ assertTrue(mSplitScreenController.isLaunchingAdjacently(
+ startIntent, SPLIT_POSITION_TOP_OR_LEFT));
+
+ // Verify launching different activity returns false.
+ Intent diffIntent = createStartIntent("diffActivity");
+ focusTaskInfo =
+ createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, diffIntent);
+ doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
+ assertFalse(mSplitScreenController.isLaunchingAdjacently(
+ startIntent, SPLIT_POSITION_TOP_OR_LEFT));
+ }
+
+ @Test
+ public void testIsLaunchingAdjacently_inSplitScreen() {
+ doReturn(true).when(mSplitScreenController).isSplitScreenVisible();
+
+ // Verify launching the same activity returns true.
+ Intent startIntent = createStartIntent("startActivity");
+ ActivityManager.RunningTaskInfo pairingTaskInfo =
+ createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent);
+ doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt());
+ assertTrue(mSplitScreenController.isLaunchingAdjacently(
+ startIntent, SPLIT_POSITION_TOP_OR_LEFT));
+
+ // Verify launching different activity returns false.
+ Intent diffIntent = createStartIntent("diffActivity");
+ pairingTaskInfo =
+ createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent);
+ doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt());
+ assertFalse(mSplitScreenController.isLaunchingAdjacently(
+ startIntent, SPLIT_POSITION_TOP_OR_LEFT));
+ }
+
+ private Intent createStartIntent(String activityName) {
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(mContext, activityName));
+ return intent;
+ }
+
+ private ActivityManager.RunningTaskInfo createTaskInfo(int winMode, int actType,
+ Intent strIntent) {
+ ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
+ info.configuration.windowConfiguration.setActivityType(actType);
+ info.configuration.windowConfiguration.setWindowingMode(winMode);
+ info.supportsMultiWindow = true;
+ info.baseIntent = strIntent;
+ info.baseActivity = strIntent.getComponent();
+ ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.packageName = info.baseActivity.getPackageName();
+ activityInfo.name = info.baseActivity.getClassName();
+ info.topActivityInfo = activityInfo;
+ return info;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index eb9d3a11d285..a67853cfe745 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -40,8 +40,6 @@ import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
-import javax.inject.Provider;
-
public class SplitTestUtils {
static SplitLayout createMockSplitLayout() {
@@ -74,12 +72,10 @@ public class SplitTestUtils {
DisplayInsetsController insetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
SplitscreenEventLogger logger, ShellExecutor mainExecutor,
- Optional<RecentTasksController> recentTasks,
- Provider<Optional<StageTaskUnfoldController>> unfoldController) {
+ Optional<RecentTasksController> recentTasks) {
super(context, displayId, syncQueue, taskOrganizer, mainStage,
sideStage, displayController, imeController, insetsController, splitLayout,
- transitions, transactionPool, logger, mainExecutor, recentTasks,
- unfoldController);
+ transitions, transactionPool, logger, mainExecutor, recentTasks);
// Prepare root task for testing.
mRootTask = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index ffaab652aa99..304ca66dd3bb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -118,16 +118,16 @@ public class SplitTransitionTests extends ShellTestCase {
mSplitLayout = SplitTestUtils.createMockSplitLayout();
mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
- mIconProvider, null);
+ mIconProvider);
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
- mIconProvider, null);
+ mIconProvider);
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
- mTransactionPool, mLogger, mMainExecutor, Optional.empty(), Optional::empty);
+ mTransactionPool, mLogger, mMainExecutor, Optional.empty());
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
.when(mTransitions).startTransition(anyInt(), any(), any());
@@ -340,7 +340,7 @@ public class SplitTransitionTests extends ShellTestCase {
TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
info.addChange(mainChange);
info.addChange(sideChange);
- IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
+ IBinder transition = mSplitScreenTransitions.startDismissTransition(
new WindowContainerTransaction(), mStageCoordinator,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, STAGE_TYPE_SIDE);
boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -363,7 +363,7 @@ public class SplitTransitionTests extends ShellTestCase {
TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
info.addChange(mainChange);
info.addChange(sideChange);
- IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
+ IBinder transition = mSplitScreenTransitions.startDismissTransition(
new WindowContainerTransaction(), mStageCoordinator, EXIT_REASON_DRAG_DIVIDER,
STAGE_TYPE_SIDE);
mMainStage.onTaskVanished(mMainChild);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 42d998f6b0ee..4b68870d4129 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -34,6 +34,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -58,6 +59,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -68,8 +70,6 @@ import org.mockito.MockitoAnnotations;
import java.util.Optional;
-import javax.inject.Provider;
-
/**
* Tests for {@link StageCoordinator}
*/
@@ -85,10 +85,6 @@ public class StageCoordinatorTests extends ShellTestCase {
@Mock
private SideStage mSideStage;
@Mock
- private StageTaskUnfoldController mMainUnfoldController;
- @Mock
- private StageTaskUnfoldController mSideUnfoldController;
- @Mock
private SplitLayout mSplitLayout;
@Mock
private DisplayController mDisplayController;
@@ -107,6 +103,7 @@ public class StageCoordinatorTests extends ShellTestCase {
private final Rect mBounds1 = new Rect(10, 20, 30, 40);
private final Rect mBounds2 = new Rect(5, 10, 15, 20);
+ private final Rect mRootBounds = new Rect(0, 0, 45, 60);
private SurfaceSession mSurfaceSession = new SurfaceSession();
private SurfaceControl mRootLeash;
@@ -119,16 +116,20 @@ public class StageCoordinatorTests extends ShellTestCase {
mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mLogger,
- mMainExecutor, Optional.empty(), new UnfoldControllerProvider()));
+ mMainExecutor, Optional.empty()));
doNothing().when(mStageCoordinator).updateActivityOptions(any(), anyInt());
when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
when(mSplitLayout.getBounds2()).thenReturn(mBounds2);
+ when(mSplitLayout.getRootBounds()).thenReturn(mRootBounds);
when(mSplitLayout.isLandscape()).thenReturn(false);
mRootTask = new TestRunningTaskInfoBuilder().build();
mRootLeash = new SurfaceControl.Builder(mSurfaceSession).setName("test").build();
mStageCoordinator.onTaskAppeared(mRootTask, mRootLeash);
+
+ mSideStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
+ mMainStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
}
@Test
@@ -168,13 +169,6 @@ public class StageCoordinatorTests extends ShellTestCase {
}
@Test
- public void testRootTaskAppeared_initializesUnfoldControllers() {
- verify(mMainUnfoldController).init();
- verify(mSideUnfoldController).init();
- verify(mStageCoordinator).onRootTaskAppeared();
- }
-
- @Test
public void testRootTaskInfoChanged_updatesSplitLayout() {
mStageCoordinator.onTaskInfoChanged(mRootTask);
@@ -184,26 +178,25 @@ public class StageCoordinatorTests extends ShellTestCase {
@Test
public void testLayoutChanged_topLeftSplitPosition_updatesUnfoldStageBounds() {
mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
- clearInvocations(mMainUnfoldController, mSideUnfoldController);
+ final SplitScreenListener listener = mock(SplitScreenListener.class);
+ mStageCoordinator.registerSplitScreenListener(listener);
+ clearInvocations(listener);
mStageCoordinator.onLayoutSizeChanged(mSplitLayout);
- verify(mMainUnfoldController).onLayoutChanged(mBounds2, SPLIT_POSITION_BOTTOM_OR_RIGHT,
- false);
- verify(mSideUnfoldController).onLayoutChanged(mBounds1, SPLIT_POSITION_TOP_OR_LEFT, false);
+ verify(listener).onSplitBoundsChanged(mRootBounds, mBounds2, mBounds1);
}
@Test
public void testLayoutChanged_bottomRightSplitPosition_updatesUnfoldStageBounds() {
mStageCoordinator.setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, null);
- clearInvocations(mMainUnfoldController, mSideUnfoldController);
+ final SplitScreenListener listener = mock(SplitScreenListener.class);
+ mStageCoordinator.registerSplitScreenListener(listener);
+ clearInvocations(listener);
mStageCoordinator.onLayoutSizeChanged(mSplitLayout);
- verify(mMainUnfoldController).onLayoutChanged(mBounds1, SPLIT_POSITION_TOP_OR_LEFT,
- false);
- verify(mSideUnfoldController).onLayoutChanged(mBounds2, SPLIT_POSITION_BOTTOM_OR_RIGHT,
- false);
+ verify(listener).onSplitBoundsChanged(mRootBounds, mBounds1, mBounds2);
}
@Test
@@ -234,8 +227,8 @@ public class StageCoordinatorTests extends ShellTestCase {
mStageCoordinator.exitSplitScreen(testTaskId, EXIT_REASON_RETURN_HOME);
verify(mMainStage).reorderChild(eq(testTaskId), eq(true),
any(WindowContainerTransaction.class));
- verify(mSideStage).removeAllTasks(any(WindowContainerTransaction.class), eq(false));
- verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(true));
+ verify(mSideStage).dismiss(any(WindowContainerTransaction.class), eq(false));
+ verify(mMainStage).resetBounds(any(WindowContainerTransaction.class));
}
@Test
@@ -247,8 +240,8 @@ public class StageCoordinatorTests extends ShellTestCase {
mStageCoordinator.exitSplitScreen(testTaskId, EXIT_REASON_RETURN_HOME);
verify(mSideStage).reorderChild(eq(testTaskId), eq(true),
any(WindowContainerTransaction.class));
- verify(mSideStage).removeAllTasks(any(WindowContainerTransaction.class), eq(true));
- verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false));
+ verify(mSideStage).resetBounds(any(WindowContainerTransaction.class));
+ verify(mMainStage).dismiss(any(WindowContainerTransaction.class), eq(false));
}
@Test
@@ -314,20 +307,4 @@ public class StageCoordinatorTests extends ShellTestCase {
verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any(), eq(false));
}
-
- private class UnfoldControllerProvider implements
- Provider<Optional<StageTaskUnfoldController>> {
-
- private boolean isMain = true;
-
- @Override
- public Optional<StageTaskUnfoldController> get() {
- if (isMain) {
- isMain = false;
- return Optional.of(mMainUnfoldController);
- } else {
- return Optional.of(mSideUnfoldController);
- }
- }
- }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 157c30bcb6c7..5ee8bf3006a3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -25,7 +25,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -72,8 +71,6 @@ public final class StageTaskListenerTests extends ShellTestCase {
private SyncTransactionQueue mSyncQueue;
@Mock
private IconProvider mIconProvider;
- @Mock
- private StageTaskUnfoldController mStageTaskUnfoldController;
@Captor
private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
private SurfaceSession mSurfaceSession = new SurfaceSession();
@@ -92,8 +89,7 @@ public final class StageTaskListenerTests extends ShellTestCase {
mCallbacks,
mSyncQueue,
mSurfaceSession,
- mIconProvider,
- mStageTaskUnfoldController);
+ mIconProvider);
mRootTask = new TestRunningTaskInfoBuilder().build();
mRootTask.parentTaskId = INVALID_TASK_ID;
mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession).setName("test").build();
@@ -130,30 +126,6 @@ public final class StageTaskListenerTests extends ShellTestCase {
verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(true));
}
- @Test
- public void testTaskAppeared_notifiesUnfoldListener() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- final ActivityManager.RunningTaskInfo task =
- new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
-
- mStageTaskListener.onTaskAppeared(task, mSurfaceControl);
-
- verify(mStageTaskUnfoldController).onTaskAppeared(eq(task), eq(mSurfaceControl));
- }
-
- @Test
- public void testTaskVanished_notifiesUnfoldListener() {
- assumeFalse(ENABLE_SHELL_TRANSITIONS);
- final ActivityManager.RunningTaskInfo task =
- new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
- mStageTaskListener.onTaskAppeared(task, mSurfaceControl);
- clearInvocations(mStageTaskUnfoldController);
-
- mStageTaskListener.onTaskVanished(task);
-
- verify(mStageTaskUnfoldController).onTaskVanished(eq(task));
- }
-
@Test(expected = IllegalArgumentException.class)
public void testUnknownTaskVanished() {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 630d0d2c827c..46b040fd4325 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -73,6 +73,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.HandlerExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
@@ -91,7 +92,7 @@ import java.util.function.IntSupplier;
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class StartingSurfaceDrawerTests {
+public class StartingSurfaceDrawerTests extends ShellTestCase {
@Mock
private IBinder mBinder;
@Mock
@@ -249,7 +250,8 @@ public class StartingSurfaceDrawerTests {
any() /* window */, any() /* attrs */,
anyInt() /* viewVisibility */, anyInt() /* displayId */,
any() /* requestedVisibility */, any() /* outInputChannel */,
- any() /* outInsetsState */, any() /* outActiveControls */);
+ any() /* outInsetsState */, any() /* outActiveControls */,
+ any() /* outAttachedFrame */);
TaskSnapshotWindow mockSnapshotWindow = TaskSnapshotWindow.create(windowInfo,
mBinder,
snapshot, mTestExecutor, () -> {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
index 78e27c956807..3de50bb60470 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
@@ -47,6 +47,7 @@ import android.window.TaskSnapshot;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import org.junit.Test;
@@ -58,7 +59,7 @@ import org.junit.runner.RunWith;
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class TaskSnapshotWindowTest {
+public class TaskSnapshotWindowTest extends ShellTestCase {
private TaskSnapshotWindow mWindow;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
new file mode 100644
index 000000000000..1c0e46f7264e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.sysui;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.res.Configuration;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.ShellExecutor;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Locale;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class ShellControllerTest extends ShellTestCase {
+
+ @Mock
+ private ShellExecutor mExecutor;
+
+ private ShellController mController;
+ private TestConfigurationChangeListener mConfigChangeListener;
+ private TestKeyguardChangeListener mKeyguardChangeListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mKeyguardChangeListener = new TestKeyguardChangeListener();
+ mConfigChangeListener = new TestConfigurationChangeListener();
+ mController = new ShellController(mExecutor);
+ mController.onConfigurationChanged(getConfigurationCopy());
+ }
+
+ @After
+ public void tearDown() {
+ // Do nothing
+ }
+
+ @Test
+ public void testAddKeyguardChangeListener_ensureCallback() {
+ mController.addKeyguardChangeListener(mKeyguardChangeListener);
+
+ mController.onKeyguardVisibilityChanged(true, false, false);
+ assertTrue(mKeyguardChangeListener.visibilityChanged == 1);
+ assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0);
+ }
+
+ @Test
+ public void testDoubleAddKeyguardChangeListener_ensureSingleCallback() {
+ mController.addKeyguardChangeListener(mKeyguardChangeListener);
+ mController.addKeyguardChangeListener(mKeyguardChangeListener);
+
+ mController.onKeyguardVisibilityChanged(true, false, false);
+ assertTrue(mKeyguardChangeListener.visibilityChanged == 1);
+ assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0);
+ }
+
+ @Test
+ public void testAddRemoveKeyguardChangeListener_ensureNoCallback() {
+ mController.addKeyguardChangeListener(mKeyguardChangeListener);
+ mController.removeKeyguardChangeListener(mKeyguardChangeListener);
+
+ mController.onKeyguardVisibilityChanged(true, false, false);
+ assertTrue(mKeyguardChangeListener.visibilityChanged == 0);
+ assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0);
+ }
+
+ @Test
+ public void testKeyguardVisibilityChanged() {
+ mController.addKeyguardChangeListener(mKeyguardChangeListener);
+
+ mController.onKeyguardVisibilityChanged(true, true, true);
+ assertTrue(mKeyguardChangeListener.visibilityChanged == 1);
+ assertTrue(mKeyguardChangeListener.lastAnimatingDismiss);
+ assertTrue(mKeyguardChangeListener.lastOccluded);
+ assertTrue(mKeyguardChangeListener.lastAnimatingDismiss);
+ assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0);
+ }
+
+ @Test
+ public void testKeyguardDismissAnimationFinished() {
+ mController.addKeyguardChangeListener(mKeyguardChangeListener);
+
+ mController.onKeyguardDismissAnimationFinished();
+ assertTrue(mKeyguardChangeListener.visibilityChanged == 0);
+ assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 1);
+ }
+
+ @Test
+ public void testAddConfigurationChangeListener_ensureCallback() {
+ mController.addConfigurationChangeListener(mConfigChangeListener);
+
+ Configuration newConfig = getConfigurationCopy();
+ newConfig.densityDpi = 200;
+ mController.onConfigurationChanged(newConfig);
+ assertTrue(mConfigChangeListener.configChanges == 1);
+ }
+
+ @Test
+ public void testDoubleAddConfigurationChangeListener_ensureSingleCallback() {
+ mController.addConfigurationChangeListener(mConfigChangeListener);
+ mController.addConfigurationChangeListener(mConfigChangeListener);
+
+ Configuration newConfig = getConfigurationCopy();
+ newConfig.densityDpi = 200;
+ mController.onConfigurationChanged(newConfig);
+ assertTrue(mConfigChangeListener.configChanges == 1);
+ }
+
+ @Test
+ public void testAddRemoveConfigurationChangeListener_ensureNoCallback() {
+ mController.addConfigurationChangeListener(mConfigChangeListener);
+ mController.removeConfigurationChangeListener(mConfigChangeListener);
+
+ Configuration newConfig = getConfigurationCopy();
+ newConfig.densityDpi = 200;
+ mController.onConfigurationChanged(newConfig);
+ assertTrue(mConfigChangeListener.configChanges == 0);
+ }
+
+ @Test
+ public void testMultipleConfigurationChangeListeners() {
+ TestConfigurationChangeListener listener2 = new TestConfigurationChangeListener();
+ mController.addConfigurationChangeListener(mConfigChangeListener);
+ mController.addConfigurationChangeListener(listener2);
+
+ Configuration newConfig = getConfigurationCopy();
+ newConfig.densityDpi = 200;
+ mController.onConfigurationChanged(newConfig);
+ assertTrue(mConfigChangeListener.configChanges == 1);
+ assertTrue(listener2.configChanges == 1);
+ }
+
+ @Test
+ public void testRemoveListenerDuringCallback() {
+ TestConfigurationChangeListener badListener = new TestConfigurationChangeListener() {
+ @Override
+ public void onConfigurationChanged(Configuration newConfiguration) {
+ mController.removeConfigurationChangeListener(this);
+ }
+ };
+ mController.addConfigurationChangeListener(badListener);
+ mController.addConfigurationChangeListener(mConfigChangeListener);
+
+ // Ensure we don't fail just because a listener was removed mid-callback
+ Configuration newConfig = getConfigurationCopy();
+ newConfig.densityDpi = 200;
+ mController.onConfigurationChanged(newConfig);
+ }
+
+ @Test
+ public void testDensityChangeCallback() {
+ mController.addConfigurationChangeListener(mConfigChangeListener);
+
+ Configuration newConfig = getConfigurationCopy();
+ newConfig.densityDpi = 200;
+ mController.onConfigurationChanged(newConfig);
+ assertTrue(mConfigChangeListener.configChanges == 1);
+ assertTrue(mConfigChangeListener.densityChanges == 1);
+ assertTrue(mConfigChangeListener.smallestWidthChanges == 0);
+ assertTrue(mConfigChangeListener.themeChanges == 0);
+ assertTrue(mConfigChangeListener.localeChanges == 0);
+ }
+
+ @Test
+ public void testFontScaleChangeCallback() {
+ mController.addConfigurationChangeListener(mConfigChangeListener);
+
+ Configuration newConfig = getConfigurationCopy();
+ newConfig.fontScale = 2;
+ mController.onConfigurationChanged(newConfig);
+ assertTrue(mConfigChangeListener.configChanges == 1);
+ assertTrue(mConfigChangeListener.densityChanges == 1);
+ assertTrue(mConfigChangeListener.smallestWidthChanges == 0);
+ assertTrue(mConfigChangeListener.themeChanges == 0);
+ assertTrue(mConfigChangeListener.localeChanges == 0);
+ }
+
+ @Test
+ public void testSmallestWidthChangeCallback() {
+ mController.addConfigurationChangeListener(mConfigChangeListener);
+
+ Configuration newConfig = getConfigurationCopy();
+ newConfig.smallestScreenWidthDp = 100;
+ mController.onConfigurationChanged(newConfig);
+ assertTrue(mConfigChangeListener.configChanges == 1);
+ assertTrue(mConfigChangeListener.densityChanges == 0);
+ assertTrue(mConfigChangeListener.smallestWidthChanges == 1);
+ assertTrue(mConfigChangeListener.themeChanges == 0);
+ assertTrue(mConfigChangeListener.localeChanges == 0);
+ }
+
+ @Test
+ public void testThemeChangeCallback() {
+ mController.addConfigurationChangeListener(mConfigChangeListener);
+
+ Configuration newConfig = getConfigurationCopy();
+ newConfig.assetsSeq++;
+ mController.onConfigurationChanged(newConfig);
+ assertTrue(mConfigChangeListener.configChanges == 1);
+ assertTrue(mConfigChangeListener.densityChanges == 0);
+ assertTrue(mConfigChangeListener.smallestWidthChanges == 0);
+ assertTrue(mConfigChangeListener.themeChanges == 1);
+ assertTrue(mConfigChangeListener.localeChanges == 0);
+ }
+
+ @Test
+ public void testNightModeChangeCallback() {
+ mController.addConfigurationChangeListener(mConfigChangeListener);
+
+ Configuration newConfig = getConfigurationCopy();
+ newConfig.uiMode = Configuration.UI_MODE_NIGHT_YES;
+ mController.onConfigurationChanged(newConfig);
+ assertTrue(mConfigChangeListener.configChanges == 1);
+ assertTrue(mConfigChangeListener.densityChanges == 0);
+ assertTrue(mConfigChangeListener.smallestWidthChanges == 0);
+ assertTrue(mConfigChangeListener.themeChanges == 1);
+ assertTrue(mConfigChangeListener.localeChanges == 0);
+ }
+
+ @Test
+ public void testLocaleChangeCallback() {
+ mController.addConfigurationChangeListener(mConfigChangeListener);
+
+ Configuration newConfig = getConfigurationCopy();
+ // Just change the locales to be different
+ if (newConfig.locale == Locale.CANADA) {
+ newConfig.locale = Locale.US;
+ } else {
+ newConfig.locale = Locale.CANADA;
+ }
+ mController.onConfigurationChanged(newConfig);
+ assertTrue(mConfigChangeListener.configChanges == 1);
+ assertTrue(mConfigChangeListener.densityChanges == 0);
+ assertTrue(mConfigChangeListener.smallestWidthChanges == 0);
+ assertTrue(mConfigChangeListener.themeChanges == 0);
+ assertTrue(mConfigChangeListener.localeChanges == 1);
+ }
+
+ private Configuration getConfigurationCopy() {
+ final Configuration c = new Configuration(InstrumentationRegistry.getInstrumentation()
+ .getTargetContext().getResources().getConfiguration());
+ // In tests this might be undefined so make sure it's valid
+ c.assetsSeq = 1;
+ return c;
+ }
+
+ private class TestConfigurationChangeListener implements ConfigurationChangeListener {
+ // Counts of number of times each of the callbacks are called
+ public int configChanges;
+ public int densityChanges;
+ public int smallestWidthChanges;
+ public int themeChanges;
+ public int localeChanges;
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfiguration) {
+ configChanges++;
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ densityChanges++;
+ }
+
+ @Override
+ public void onSmallestScreenWidthChanged() {
+ smallestWidthChanges++;
+ }
+
+ @Override
+ public void onThemeChanged() {
+ themeChanges++;
+ }
+
+ @Override
+ public void onLocaleOrLayoutDirectionChanged() {
+ localeChanges++;
+ }
+ }
+
+ private class TestKeyguardChangeListener implements KeyguardChangeListener {
+ // Counts of number of times each of the callbacks are called
+ public int visibilityChanged;
+ public boolean lastVisibility;
+ public boolean lastOccluded;
+ public boolean lastAnimatingDismiss;
+ public int dismissAnimationFinished;
+
+ @Override
+ public void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
+ boolean animatingDismiss) {
+ lastVisibility = visible;
+ lastOccluded = occluded;
+ lastAnimatingDismiss = animatingDismiss;
+ visibilityChanged++;
+ }
+
+ @Override
+ public void onKeyguardDismissAnimationFinished() {
+ dismissAnimationFinished++;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperControllerTest.java
deleted file mode 100644
index d6142753b48a..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperControllerTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wm.shell.tasksurfacehelper;
-
-import static org.mockito.Mockito.verify;
-
-import android.platform.test.annotations.Presubmit;
-import android.testing.AndroidTestingRunner;
-import android.view.SurfaceControl;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.ShellExecutor;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@Presubmit
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class TaskSurfaceHelperControllerTest {
- private TaskSurfaceHelperController mTaskSurfaceHelperController;
- @Mock
- private ShellTaskOrganizer mMockTaskOrganizer;
- @Mock
- private ShellExecutor mMockShellExecutor;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mTaskSurfaceHelperController = new TaskSurfaceHelperController(
- mMockTaskOrganizer, mMockShellExecutor);
- }
-
- @Test
- public void testSetGameModeForTask() {
- mTaskSurfaceHelperController.setGameModeForTask(/*taskId*/1, /*gameMode*/3);
- verify(mMockTaskOrganizer).setSurfaceMetadata(1, SurfaceControl.METADATA_GAME_MODE, 3);
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index a0b12976b467..e2f2b71cea04 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -79,6 +79,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
@@ -98,7 +99,7 @@ import java.util.ArrayList;
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ShellTransitionTests {
+public class ShellTransitionTests extends ShellTestCase {
private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class);
private final TransactionPool mTransactionPool = mock(TransactionPool.class);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java
new file mode 100644
index 000000000000..46de60772766
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.unfold;
+
+import static com.android.wm.shell.unfold.UnfoldAnimationControllerTest.TestUnfoldTaskAnimator.UNSET;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.TaskInfo;
+import android.testing.AndroidTestingRunner;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.Predicate;
+
+/**
+ * Tests for {@link UnfoldAnimationController}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:UnfoldAnimationControllerTest
+ */
+@RunWith(AndroidTestingRunner.class)
+public class UnfoldAnimationControllerTest extends ShellTestCase {
+
+ @Mock
+ private TransactionPool mTransactionPool;
+ @Mock
+ private UnfoldTransitionHandler mUnfoldTransitionHandler;
+ @Mock
+ private SurfaceControl mLeash;
+
+ private UnfoldAnimationController mUnfoldAnimationController;
+
+ private final TestShellUnfoldProgressProvider mProgressProvider =
+ new TestShellUnfoldProgressProvider();
+ private final TestShellExecutor mShellExecutor = new TestShellExecutor();
+
+ private final TestUnfoldTaskAnimator mTaskAnimator1 = new TestUnfoldTaskAnimator();
+ private final TestUnfoldTaskAnimator mTaskAnimator2 = new TestUnfoldTaskAnimator();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mTransactionPool.acquire()).thenReturn(mock(SurfaceControl.Transaction.class));
+
+ final List<UnfoldTaskAnimator> animators = new ArrayList<>();
+ animators.add(mTaskAnimator1);
+ animators.add(mTaskAnimator2);
+ mUnfoldAnimationController = new UnfoldAnimationController(
+ mTransactionPool,
+ mProgressProvider,
+ animators,
+ () -> Optional.of(mUnfoldTransitionHandler),
+ mShellExecutor
+ );
+ }
+
+ @Test
+ public void testAppearedMatchingTask_appliesUnfoldProgress() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(2).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
+ }
+
+ @Test
+ public void testAppearedMatchingTaskTwoDifferentAnimators_appliesUnfoldProgressToBoth() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 1);
+ mTaskAnimator2.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo1 = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(1).build();
+ RunningTaskInfo taskInfo2 = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(2).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo1, mLeash);
+ mUnfoldAnimationController.onTaskAppeared(taskInfo2, mLeash);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
+ assertThat(mTaskAnimator2.mLastAppliedProgress).isEqualTo(0.5f);
+ }
+
+ @Test
+ public void testAppearedNonMatchingTask_doesNotApplyUnfoldProgress() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(0).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(UNSET);
+ }
+
+ @Test
+ public void testAppearedAndChangedToNonMatchingTask_doesNotApplyUnfoldProgress() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(2).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(0);
+ mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(UNSET);
+ }
+
+ @Test
+ public void testAppearedAndChangedToNonMatchingTaskAndBack_appliesUnfoldProgress() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(2).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(0);
+ mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(2);
+ mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
+ }
+
+ @Test
+ public void testAppearedNonMatchingTaskAndChangedToMatching_appliesUnfoldProgress() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(0).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(2);
+ mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
+ }
+
+ @Test
+ public void testAppearedMatchingTaskAndChanged_appliesUnfoldProgress() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(2).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+ mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
+ }
+
+ @Test
+ public void testShellTransitionRunning_doesNotApplyUnfoldProgress() {
+ when(mUnfoldTransitionHandler.willHandleTransition()).thenReturn(true);
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(2).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+
+ mUnfoldAnimationController.onStateChangeProgress(0.5f);
+ assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(UNSET);
+ }
+
+ @Test
+ public void testApplicableTaskDisappeared_resetsSurface() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 0);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(0).build();
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+ assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
+
+ mUnfoldAnimationController.onTaskVanished(taskInfo);
+
+ assertThat(mTaskAnimator1.mResetTasks).contains(taskInfo.taskId);
+ }
+
+ @Test
+ public void testApplicablePinnedTaskDisappeared_doesNotResetSurface() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(2).build();
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+ assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
+
+ mUnfoldAnimationController.onTaskVanished(taskInfo);
+
+ assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
+ }
+
+ @Test
+ public void testNonApplicableTaskAppearedDisappeared_doesNotResetSurface() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(0).build();
+
+ mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+ mUnfoldAnimationController.onTaskVanished(taskInfo);
+
+ assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
+ }
+
+ @Test
+ public void testInit_initsAndStartsAnimators() {
+ mUnfoldAnimationController.init();
+
+ assertThat(mTaskAnimator1.mInitialized).isTrue();
+ assertThat(mTaskAnimator1.mStarted).isTrue();
+ }
+
+ private static class TestShellUnfoldProgressProvider implements ShellUnfoldProgressProvider,
+ ShellUnfoldProgressProvider.UnfoldListener {
+
+ private final List<UnfoldListener> mListeners = new ArrayList<>();
+
+ @Override
+ public void addListener(Executor executor, UnfoldListener listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void onStateChangeStarted() {
+ mListeners.forEach(UnfoldListener::onStateChangeStarted);
+ }
+
+ @Override
+ public void onStateChangeProgress(float progress) {
+ mListeners.forEach(unfoldListener -> unfoldListener.onStateChangeProgress(progress));
+ }
+
+ @Override
+ public void onStateChangeFinished() {
+ mListeners.forEach(UnfoldListener::onStateChangeFinished);
+ }
+ }
+
+ public static class TestUnfoldTaskAnimator implements UnfoldTaskAnimator {
+
+ public static final float UNSET = -1f;
+ private Predicate<TaskInfo> mTaskMatcher = (info) -> false;
+
+ Map<Integer, TaskInfo> mTasksMap = new HashMap<>();
+ Set<Integer> mResetTasks = new HashSet<>();
+
+ boolean mInitialized = false;
+ boolean mStarted = false;
+ float mLastAppliedProgress = UNSET;
+
+ @Override
+ public void init() {
+ mInitialized = true;
+ }
+
+ @Override
+ public void start() {
+ mStarted = true;
+ }
+
+ @Override
+ public void stop() {
+ mStarted = false;
+ }
+
+ @Override
+ public boolean isApplicableTask(TaskInfo taskInfo) {
+ return mTaskMatcher.test(taskInfo);
+ }
+
+ @Override
+ public void applyAnimationProgress(float progress, Transaction transaction) {
+ mLastAppliedProgress = progress;
+ }
+
+ public void setTaskMatcher(Predicate<TaskInfo> taskMatcher) {
+ mTaskMatcher = taskMatcher;
+ }
+
+ @Override
+ public void onTaskAppeared(TaskInfo taskInfo, SurfaceControl leash) {
+ mTasksMap.put(taskInfo.taskId, taskInfo);
+ }
+
+ @Override
+ public void onTaskVanished(TaskInfo taskInfo) {
+ mTasksMap.remove(taskInfo.taskId);
+ }
+
+ @Override
+ public void onTaskChanged(TaskInfo taskInfo) {
+ mTasksMap.put(taskInfo.taskId, taskInfo);
+ }
+
+ @Override
+ public void resetSurface(TaskInfo taskInfo, Transaction transaction) {
+ mResetTasks.add(taskInfo.taskId);
+ }
+
+ @Override
+ public void resetAllSurfaces(Transaction transaction) {
+ mTasksMap.values().forEach((t) -> mResetTasks.add(t.taskId));
+ }
+
+ @Override
+ public boolean hasActiveTasks() {
+ return mTasksMap.size() > 0;
+ }
+
+ public List<TaskInfo> getCurrentTasks() {
+ return new ArrayList<>(mTasksMap.values());
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
new file mode 100644
index 000000000000..680034bd2ea5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.wm.shell.windowdecor;
+
+import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlBuilder;
+import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.view.Display;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+import android.window.WindowContainerTransaction;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.DisplayController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.function.Supplier;
+
+/**
+ * Tests for {@link WindowDecoration}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:WindowDecorationTests
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class WindowDecorationTests extends ShellTestCase {
+ private static final int CAPTION_HEIGHT_DP = 32;
+ private static final int SHADOW_RADIUS_DP = 5;
+
+ private final Rect mOutsetsDp = new Rect();
+ private final WindowDecoration.RelayoutResult<TestView> mRelayoutResult =
+ new WindowDecoration.RelayoutResult<>();
+
+ @Mock
+ private DisplayController mMockDisplayController;
+ @Mock
+ private ShellTaskOrganizer mMockShellTaskOrganizer;
+ @Mock
+ private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
+ @Mock
+ private SurfaceControlViewHost mMockSurfaceControlViewHost;
+ @Mock
+ private TestView mMockView;
+ @Mock
+ private WindowContainerTransaction mMockWindowContainerTransaction;
+
+ private SurfaceControl.Builder mMockSurfaceControlBuilder;
+ private SurfaceControl.Transaction mMockSurfaceControlTransaction;
+
+ @Before
+ public void setUp() {
+ mMockSurfaceControlBuilder = createMockSurfaceControlBuilder(mock(SurfaceControl.class));
+ mMockSurfaceControlTransaction = createMockSurfaceControlTransaction();
+
+ doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory)
+ .create(any(), any(), any(), anyBoolean());
+ }
+
+ @Test
+ public void testNotCrashWhenDisplayAppearsAfterTask() {
+ doReturn(mock(Display.class)).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final int displayId = Display.DEFAULT_DISPLAY + 1;
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder()
+ .setBackgroundColor(Color.BLACK);
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setVisible(true)
+ .build();
+
+ final TestWindowDecoration windowDecor =
+ createWindowDecoration(taskInfo, new SurfaceControl());
+ windowDecor.relayout(taskInfo);
+
+ // It shouldn't show the window decoration when it can't obtain the display instance.
+ assertThat(mRelayoutResult.mRootView).isNull();
+
+ final ArgumentCaptor<DisplayController.OnDisplaysChangedListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(DisplayController.OnDisplaysChangedListener.class);
+ verify(mMockDisplayController).addDisplayWindowListener(listenerArgumentCaptor.capture());
+ final DisplayController.OnDisplaysChangedListener listener =
+ listenerArgumentCaptor.getValue();
+
+ // Adding an irrelevant display shouldn't change the result.
+ listener.onDisplayAdded(Display.DEFAULT_DISPLAY);
+ assertThat(mRelayoutResult.mRootView).isNull();
+
+ final Display mockDisplay = mock(Display.class);
+ doReturn(mockDisplay).when(mMockDisplayController).getDisplay(displayId);
+
+ listener.onDisplayAdded(displayId);
+
+ // The listener should be removed when the display shows up.
+ verify(mMockDisplayController).removeDisplayWindowListener(same(listener));
+
+ assertThat(mRelayoutResult.mRootView).isSameInstanceAs(mMockView);
+ verify(mMockSurfaceControlViewHostFactory)
+ .create(any(), eq(mockDisplay), any(), anyBoolean());
+ verify(mMockSurfaceControlViewHost).setView(same(mMockView), any());
+ }
+
+ private TestWindowDecoration createWindowDecoration(
+ ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
+ return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
+ taskInfo, testSurface, () -> mMockSurfaceControlBuilder,
+ mMockSurfaceControlViewHostFactory);
+ }
+
+ private static class TestView extends View implements TaskFocusStateConsumer {
+ private TestView(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void setTaskFocusState(boolean focused) {}
+ }
+
+ private class TestWindowDecoration extends WindowDecoration<TestView> {
+ TestWindowDecoration(Context context, DisplayController displayController,
+ ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
+ SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
+ super(context, displayController, taskOrganizer, taskInfo, taskSurface,
+ surfaceControlBuilderSupplier, surfaceControlViewHostFactory);
+ }
+
+ @Override
+ void relayout(ActivityManager.RunningTaskInfo taskInfo) {
+ relayout(null /* taskInfo */, 0 /* layoutResId */, mMockView, CAPTION_HEIGHT_DP,
+ mOutsetsDp, SHADOW_RADIUS_DP, mMockSurfaceControlTransaction,
+ mMockWindowContainerTransaction, mRelayoutResult);
+ }
+ }
+}
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 1e5be6c3eed7..4b0ddd2fa2ef 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -201,8 +201,9 @@ void JankTracker::finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsRepo
// If we are in triple buffering, we have enough buffers in queue to sustain a single frame
// drop without jank, so adjust the frame interval to the deadline.
if (isTripleBuffered) {
- deadline += frameInterval;
- frame.set(FrameInfoIndex::FrameDeadline) += frameInterval;
+ int64_t originalDeadlineDuration = deadline - frame[FrameInfoIndex::IntendedVsync];
+ deadline = mNextFrameStartUnstuffed + originalDeadlineDuration;
+ frame.set(FrameInfoIndex::FrameDeadline) = deadline;
}
// If we hit the deadline, cool!
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 4cce87ad1a2f..a3ba88e4ee8a 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -90,11 +90,36 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRec
SkRect srcRect = inSrcRect.toSkRect();
- SkRect imageSrcRect =
- SkRect::MakeLTRB(cropRect.left, cropRect.top, cropRect.right, cropRect.bottom);
- if (imageSrcRect.isEmpty()) {
- imageSrcRect = SkRect::MakeIWH(description.width, description.height);
+ SkRect imageSrcRect = SkRect::MakeIWH(description.width, description.height);
+ SkISize imageWH = SkISize::Make(description.width, description.height);
+ if (cropRect.left < cropRect.right && cropRect.top < cropRect.bottom) {
+ imageSrcRect =
+ SkRect::MakeLTRB(cropRect.left, cropRect.top, cropRect.right, cropRect.bottom);
+ imageWH = SkISize::Make(cropRect.right - cropRect.left, cropRect.bottom - cropRect.top);
+
+ // Chroma channels of YUV420 images are subsampled we may need to shrink the crop region by
+ // a whole texel on each side. Since skia still adds its own 0.5 inset, we apply an
+ // additional 0.5 inset. See GLConsumer::computeTransformMatrix for details.
+ float shrinkAmount = 0.0f;
+ switch (description.format) {
+ // Use HAL formats since some AHB formats are only available in vndk
+ case HAL_PIXEL_FORMAT_YCBCR_420_888:
+ case HAL_PIXEL_FORMAT_YV12:
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+ shrinkAmount = 0.5f;
+ break;
+ default:
+ break;
+ }
+
+ // Shrink the crop if it has more than 1-px and differs from the buffer size.
+ if (imageWH.width() > 1 && imageWH.width() < (int32_t)description.width)
+ imageSrcRect = imageSrcRect.makeInset(shrinkAmount, 0);
+
+ if (imageWH.height() > 1 && imageWH.height() < (int32_t)description.height)
+ imageSrcRect = imageSrcRect.makeInset(0, shrinkAmount);
}
+
ALOGV("imageSrcRect = " RECT_STRING, SK_RECT_ARGS(imageSrcRect));
// Represents the "logical" width/height of the texture. That is, the dimensions of the buffer
@@ -153,7 +178,7 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRec
*/
SkMatrix m;
- const SkRect imageDstRect = SkRect::MakeIWH(imageSrcRect.width(), imageSrcRect.height());
+ const SkRect imageDstRect = SkRect::Make(imageWH);
const float px = imageDstRect.centerX();
const float py = imageDstRect.centerY();
if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
diff --git a/libs/hwui/tests/unit/JankTrackerTests.cpp b/libs/hwui/tests/unit/JankTrackerTests.cpp
index 5b397de36a86..b67e419e7d4a 100644
--- a/libs/hwui/tests/unit/JankTrackerTests.cpp
+++ b/libs/hwui/tests/unit/JankTrackerTests.cpp
@@ -195,3 +195,68 @@ TEST(JankTracker, doubleStuffedThenPauseThenJank) {
ASSERT_EQ(3, container.get()->totalFrameCount());
ASSERT_EQ(2, container.get()->jankFrameCount());
}
+
+TEST(JankTracker, doubleStuffedTwoIntervalBehind) {
+ std::mutex mutex;
+ ProfileDataContainer container(mutex);
+ JankTracker jankTracker(&container);
+ std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
+
+ uint64_t frameNumber = 0;
+ uint32_t surfaceId = 0;
+
+ // First frame janks
+ FrameInfo* info = jankTracker.startFrame();
+ info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
+ info->set(FrameInfoIndex::Vsync) = 101_ms;
+ info->set(FrameInfoIndex::SwapBuffersCompleted) = 107_ms;
+ info->set(FrameInfoIndex::GpuCompleted) = 117_ms;
+ info->set(FrameInfoIndex::FrameCompleted) = 117_ms;
+ info->set(FrameInfoIndex::FrameInterval) = 16_ms;
+ info->set(FrameInfoIndex::FrameDeadline) = 116_ms;
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
+
+ ASSERT_EQ(1, container.get()->jankFrameCount());
+
+ // Second frame is long, but doesn't jank because double-stuffed.
+ // Second frame duration is between 1*interval ~ 2*interval
+ info = jankTracker.startFrame();
+ info->set(FrameInfoIndex::IntendedVsync) = 116_ms;
+ info->set(FrameInfoIndex::Vsync) = 116_ms;
+ info->set(FrameInfoIndex::SwapBuffersCompleted) = 129_ms;
+ info->set(FrameInfoIndex::GpuCompleted) = 133_ms;
+ info->set(FrameInfoIndex::FrameCompleted) = 133_ms;
+ info->set(FrameInfoIndex::FrameInterval) = 16_ms;
+ info->set(FrameInfoIndex::FrameDeadline) = 132_ms;
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
+
+ ASSERT_EQ(1, container.get()->jankFrameCount());
+
+ // Third frame is even longer, cause a jank
+ // Third frame duration is between 2*interval ~ 3*interval
+ info = jankTracker.startFrame();
+ info->set(FrameInfoIndex::IntendedVsync) = 132_ms;
+ info->set(FrameInfoIndex::Vsync) = 132_ms;
+ info->set(FrameInfoIndex::SwapBuffersCompleted) = 160_ms;
+ info->set(FrameInfoIndex::GpuCompleted) = 165_ms;
+ info->set(FrameInfoIndex::FrameCompleted) = 165_ms;
+ info->set(FrameInfoIndex::FrameInterval) = 16_ms;
+ info->set(FrameInfoIndex::FrameDeadline) = 148_ms;
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
+
+ ASSERT_EQ(2, container.get()->jankFrameCount());
+
+ // 4th frame is double-stuffed with a 2 * interval latency
+ // 4th frame duration is between 2*interval ~ 3*interval
+ info = jankTracker.startFrame();
+ info->set(FrameInfoIndex::IntendedVsync) = 148_ms;
+ info->set(FrameInfoIndex::Vsync) = 148_ms;
+ info->set(FrameInfoIndex::SwapBuffersCompleted) = 170_ms;
+ info->set(FrameInfoIndex::GpuCompleted) = 181_ms;
+ info->set(FrameInfoIndex::FrameCompleted) = 181_ms;
+ info->set(FrameInfoIndex::FrameInterval) = 16_ms;
+ info->set(FrameInfoIndex::FrameDeadline) = 164_ms;
+ jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
+
+ ASSERT_EQ(2, container.get()->jankFrameCount());
+}
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index 4ec4767a35cc..05fbc7a52ec6 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -22,11 +22,15 @@
}
],
"file_patterns": ["(?i)drm|crypto"]
- }
- ],
- "imports": [
+ },
{
- "path": "frameworks/av/drm/mediadrm/plugins"
+ "name": "CtsMediaDrmFrameworkTestCases",
+ "options" : [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ }
+ ],
+ "file_patterns": ["(?i)drm|crypto"]
}
]
}
diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl
index b136d5bc4db3..2bdd5c8bc977 100644
--- a/media/java/android/media/projection/IMediaProjection.aidl
+++ b/media/java/android/media/projection/IMediaProjection.aidl
@@ -17,7 +17,7 @@
package android.media.projection;
import android.media.projection.IMediaProjectionCallback;
-import android.window.WindowContainerToken;
+import android.os.IBinder;
/** {@hide} */
interface IMediaProjection {
@@ -31,14 +31,14 @@ interface IMediaProjection {
void unregisterCallback(IMediaProjectionCallback callback);
/**
- * Returns the {@link android.window.WindowContainerToken} identifying the task to record, or
- * {@code null} if there is none.
+ * Returns the {@link android.os.IBinder} identifying the task to record, or {@code null} if
+ * there is none.
*/
- WindowContainerToken getTaskRecordingWindowContainerToken();
+ IBinder getLaunchCookie();
/**
- * Updates the {@link android.window.WindowContainerToken} identifying the task to record, or
- * {@code null} if there is none.
+ * Updates the {@link android.os.IBinder} identifying the task to record, or {@code null} if
+ * there is none.
*/
- void setTaskRecordingWindowContainerToken(in WindowContainerToken token);
+ void setLaunchCookie(in IBinder launchCookie);
}
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index ba7bf3f5f5d3..ae44fc575f7c 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -25,13 +25,13 @@ import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.hardware.display.VirtualDisplayConfig;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.Log;
import android.view.ContentRecordingSession;
import android.view.Surface;
-import android.window.WindowContainerToken;
import java.util.Map;
@@ -172,18 +172,16 @@ public final class MediaProjection {
@NonNull VirtualDisplayConfig.Builder virtualDisplayConfig,
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
try {
- final WindowContainerToken taskWindowContainerToken =
- mImpl.getTaskRecordingWindowContainerToken();
+ final IBinder launchCookie = mImpl.getLaunchCookie();
Context windowContext = null;
ContentRecordingSession session;
- if (taskWindowContainerToken == null) {
+ if (launchCookie == null) {
windowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(),
TYPE_APPLICATION, null /* options */);
session = ContentRecordingSession.createDisplaySession(
windowContext.getWindowContextToken());
} else {
- session = ContentRecordingSession.createTaskSession(
- taskWindowContainerToken.asBinder());
+ session = ContentRecordingSession.createTaskSession(launchCookie);
}
virtualDisplayConfig.setWindowManagerMirroring(true);
final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
index 480bc484b73a..004b5632a18b 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Stroom jou foon se programme"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Gee &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toegang tot hierdie inligting op jou foon"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Oorkruistoestel-dienste"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toegang tot jou foon se foto\'s, media en kennisgewings"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toestemming om programme tussen jou toestelle te stroom"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Gee &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toegang tot hierdie inligting op jou foon"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Foto\'s en media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play Dienste"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toestemming om programme tussen jou toestelle te stroom"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toegang tot jou foon se foto\'s, media en kennisgewings"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Laat toe"</string>
diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml
index 5f2bd7f1b3f9..b1f414414a15 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"የስልክዎን መተግበሪያዎች በዥረት ይልቀቁ"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ይህን መረጃ ከስልክዎ እንዲደርስበት ይፍቀዱለት"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"መሣሪያ ተሻጋሪ አገልግሎቶች"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> የእርስዎን ስልክ ፎቶዎች፣ ሚዲያ እና ማሳወቂያዎች ለመድረስ የእርስዎን <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ወክሎ ፈቃድ እየጠየቀ ነው"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ይህን መረጃ ከስልክዎ ላይ እንዲደርስ ይፍቀዱለት"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"ፎቶዎች እና ሚዲያ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"የGoogle Play አገልግሎቶች"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> በእርስዎ መሣሪያዎች መካከል መተግበሪያዎችን በዥረት ለመልቀቅ የእርስዎን <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ወክሎ ፈቃድ እየጠየቀ ነው"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ፍቀድ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index 138386b0b8e6..4268c0b909ee 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"بث تطبيقات هاتفك"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"‏السماح لتطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; بالوصول إلى هذه المعلومات من هاتفك"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"الخدمات التي تعمل بين الأجهزة"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"تطلب \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> للوصول إلى الصور والوسائط والإشعارات في هاتفك."</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"‏السماح لتطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; بالوصول إلى هذه المعلومات من هاتفك"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"الصور والوسائط"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"‏خدمات Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"يطلب التطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> الحصول على إذن نيابةً عن <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> لمشاركة التطبيقات بين أجهزتك."</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"السماح"</string>
diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml
index 95c4e68c0dcf..644140035398 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"আপোনাৰ ফ’নৰ এপ্‌ ষ্ট্ৰীম কৰক"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ ফ’নৰ পৰা এই তথ্যখিনি এক্সেছ কৰাৰ অনুমতি দিয়ক"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"ক্ৰছ-ডিভাইচ সেৱাসমূহ"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনৰ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ৰ হৈ আপোনাৰ ফ’নৰ ফট’, মিডিয়া আৰু জাননী এক্সেছ কৰাৰ বাবে অনুৰোধ জনাইছে"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ ফ’নৰ পৰা এই তথ্যখিনি এক্সেছ কৰাৰ অনুমতি দিয়ক"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"ফট’ আৰু মিডিয়া"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play সেৱা"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনৰ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ৰ হৈ আপোনাৰ ডিভাইচসমূহৰ মাজত এপ্‌ ষ্ট্ৰীম কৰাৰ বাবে অনুৰোধ জনাইছে"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইচ"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিয়ক"</string>
diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml
index 9578fb5fcf47..3549317b383f 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Telefonunuzun tətbiqlərini yayımlayın"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə telefonunuzdan bu məlumata giriş icazəsi verin"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Cihazlararası xidmətlər"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqi <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> adından telefonunuzun fotoları, mediası və bildirişlərinə giriş üçün icazə istəyir"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqi <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> adından cihazlarınız arasında tətbiqləri yayımlamaq üçün icazə istəyir"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə telefonunuzdan bu məlumata giriş icazəsi verin"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Foto və media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play xidmətləri"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqi <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> adından cihazlarınız arasında tətbiqləri yayımlamaq üçün icazə istəyir"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqi <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> adından telefonunuzun fotoları, mediası və bildirişlərinə giriş üçün icazə istəyir"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"İcazə verin"</string>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
index d82124758ce6..75a4f1d2aeeb 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Strimujte aplikacije na telefonu"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Dozvolite da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pristupa ovim informacijama sa telefona"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Usluge na više uređaja"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za pristup slikama, medijskom sadržaju i obaveštenjima sa telefona"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za strimovanje aplikacija između uređaja"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Dozvolite da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pristupa ovim informacijama sa telefona"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Slike i mediji"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play usluge"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za strimovanje aplikacija između uređaja"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za pristup slikama, medijskom sadržaju i obaveštenjima sa telefona"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
index 2086f2eddf09..82ff9ae00138 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Трансліруйце змесціва праграм з вашага тэлефона"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; мець доступ да гэтай інфармацыі з вашага тэлефона"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Сэрвісы для некалькіх прылад"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на доступ да фота, медыяфайлаў і апавяшчэнняў вашага тэлефона"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; мець доступ да гэтай інфармацыі з вашага тэлефона"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Фота і медыяфайлы"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Сэрвісы Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на перадачу праграм плынню паміж вашымі прыладамі"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"прылада"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Дазволіць"</string>
diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml
index 134ae1c90bab..0154bfb7aac7 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Поточно предаване на приложенията на телефона ви"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Разрешете на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да осъществява достъп до тази информация от телефона ви"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Услуги за различни устройства"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ иска разрешение от името на <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за достъп до снимките, мултимедията и известията на телефона ви"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Разрешете на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да осъществява достъп до тази информация от телефона ви"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Снимки и мултимедия"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Услуги за Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> да предава поточно приложения между устройствата ви"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Разрешаване"</string>
diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml
index 08c4a16b9000..abdc12847bd3 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"আপনার ফোনের অ্যাপ স্ট্রিমিংয়ের মাধ্যমে কাস্ট করুন"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"আপনার ফোন থেকে &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; অ্যাপকে এই তথ্য অ্যাক্সেস করার অনুমতি দিন"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"ক্রস-ডিভাইস পরিষেবা"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"আপনার ফোনের ফটো, মিডিয়া এবং তথ্য অ্যাক্সেস করার জন্য <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-এর হয়ে অনুমতি চাইছে"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"আপনার ফোন থেকে &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-কে এই তথ্য অ্যাক্সেস করার অনুমতি দিন"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"ফটো ও মিডিয়া"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play পরিষেবা"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"আপনার ডিভাইসগুলির মধ্যে অ্যাপ স্ট্রিম করার জন্য <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-এর হয়ে অনুমতি চাইছে"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিন"</string>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
index 340fd6a1e532..d7423ac9ceee 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Prenosite aplikacije s telefona"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Dozvolite da aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pristupa ovim informacijama s telefona"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Usluga na više uređaja"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> zahtijeva odobrenje da pristupi fotografijama, medijima i odobrenjima na vašem telefonu"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> zahtijeva odobrenje da prenosi aplikacije između vaših uređaja"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da pristupa ovim informacijama s vašeg telefona"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotografije i mediji"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play usluge"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> zahtijeva odobrenje da prenosi aplikacije između vaših uređaja"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> zahtijeva odobrenje da pristupi fotografijama, medijima i odobrenjima na vašem telefonu"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
index 967b390c36a5..a40efd019477 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Reprodueix en continu aplicacions del telèfon"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Permet que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; accedeixi a aquesta informació del telèfon"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Serveis multidispositiu"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del teu dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) per accedir a les fotos, el contingut multimèdia i les notificacions del telèfon"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Permet que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; accedeixi a aquesta informació del telèfon"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotos i contingut multimèdia"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Serveis de Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del teu dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) per reproduir en continu aplicacions entre els dispositius"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permet"</string>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index 7ab5f6243b11..cfea6c388351 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Streamujte aplikace v telefonu"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Povolte aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; přístup k těmto informacím z vašeho telefonu"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Služby pro více zařízení"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> požaduje za vaše zařízení <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> oprávnění k přístupu k fotkám, médiím a oznámením v telefonu"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Povolte aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; přístup k těmto informacím z vašeho telefonu"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotky a média"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Služby Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> požaduje za vaše zařízení <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> oprávnění ke streamování aplikací mezi zařízeními"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Povolit"</string>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index 2fb2e6e4d81c..5ba30ec39c51 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Stream din telefons apps"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Giv &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; adgang til disse oplysninger fra din telefon"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Tjenester, som kan tilsluttes en anden enhed"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til at få adgang til din telefons billeder, medier og notifikationer"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til at streame apps mellem dine enheder"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Tillad, at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; får adgang til disse oplysninger fra din telefon"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Billeder og medier"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play-tjenester"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til at streame apps mellem dine enheder"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til at få adgang til din telefons billeder, medier og notifikationer"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Tillad"</string>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index 0b6a195763c6..21f0322ce873 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Smartphone-Apps streamen"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; Zugriff auf diese Informationen von deinem Smartphone gewähren"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Geräteübergreifende Dienste"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet im Namen deines <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> um die Berechtigung zum Zugriff auf die Fotos, Medien und Benachrichtigungen deines Smartphones"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; Zugriff auf diese Informationen von deinem Smartphone gewähren"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotos und Medien"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play-Dienste"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet im Namen deines <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> um die Berechtigung zum Streamen von Apps zwischen deinen Geräten"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Zulassen"</string>
diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml
index 726009fb0638..2c27f9281e4c 100644
--- a/packages/CompanionDeviceManager/res/values-el/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Μεταδώστε σε ροή τις εφαρμογές του τηλεφώνου σας"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Να επιτρέπεται στο &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; η πρόσβαση σε αυτές τις πληροφορίες από το τηλέφωνό σας."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Υπηρεσίες πολλών συσκευών"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά εκ μέρους της συσκευής σας <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> άδεια για πρόσβαση στις φωτογραφίες, τα αρχεία μέσων και τις ειδοποιήσεις του τηλεφώνου σας"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά εκ μέρους της συσκευής σας <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> άδεια για ροή εφαρμογών μεταξύ των συσκευών σας"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Επιτρέψτε στην εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; να έχει πρόσβαση σε αυτές τις πληροφορίες από το τηλέφωνό σας"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Φωτογραφίες και μέσα"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Υπηρεσίες Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά εκ μέρους της συσκευής σας <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> άδεια για ροή εφαρμογών μεταξύ των συσκευών σας"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά εκ μέρους της συσκευής σας <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> άδεια για πρόσβαση στις φωτογραφίες, τα αρχεία μέσων και τις ειδοποιήσεις του τηλεφώνου σας"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Να επιτρέπεται"</string>
diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
index dacfaaeee4a3..982101456424 100644
--- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Stream your phone’s apps"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
index dacfaaeee4a3..982101456424 100644
--- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Stream your phone’s apps"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
index dacfaaeee4a3..982101456424 100644
--- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Stream your phone’s apps"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
index dacfaaeee4a3..982101456424 100644
--- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Stream your phone’s apps"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
index 56d979a7b950..ea1ff66c672d 100644
--- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎‎‏‎‏‎‎‏‏‏‎‏‎‎‏‎‎‏‏‏‎Stream your phone’s apps‎‏‎‎‏‎"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‎‎‎‏‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to access this information from your phone‎‏‎‎‏‎"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‏‎‏‎‏‎‎‏‎‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‏‎Cross-device services‎‏‎‎‏‎"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‎‏‎‏‎‏‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‏‎‏‎‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is requesting permission on behalf of your ‎‏‎‎‏‏‎<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>‎‏‎‎‏‏‏‎ to access your phone’s photos, media, and notifications‎‏‎‎‏‎"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‏‎‏‎‏‎‎‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‎‎‎‏‎‎‏‏‎‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is requesting permission on behalf of your ‎‏‎‎‏‏‎<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>‎‏‎‎‏‏‏‎ to stream apps between your devices‎‏‎‎‏‎"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to access this information from your phone‎‏‎‎‏‎"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎Photos and media‎‏‎‎‏‎"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎Google Play services‎‏‎‎‏‎"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is requesting permission on behalf of your ‎‏‎‎‏‏‎<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>‎‏‎‎‏‏‏‎ to stream apps between your devices‎‏‎‎‏‎"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‎‎‎‎‏‏‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is requesting permission on behalf of your ‎‏‎‎‏‏‎<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>‎‏‎‎‏‏‏‎ to access your phone’s photos, media, and notifications‎‏‎‎‏‎"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎device‎‏‎‎‏‎"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎Allow‎‏‎‎‏‎"</string>
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
index 54d13c4a42a9..143e1cb32178 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Transmitir las apps de tu teléfono"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Permite que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información de tu teléfono"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicios multidispositivo"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita tu permiso en nombre de <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para acceder a las fotos, el contenido multimedia y las notificaciones de tu teléfono"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Permite que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información de tu teléfono"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotos y contenido multimedia"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Servicios de Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita tu permiso en nombre de <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para transmitir apps entre dispositivos"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
index 0d0b10e7ce4a..6edc02ae647e 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Emite las aplicaciones de tu teléfono"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información desde tu teléfono"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicios multidispositivo"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para acceder a las fotos, los archivos multimedia y las notificaciones de tu teléfono"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información desde tu teléfono"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotos y elementos multimedia"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Servicios de Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para emitir aplicaciones en otros dispositivos tuyos"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml
index b1603903cfc7..d15b975bac89 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Telefoni rakenduste voogesitamine"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Lubage rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pääseda teie telefonis juurde sellele teabele"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Seadmeülesed teenused"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> taotlevad teie seadme <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> nimel luba pääseda juurde telefoni fotodele, meediale ja märguannetele"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Lubage rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pääseda teie telefonis juurde sellele teabele"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotod ja meedia"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play teenused"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> nimel luba teie seadmete vahel rakendusi voogesitada"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"seade"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Luba"</string>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 395c385948da..fda2c4e3c4b6 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Igorri zuzenean telefonoko aplikazioak"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Eman informazioa telefonotik hartzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Gailu baterako baino gehiagotarako zerbitzuak"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Telefonoko argazkiak, multimedia-edukia eta jakinarazpenak atzitzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuaren izenean"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Eman informazio hori telefonotik hartzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Argazkiak eta multimedia-edukia"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play Services"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"Gailu batetik bestera aplikazioak igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuaren izenean"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index beabaf1842bd..0edab4d03d5f 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"جاری‌سازی برنامه‌های تلفن"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"‏اجازه دادن به &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای دسترسی به اطلاعات تلفن"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"سرویس‌های بین‌دستگاهی"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> اجازه می‌خواهد ازطرف <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> به عکس‌ها، رسانه، و اعلان‌های تلفن شما دسترسی پیدا کند"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"‏&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; مجاز می‌شود به این اطلاعات در دستگاهتان دسترسی پیدا کند"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"عکس‌ها و رسانه‌ها"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"‏خدمات Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> اجازه می‌خواهد ازطرف <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> برنامه‌ها را بین دستگاه‌های شما جاری‌سازی کند"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"اجازه دادن"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml
index 35e0e47a0649..b34bb364df0c 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Striimaa puhelimen sovelluksia"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Salli, että &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; saa pääsyn näihin puhelimesi tietoihin"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Laitteidenväliset palvelut"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteeltasi (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) lupaa päästä puhelimesi kuviin, mediaan ja ilmoituksiin"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Salli pääsy tähän tietoon puhelimellasi: &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Kuvat ja media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play Palvelut"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteeltasi (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) lupaa striimata sovelluksia laitteidesi välillä"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"laite"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Salli"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
index 1b1727e8bc10..7e46c468d0b2 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Diffusez les applications de votre téléphone"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Autorisez &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à accéder à ces informations à partir de votre téléphone"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Services multiappareils"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> pour accéder aux photos, aux fichiers multimédias et aux notifications de votre téléphone"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Autorisez &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à accéder à ces informations à partir de votre téléphone"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Photos et fichiers multimédias"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Services Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> pour diffuser des applications entre vos appareils"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
index 30db3187857e..b8cd4995bf5a 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Diffuser en streaming les applis de votre téléphone"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à accéder à ces informations depuis votre téléphone"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Services inter-appareils"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> pour accéder aux photos, contenus multimédias et notifications de votre téléphone"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à accéder à ces informations depuis votre téléphone"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Photos et contenus multimédias"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Services Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> pour caster des applis d\'un appareil à l\'autre"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
index c692c034cf22..ae9111ebbcba 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Emite as aplicacións do teu teléfono"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Permitir que a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información desde o teu teléfono"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Servizos multidispositivo"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome do teu dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) para acceder ás fotos, ao contido multimedia e ás notificacións do teléfono"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información desde o teu teléfono"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotos e contido multimedia"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Servizos de Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome do teu dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) para emitir aplicacións entre os teus aparellos"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index 8c92de8b96e0..747b331dd30d 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"તમારા ફોનની ઍપ સ્ટ્રીમ કરો"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"તમારા ફોનમાંથી આ માહિતી ઍક્સેસ કરવા માટે, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને મંજૂરી આપો"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"ક્રોસ-ડિવાઇસ સેવાઓ"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> વતી તમારા ફોનના ફોટા, મીડિયા અને નોટિફિકેશન ઍક્સેસ કરવાની પરવાનગીની વિનંતી કરી રહી છે"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"તમારા ફોનમાંથી આ માહિતી ઍક્સેસ કરવા માટે, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને મંજૂરી આપો"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"ફોટા અને મીડિયા"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play સેવાઓ"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> વતી તમારા ડિવાઇસ વચ્ચે ઍપ સ્ટ્રીમ કરવાની પરવાનગીની વિનંતી કરી રહી છે"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"મંજૂરી આપો"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index 1ac399946cbe..8f06cc21ae49 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"अपने फ़ोन के ऐप्लिकेशन को स्ट्रीम करें"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को अपने फ़ोन से यह जानकारी ऐक्सेस करने की अनुमति दें"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"क्रॉस-डिवाइस से जुड़ी सेवाएं"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> की ओर से, फ़ोन में मौजूद फ़ोटो, मीडिया, और सूचनाओं को ऐक्सेस करने की अनुमति मांग रही हैं"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को अपने फ़ोन से यह जानकारी ऐक्सेस करने की अनुमति दें"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"फ़ोटो और मीडिया"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play सेवाएं"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> की ओर से, डिवाइसों के बीच ऐप्लिकेशन को स्ट्रीम करने की अनुमति मांग रहा है"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"अनुमति दें"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml
index 6e186281e3b6..7f8a5893ab8e 100644
--- a/packages/CompanionDeviceManager/res/values-hr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Streaming aplikacija vašeg telefona"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Omogućite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da pristupa informacijama s vašeg telefona"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Usluge na različitim uređajima"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> da pristupi fotografijama, medijskim sadržajima i obavijestima na telefonu"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za emitiranje aplikacija između vaših uređaja"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Omogućite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da pristupa informacijama s vašeg telefona"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotografije i mediji"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Usluge za Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za streamanje aplikacija između vaših uređaja"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za pristup fotografijama, medijskim sadržajima i obavijestima na telefonu"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Dopusti"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml
index 2f8dd8536844..389a8213dcbd 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"A telefon alkalmazásainak streamelése"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Engedélyezi a(z) „<xliff:g id="APP_NAME">%1$s</xliff:g>” alkalmazás számára az információhoz való hozzáférést a telefonról"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Többeszközös szolgáltatások"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> nevében a telefonon tárolt fotókhoz, médiatartalmakhoz és értesítésekhez való hozzáféréshez"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Engedélyezi a(z) „<xliff:g id="APP_NAME">%1$s</xliff:g>” alkalmazás számára az információhoz való hozzáférést a telefonról"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotók és médiatartalmak"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play-szolgáltatások"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> nevében az alkalmazások eszközök közötti streameléséhez"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"eszköz"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Engedélyezés"</string>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
index aed650b7c472..96a6fe28d74f 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Հեռարձակել հեռախոսի հավելվածները"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Թույլատրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին օգտագործել այս տեղեկությունները ձեր հեռախոսից"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Միջսարքային ծառայություններ"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր հեռախոսի լուսանկարները, մեդիաֆայլերն ու ծանուցումները տեսնելու համար"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Թույլատրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին օգտագործել այս տեղեկությունները ձեր հեռախոսից"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Լուսանկարներ և մուլտիմեդիա"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play ծառայություններ"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր սարքերի միջև հավելվածներ հեռարձակելու համար"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Թույլատրել"</string>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index 048325c4f676..a5fa8011cc87 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Streaming aplikasi ponsel"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengakses informasi ini dari ponsel Anda"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Layanan lintas perangkat"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> untuk mengakses foto, media, dan notifikasi ponsel Anda"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; mengakses informasi ini dari ponsel Anda"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Foto dan media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Layanan Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> untuk menstreaming aplikasi di antara perangkat Anda"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Izinkan"</string>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index 3f5a3de9de77..7313cda259c0 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Streymdu forritum símans"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aðgang að þessum upplýsingum úr símanum þínum"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Þjónustur á milli tækja"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> sendir beiðni um aðgang að myndum, margmiðlunarefni og tilkynningum símans þíns fyrir hönd <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aðgang að þessum upplýsingum úr símanum þínum"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Myndir og efni"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Þjónusta Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> sendir beiðni um heimild fyrir straumspilun forrita á milli tækjanna þinna fyrir hönd <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Leyfa"</string>
diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml
index 0f7fb08c9da3..5ea7c0a43246 100644
--- a/packages/CompanionDeviceManager/res/values-it/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Trasmetti in streaming le app del tuo telefono"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Consenti a &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di accedere a queste informazioni dal tuo telefono"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Servizi cross-device"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede per conto del tuo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> l\'autorizzazione ad accedere a foto, contenuti multimediali e notifiche del telefono"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Consenti a &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di accedere a questa informazione dal tuo telefono"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Foto e contenuti multimediali"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play Services"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede per conto del tuo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> l\'autorizzazione a trasmettere app in streaming tra i dispositivi"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Consenti"</string>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index 9622ce54aa6a..d488e2865063 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"שידור אפליקציות מהטלפון"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"‏מתן אישור לאפליקציה &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לגשת למידע הזה מהטלפון שלך"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"שירותים למספר מכשירים"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה עבור ה‑<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> כדי לגשת לתמונות, למדיה ולהתראות בטלפון שלך"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה עבור מכשיר <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> כדי לשדר אפליקציות בין המכשירים שלך"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"‏מתן אישור לאפליקציה &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לגשת למידע הזה מהטלפון שלך"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"תמונות ומדיה"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play Services"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה עבור ה‑<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> כדי לשדר אפליקציות בין המכשירים שלך"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה עבור מכשיר <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> כדי לגשת לתמונות, למדיה ולהתראות בטלפון שלך"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"יש אישור"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
index 5b282f0a0967..041a5a4e1968 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"スマートフォンのアプリのストリーミング"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"スマートフォンのこの情報へのアクセスを &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; に許可"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"クロスデバイス サービス"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> が <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> に代わってスマートフォンの写真、メディア、通知にアクセスする権限をリクエストしています"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"このスマートフォンからの情報へのアクセスを &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; に許可"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"写真とメディア"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play 開発者サービス"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> が <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> に代わってデバイス間でアプリをストリーミングする権限をリクエストしています"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"デバイス"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"許可"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml
index 94c33e5df268..6fa113fcb21e 100644
--- a/packages/CompanionDeviceManager/res/values-ka/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"თქვენი ტელეფონის აპების სტრიმინგი"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"ნება დართეთ, რომ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; აპს ჰქონდეს ამ ინფორმაციაზე წვდომა თქვენი ტელეფონიდან"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"მოწყობილობათშორისი სერვისები"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> ითხოვს უფლებას თქვენი <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-ის სახელით, რომ წვდომა ჰქონდეს თქვენი ტელეფონის ფოტოებზე, მედიასა და შეტყობინებებზე"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"ნება დართეთ, რომ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; აპს ჰქონდეს ამ ინფორმაციაზე წვდომა თქვენი ტელეფონიდან"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"ფოტოები და მედია"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> ითხოვს უფლებას თქვენი <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-ის სახელით, რომ მოწყობილობებს შორის სტრიმინგი შეძლოს"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"მოწყობილობა"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"დაშვება"</string>
diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml
index 5a4314bf9979..0625210ed3cc 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Телефон қолданбаларын трансляциялайды."</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына телефоныңыздағы осы ақпаратты пайдалануға рұқсат беріңіз."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Аралық құрылғы қызметтері"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> атынан телефондағы фотосуреттерді, медиафайлдар мен хабарландыруларды пайдалану үшін рұқсат сұрайды."</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына телефоныңыздағы осы ақпаратты пайдалануға рұқсат беріңіз."</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Фотосуреттер мен медиафайлдар"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play қызметтері"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> атынан құрылғылар арасында қолданбалар трансляциялау үшін рұқсат сұрайды."</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Рұқсат беру"</string>
diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml
index 2456f63f1b34..854fcac990ec 100644
--- a/packages/CompanionDeviceManager/res/values-km/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"ផ្សាយកម្មវិធីរបស់ទូរសព្ទអ្នក"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ចូលប្រើព័ត៌មាននេះពីទូរសព្ទរបស់អ្នក"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"សេវាកម្មឆ្លងកាត់ឧបករណ៍"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> របស់អ្នក ដើម្បីចូលប្រើរូបថត មេឌៀ និងការជូនដំណឹងរបស់ទូរសព្ទអ្នក"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ចូលមើលព័ត៌មាននេះពីទូរសព្ទរបស់អ្នក"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"រូបថត និងមេឌៀ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"សេវាកម្ម Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> របស់អ្នក ដើម្បីបញ្ចាំងកម្មវិធីរវាងឧបករណ៍របស់អ្នក"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"អនុញ្ញាត"</string>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index 5284ebf93c6c..809a811e1ba4 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"ನಿಮ್ಮ ಫೋನ್‍ನ ಆ್ಯಪ್‌ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಿ"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"ನಿಮ್ಮ ಫೋನ್ ಮೂಲಕ ಈ ಮಾಹಿತಿಯನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಗೆ ಅನುಮತಿಸಿ"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"ಕ್ರಾಸ್-ಡಿವೈಸ್ ಸೇವೆಗಳು"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"ನಿಮ್ಮ ಫೋನ್‌ನ ಫೋಟೋಗಳು, ಮೀಡಿಯಾ ಮತ್ತು ಅಧಿಸೂಚನೆಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ನ ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸಿಕೊಳ್ಳುತ್ತಿದೆ"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"ನಿಮ್ಮ ಫೋನ್ ಮೂಲಕ ಈ ಮಾಹಿತಿಯನ್ನು ಪ್ರವೇಶಿಸಲು &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಗೆ ಅನುಮತಿಸಿ"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"ಫೋಟೋಗಳು ಮತ್ತು ಮಾಧ್ಯಮ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play ಸೇವೆಗಳು"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"ನಿಮ್ಮ ಸಾಧನಗಳ ನಡುವೆ ಆ್ಯಪ್‌ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ನ ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸಿಕೊಳ್ಳುತ್ತಿದೆ"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ಅನುಮತಿಸಿ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
index 4451cb93456c..79cd1b6f6330 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"휴대전화의 앱을 스트리밍합니다."</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 앱이 휴대전화에서 이 정보에 액세스하도록 허용합니다."</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"교차 기기 서비스"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> 대신 휴대전화의 사진, 미디어, 알림에 액세스할 수 있는 권한을 요청하고 있습니다."</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 앱이 휴대전화에서 이 정보에 액세스하도록 허용합니다."</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"사진 및 미디어"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play 서비스"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> 대신 기기 간에 앱을 스트리밍할 수 있는 권한을 요청하고 있습니다."</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"기기"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"허용"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index d641f299d652..634804392f42 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Телефондогу колдонмолорду алып ойнотуу"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна телефонуңуздагы ушул маалыматты көрүүгө уруксат бериңиз"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Түзмөктөр аралык кызматтар"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздүн атынан телефондогу сүрөттөрдү, медиа файлдарды жана билдирмелерди колдонууга уруксат сурап жатат"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна телефонуңуздагы ушул маалыматты көрүүгө уруксат бериңиз"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Сүрөттөр жана медиа"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play кызматтары"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздүн атынан түзмөктөрүңүздүн ортосунда колдонмолорду тышкы экранга чыгарууга уруксат сурап жатат"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Ооба"</string>
diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml
index f9d65faee9a2..47fbadd1dba7 100644
--- a/packages/CompanionDeviceManager/res/values-lo/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"ສະຕຣີມແອັບຂອງໂທລະສັບທ່ານ"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"ອະນຸຍາດ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ໃຫ້ເຂົ້າເຖິງຂໍ້ມູນນີ້ຈາກໂທລະສັບຂອງທ່ານໄດ້"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"ບໍລິການຂ້າມອຸປະກອນ"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> ກຳລັງຮ້ອງຂໍການອະນຸຍາດໃນນາມຂອງ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ເພື່ອເຂົ້າເຖິງຮູບພາບ, ມີເດຍ ແລະ ການແຈ້ງເຕືອນຂອງໂທລະສັບທ່ານ"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"ອະນຸຍາດ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ໃຫ້ເຂົ້າເຖິງຂໍ້ມູນນີ້ຈາກໂທລະສັບຂອງທ່ານໄດ້"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"ຮູບພາບ ແລະ ມີເດຍ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"ບໍລິການ Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> ກຳລັງຮ້ອງຂໍການອະນຸຍາດໃນນາມຂອງ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ເພື່ອສະຕຣີມແອັບລະຫວ່າງອຸປະກອນຂອງທ່ານ"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"ອຸປະກອນ"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ອະນຸຍາດ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml
index 6e7b00786221..cc377793a3e3 100644
--- a/packages/CompanionDeviceManager/res/values-lt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Telefono programų perdavimas srautu"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Leisti &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pasiekti šią informaciją iš jūsų telefono"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Pasl. keliuose įrenginiuose"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo jūsų „<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>“ vardu, kad galėtų pasiekti telefono nuotraukas, mediją ir pranešimus"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Leisti &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pasiekti šią informaciją iš jūsų telefono"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Nuotraukos ir medija"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"„Google Play“ paslaugos"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo jūsų „<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>“ vardu, kad galėtų srautu perduoti programas iš vieno įrenginio į kitą"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Leisti"</string>
diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml
index 1fdc99d82be8..f1c6f8dbbc24 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Var straumēt jūsu tālruņa lietotnes"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Atļaut lietotnei &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; piekļūt šai informācijai no jūsu tālruņa"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Vairāku ierīču pakalpojumi"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju piekļūt jūsu tālruņa fotoattēliem, multivides saturam un paziņojumiem šīs ierīces vārdā: <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Atļaut lietotnei &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; piekļūt šai informācijai no jūsu tālruņa"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotoattēli un multivides faili"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play pakalpojumi"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju straumēt lietotnes starp jūsu ierīcēm šīs ierīces vārdā: <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Atļaut"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml
index e09a5b395efa..46fcc02e0c8c 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Стримувајте ги апликациите на телефонот"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Овозможете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да пристапува до овие податоци на телефонот"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Повеќенаменски услуги"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на вашиот <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за да пристапува до фотографиите, аудиовизуелните содржини и известувањата на телефонот"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Овозможете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да пристапува до овие податоци на телефонот"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Аудиовизуелни содржини"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Услуги на Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на вашиот <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за да стримува апликации помеѓу вашите уреди"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"уред"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml
index 636a7507940e..27682d4c1af1 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"നിങ്ങളുടെ ഫോണിലെ ആപ്പുകൾ സ്‌ട്രീം ചെയ്യാൻ"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"നിങ്ങളുടെ ഫോണിൽ നിന്ന് ഈ വിവരങ്ങൾ ആക്‌സസ് ചെയ്യാൻ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ആപ്പിനെ അനുവദിക്കുക"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"ക്രോസ്-ഉപകരണ സേവനങ്ങൾ"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"നിങ്ങളുടെ ഫോണിലെ ഫോട്ടോകൾ, മീഡിയ, അറിയിപ്പുകൾ എന്നിവ ആക്സസ് ചെയ്യാൻ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ഉപകരണത്തിനു വേണ്ടി <xliff:g id="APP_NAME">%1$s</xliff:g> അനുമതി അഭ്യർത്ഥിക്കുന്നു."</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"നിങ്ങളുടെ ഫോണിൽ നിന്ന് ഈ വിവരങ്ങൾ ആക്‌സസ് ചെയ്യാൻ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ആപ്പിനെ അനുവദിക്കുക"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"ഫോട്ടോകളും മീഡിയയും"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play സേവനങ്ങൾ"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"നിങ്ങളുടെ ഉപകരണങ്ങളിൽ ഒന്നിൽ നിന്ന് അടുത്തതിലേക്ക് ആപ്പുകൾ സ്ട്രീം ചെയ്യാൻ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ഉപകരണത്തിനു വേണ്ടി <xliff:g id="APP_NAME">%1$s</xliff:g> അനുമതി അഭ്യർത്ഥിക്കുന്നു."</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"അനുവദിക്കുക"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
index 64b284dcb6cb..84a986fba631 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Таны утасны аппуудыг дамжуулах"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-д таны утаснаас энэ мэдээлэлд хандахыг зөвшөөрнө үү"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Төхөөрөмж хоорондын үйлчилгээ"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> нь таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н өмнөөс утасны зураг, медиа болон мэдэгдэлд хандахын тулд зөвшөөрөл хүсэж байна"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"Таны төхөөрөмжүүд хооронд апп дамжуулахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н өмнөөс зөвшөөрөл хүсэж байна"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-д таны утаснаас энэ мэдээлэлд хандахыг зөвшөөрнө үү"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Зураг болон медиа"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play үйлчилгээ"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> нь таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н өмнөөс төхөөрөмж хооронд апп дамжуулахын тулд зөвшөөрөл хүсэж байна"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"Таны утасны зураг, медиа болон мэдэгдэлд хандахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н өмнөөс зөвшөөрөл хүсэж байна"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Зөвшөөрөх"</string>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index a070e6199469..24e92e56e316 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"फोनवरील ॲप्स स्ट्रीम करा"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला ही माहिती तुमच्या फोनवरून अ‍ॅक्सेस करण्यासाठी अनुमती द्या"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"क्रॉस-डिव्हाइस सेवा"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"तुमच्या फोनमधील फोटो, मीडिया आणि सूचना ॲक्सेस करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमच्या <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करत आहे"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला ही माहिती तुमच्या फोनवरून अ‍ॅक्सेस करण्यासाठी अनुमती द्या"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"फोटो आणि मीडिया"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play सेवा"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"तुमच्या डिव्हाइसदरम्यान ॲप्स स्ट्रीम करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमच्या <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करत आहे"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"अनुमती द्या"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index e6932e13beaa..037d4de3f253 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Strim apl telefon anda"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; mengakses maklumat ini daripada telefon anda"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Perkhidmatan silang peranti"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang meminta kebenaran bagi pihak <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> anda untuk mengakses foto, media dan pemberitahuan telefon anda"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengakses maklumat ini daripada telefon anda"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Foto dan media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Perkhidmatan Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang meminta kebenaran bagi pihak <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> anda untuk menstrim apl antara peranti anda"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Benarkan"</string>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
index 778f7be59870..1362ddfeee82 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"သင့်ဖုန်းရှိအက်ပ်များကို တိုက်ရိုက်လွှင့်နိုင်သည်"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို သင့်ဖုန်းမှ ဤအချက်အလက် သုံးခွင့်ပြုမည်"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"စက်များကြားသုံး ဝန်ဆောင်မှုများ"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင့်ဖုန်း၏ ဓာတ်ပုံ၊ မီဒီယာနှင့် အကြောင်းကြားချက်များသုံးရန် သင်၏ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင်၏စက်များအကြား အက်ပ်များတိုက်ရိုက်လွှင့်ရန် <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; အား သင့်ဖုန်းမှ ဤအချက်အလက် သုံးခွင့်ပြုခြင်း"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"ဓာတ်ပုံနှင့် မီဒီယာများ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play ဝန်ဆောင်မှုများ"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင်၏စက်များအကြား အက်ပ်များတိုက်ရိုက်လွှင့်ရန် သင်၏ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင့်ဖုန်း၏ ဓာတ်ပုံ၊ မီဒီယာနှင့် အကြောင်းကြားချက်များသုံးရန် <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ခွင့်ပြုရန်"</string>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index 274b1a9b5e8c..1b269254e638 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Strøm appene på telefonen"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Gi &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tilgang til denne informasjonen fra telefonen din"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Tjenester på flere enheter"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse på vegne av <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> for å få tilgang til bilder, medier og varsler på telefonen din"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Gi &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tilgang til denne informasjonen fra telefonen din"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Bilder og medier"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play-tjenester"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse på vegne av <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> for å strømme apper mellom enhetene dine"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Tillat"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml
index ac3338e2c211..95028eb14d79 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"आफ्नो फोनका एपहरू प्रयोग गर्नुहोस्"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; लाई तपाईंको फोनमा भएको यो जानकारी हेर्ने तथा प्रयोग गर्ने अनुमति दिनुहोस्"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"क्रस-डिभाइस सेवाहरू"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> तपाईंको डिभाइस <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> को तर्फबाट तपाईंको फोनमा भएका फोटो, मिडिया र सूचनाहरू हेर्ने तथा प्रयोग गर्ने अनुमति माग्दै छ"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; लाई तपाईंको फोनमा भएको यो जानकारी हेर्ने तथा प्रयोग गर्ने अनुमति दिनुहोस्"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"फोटो र मिडिया"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> तपाईंको डिभाइस <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> को तर्फबाट तपाईंका कुनै एउटा डिभाइसबाट अर्को डिभाइसमा एप स्ट्रिम गर्ने अनुमति माग्दै छ"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"यन्त्र"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"अनुमति दिनुहोस्"</string>
diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml
index 3eb49c556a29..31b590357d1b 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"De apps van je telefoon streamen"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toegang geven tot deze informatie op je telefoon"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device-services"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toegang tot de foto\'s, media en meldingen van je telefoon"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toestemming om apps te streamen tussen je apparaten"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toegang geven tot deze informatie op je telefoon"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Foto\'s en media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play-services"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toestemming om apps te streamen tussen je apparaten"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toegang tot de foto\'s, media en meldingen van je telefoon"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"apparaat"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Toestaan"</string>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index b9c543436e7e..1d63ec381fc4 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"ଆପଣଙ୍କ ଫୋନର ଆପ୍ସକୁ ଷ୍ଟ୍ରିମ କରନ୍ତୁ"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"ଆପଣଙ୍କ ଫୋନରୁ ଏହି ସୂଚନାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"କ୍ରସ-ଡିଭାଇସ ସେବାଗୁଡ଼ିକ"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"ଆପଣଙ୍କ ଫୋନର ଫଟୋ, ମିଡିଆ ଏବଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ପାଇଁ ଅନୁରୋଧ କରୁଛି"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"ଆପଣଙ୍କ ଫୋନରୁ ଏହି ସୂଚନାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"ଫଟୋ ଏବଂ ମିଡିଆ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play ସେବାଗୁଡ଼ିକ"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"ଆପଣଙ୍କ ଡିଭାଇସଗୁଡ଼ିକ ମଧ୍ୟରେ ଆପ୍ସକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ପାଇଁ ଅନୁରୋଧ କରୁଛି"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index 3d35e55ed68c..07989f715a84 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"ਆਪਣੇ ਫ਼ੋਨ ਦੀਆਂ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰੋ"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਤੋਂ ਇਸ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"ਕ੍ਰਾਸ-ਡੀਵਾਈਸ ਸੇਵਾਵਾਂ"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ ਫ਼ੋਨ ਦੀਆਂ ਫ਼ੋਟੋਆਂ, ਮੀਡੀਆ ਅਤੇ ਸੂਚਨਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਤੋਂ ਇਸ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"ਫ਼ੋਟੋਆਂ ਅਤੇ ਮੀਡੀਆ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play ਸੇਵਾਵਾਂ"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸਾਂ ਵਿਚਕਾਰ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ਆਗਿਆ ਦਿਓ"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index 08f6880afa9c..3ccbed7b3175 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Odtwarzaj strumieniowo aplikacje z telefonu"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Zezwól aplikacji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na dostęp do tych informacji na Twoim telefonie"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Usługi na innym urządzeniu"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> o uprawnienia dotyczące dostępu do zdjęć, multimediów i powiadomień na telefonie"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Zezwól aplikacji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na dostęp do tych informacji na Twoim telefonie"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Zdjęcia i multimedia"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Usługi Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> o uprawnienia dotyczące strumieniowego odtwarzania aplikacji między urządzeniami"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
index ce83bff93d0d..5c426048c824 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Faça streaming dos apps do seu smartphone"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acesse estas informações do smartphone"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para acessar fotos, mídia e notificações do smartphone."</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para fazer streaming de apps entre seus dispositivos"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Autorizar que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acesse estas informações do smartphone"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotos e mídia"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play Services"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para fazer streaming de apps entre seus dispositivos"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para acessar fotos, mídia e notificações do smartphone."</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
index 8d84eb76dded..1ed65bd88d11 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Faça stream das apps do telemóvel"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Permita que a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aceda a estas informações do seu telemóvel"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para aceder às fotos, conteúdo multimédia e notificações do seu telemóvel"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para fazer stream de apps entre os seus dispositivos"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Permita que a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aceda a estas informações do seu telemóvel"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotos e multimédia"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Serviços do Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para fazer stream de apps entre os seus dispositivos"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para aceder às fotos, ao conteúdo multimédia e às notificações do seu telemóvel"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index ce83bff93d0d..5c426048c824 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Faça streaming dos apps do seu smartphone"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acesse estas informações do smartphone"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para acessar fotos, mídia e notificações do smartphone."</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para fazer streaming de apps entre seus dispositivos"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Autorizar que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acesse estas informações do smartphone"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotos e mídia"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play Services"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para fazer streaming de apps entre seus dispositivos"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para acessar fotos, mídia e notificações do smartphone."</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index b6f8ff7a89b7..276ebfdda3c7 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Să redea în stream aplicațiile telefonului"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Permiteți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să acceseze aceste informații de pe telefon"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicii pe mai multe dispozitive"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a accesa fotografiile, conținutul media și notificările de pe telefon"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a reda în stream aplicații între dispozitivele dvs."</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Permiteți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să acceseze aceste informații de pe telefon"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotografii și media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Servicii Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a reda în stream aplicații între dispozitivele dvs."</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a accesa fotografiile, conținutul media și notificările de pe telefon"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Permiteți"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
index 492e345b93fd..d5031bd2d5f3 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Трансляция приложений с телефона."</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; получать эту информацию с вашего телефона"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Сервисы стриминга приложений"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запрашивает разрешение от имени вашего устройства <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, чтобы получить доступ к фотографиям, медиаконтенту и уведомлениям на телефоне."</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; получать эту информацию с вашего телефона"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Фотографии и медиафайлы"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Сервисы Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запрашивает доступ от имени вашего устройства <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, чтобы транслировать приложения между вашими устройствами."</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Разрешить"</string>
diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml
index 79c36513a575..b2451e9ef41b 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"ඔබගේ දුරකථනයේ යෙදුම් ප්‍රවාහ කරන්න"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඔබගේ දුරකථනයෙන් මෙම තොරතුරුවලට ප්‍රවේශ වීමට ඉඩ දෙන්න"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"හරස්-උපාංග සේවා"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබගේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> වෙනුවෙන් ඔබගේ දුරකථනයෙහි ඡායාරූප, මාධ්‍ය සහ දැනුම්දීම් වෙත ප්‍රවේශ වීමට අවසරය ඉල්ලමින් සිටී"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබගේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> වෙනුවෙන් ඔබගේ උපාංග අතර යෙදුම් ප්‍රවාහ කිරීමට අවසරය ඉල්ලමින් සිටියි"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඔබගේ දුරකථනයෙන් මෙම තොරතුරුවලට ප්‍රවේශ වීමට ඉඩ දෙන්න"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"ඡායාරූප සහ මාධ්‍ය"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play සේවා"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබගේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> වෙනුවෙන් ඔබගේ උපාංග අතර යෙදුම් ප්‍රවාහ කිරීමට අවසරය ඉල්ලමින් සිටී"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබගේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> වෙනුවෙන් ඔබගේ දුරකථනයෙහි ඡායාරූප, මාධ්‍ය සහ දැනුම්දීම් වෙත ප්‍රවේශ වීමට අවසරය ඉල්ලමින් සිටියි"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"ඉඩ දෙන්න"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index 54b6ce6d66ad..787c1854d3e8 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Streamovanie aplikácií v telefóne"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Povoľte aplikácii &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; prístup k týmto informáciám z vášho telefónu"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Služby pre viacero zariadení"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje povolenie na prístup k fotkám, médiám a upozorneniam vášho telefónu v mene tohto zariadenia (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje povolenie na streamovanie aplikácií medzi vašimi zariadeniami v mene tohto zariadenia (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Povoľte aplikácii &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; prístup k týmto informáciám z vášho telefónu"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotky a médiá"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Služby Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje povolenie na streamovanie aplikácií medzi vašimi zariadeniami v mene tohto zariadenia (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje povolenie na prístup k fotkám, médiám a upozorneniam vášho telefónu v mene tohto zariadenia (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Povoliť"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml
index 883bd0bcdb95..42453bab1d22 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Pretočno predvajanje aplikacij telefona"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Dovolite, da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dostopa do teh podatkov v vašem telefonu"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Storitve za zunanje naprave"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>« zahteva dovoljenje za dostop do fotografij, predstavnosti in obvestil v telefonu."</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Dovolite, da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dostopa do teh podatkov v vašem telefonu"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotografije in predstavnost"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Storitve Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>« zahteva dovoljenje za pretočno predvajanje aplikacij v vaših napravah."</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"naprava"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Dovoli"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index 3e6933ccb8db..2550f71fd665 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Transmeto aplikacionet e telefonit tënd"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Lejo që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të ketë qasje në këtë informacion nga telefoni yt"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Shërbimet mes pajisjeve"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" po kërkon leje në emër të <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> për të marrë qasje te fotografitë, media dhe njoftimet e telefonit tënd"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Lejo që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të ketë qasje në këtë informacion nga telefoni yt"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotografitë dhe media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Shërbimet e Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> po kërkon leje në emër të <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> për të transmetuar aplikacione ndërmjet pajisjeve të tua"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Lejo"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
index f9a1e0248aaa..354ff2c2ac13 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Стримујте апликације на телефону"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Дозволите да &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; приступа овим информацијама са телефона"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Услуге на више уређаја"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> захтева дозволу у име уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за приступ сликама, медијском садржају и обавештењима са телефона"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> захтева дозволу у име уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за стримовање апликација између уређаја"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Дозволите да &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; приступа овим информацијама са телефона"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Слике и медији"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play услуге"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> захтева дозволу у име уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за стримовање апликација између уређаја"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> захтева дозволу у име уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за приступ сликама, медијском садржају и обавештењима са телефона"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml
index b4df61613e9c..99df831a8942 100644
--- a/packages/CompanionDeviceManager/res/values-sv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Streama telefonens appar"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Ge &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; åtkomstbehörighet till denna information på telefonen"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Tjänster för flera enheter"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> begär behörighet att ge <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> åtkomst till foton, mediefiler och aviseringar på telefonen"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Ge &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; åtkomstbehörighet till denna information på telefonen"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Foton och media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play-tjänster"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> begär behörighet att låta <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> streama appar mellan enheter"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Tillåt"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index 6b5ca21a69de..2618de765c2f 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Tiririsha programu za simu yako"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Ruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ifikie maelezo haya kutoka kwenye simu yako"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Huduma za kifaa kilichounganishwa kwingine"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili kufikia picha, maudhui na arifa za simu yako"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Ruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ifikie maelezo haya kutoka kwenye simu yako"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Picha na maudhui"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Huduma za Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili kutiririsha programu kati ya vifaa vyako"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml
index 4408e65d8e81..8a32deea6a6b 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"உங்கள் மொபைலின் ஆப்ஸை ஸ்ட்ரீம் செய்யலாம்"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"மொபைலில் உள்ள இந்தத் தகவல்களை அணுக, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதிக்கவும்"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"பன்முக சாதன சேவைகள்"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"உங்கள் மொபைலில் உள்ள படங்கள், மீடியா, அறிவிப்புகள் ஆகியவற்றை அணுக உங்கள் <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> அனுமதி கோருகிறது"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"உங்கள் மொபைலிலிருந்து இந்தத் தகவலை அணுக &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதியுங்கள்"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"படங்கள் மற்றும் மீடியா"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play சேவைகள்"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"உங்கள் சாதனங்களுக்கு இடையே ஆப்ஸை ஸ்ட்ரீம் செய்ய உங்கள் <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> அனுமதி கோருகிறது"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"அனுமதி"</string>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index d0fa24d3e665..446d91b9b020 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"మీ ఫోన్ యాప్‌లను స్ట్రీమ్ చేయండి"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"మీ ఫోన్ నుండి ఈ సమాచారాన్ని యాక్సెస్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; యాప్‌ను అనుమతించండి"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> మీ ఫోన్ ఫోటోలు, మీడియా, నోటిఫికేషన్‌లను యాక్సెస్ చేయడానికి మీ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> తరపున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"మీ ఫోన్ నుండి ఈ సమాచారాన్ని యాక్సెస్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; యాప్‌ను అనుమతించండి"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"ఫోటోలు, మీడియా"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play సర్వీసులు"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"మీ పరికరాల మధ్య యాప్‌లను స్ట్రీమ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> మీ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> తరపున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"అనుమతించండి"</string>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
index e3174f8c9012..a75888269d53 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"สตรีมแอปของโทรศัพท์คุณ"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; เข้าถึงข้อมูลนี้จากโทรศัพท์ของคุณ"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"บริการหลายอุปกรณ์"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> เพื่อเข้าถึงรูปภาพ สื่อ และการแจ้งเตือนในโทรศัพท์ของคุณ"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; เข้าถึงข้อมูลนี้จากโทรศัพท์ของคุณ"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"รูปภาพและสื่อ"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"บริการ Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> เพื่อสตรีมแอประหว่างอุปกรณ์ต่างๆ ของคุณ"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"อนุญาต"</string>
diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml
index 9c49c5c78df7..d7e9ab69d9f8 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"I-stream ang mga app ng iyong telepono"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na i-access ang impormasyong ito sa iyong telepono"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Mga cross-device na serbisyo"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay humihiling ng pahintulot sa ngalan ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para i-access ang mga larawan, media, at notification ng telepono mo"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay humihiling ng pahintulot sa ngalan ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para mag-stream ng mga app sa pagitan ng mga device mo"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na i-access ang impormasyon sa iyong telepono"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Mga larawan at media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Mga serbisyo ng Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay humihiling ng pahintulot sa ngalan ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para mag-stream ng mga app sa pagitan ng mga device mo"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay humihiling ng pahintulot sa ngalan ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para i-access ang mga larawan, media, at notification ng telepono mo"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Payagan"</string>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
index bdad64149415..826d48657e4e 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Telefonunuzun uygulamalarını yayınlama"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasının, telefonunuzdaki bu bilgilere erişmesine izin verin"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Cihazlar arası hizmetler"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g>, telefonunuzdaki fotoğraf, medya ve bildirimlere erişmek için <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazınız adına izin istiyor"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasının, telefonunuzdaki bu bilgilere erişmesine izin verin"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Fotoğraflar ve medya"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play hizmetleri"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g>, cihazlarınız arasında uygulama akışı gerçekleştirmek için <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazınız adına izin istiyor"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"İzin ver"</string>
diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml
index 760255b4b64a..89cd2c33a609 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Транслювати додатки телефона"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Надайте додатку &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; доступ до цієї інформації з телефона"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Сервіси для кількох пристроїв"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені вашого пристрою <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> запитує дозвіл на доступ до фотографій, медіафайлів і сповіщень вашого телефона"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Надайте пристрою &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; доступ до цієї інформації з телефона"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Фотографії та медіафайли"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Сервіси Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені вашого пристрою <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> запитує дозвіл на трансляцію додатків між вашими пристроями"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"пристрій"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Дозволити"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index a311bd4936a8..7f183b8894bd 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"اپنے فون کی ایپس کی سلسلہ بندی کریں"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"‏‎&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;‎ کو اپنے فون سے ان معلومات تک رسائی حاصل کرنے کی اجازت دیں"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"کراس ڈیوائس سروسز"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> کی جانب سے آپ کے فون کی تصاویر، میڈیا اور اطلاعات تک رسائی کی اجازت طلب کر رہی ہے"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"‏اپنے فون سے اس معلومات تک رسائی حاصل Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کرنے کی اجازت دیں"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"تصاویر اور میڈیا"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"‏Google Play سروسز"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> کی جانب سے آپ کے آلات کے درمیان ایپس کی سلسلہ بندی کرنے کی اجازت کی درخواست کر رہی ہے"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"اجازت دیں"</string>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
index f1c162afa666..17426067fabf 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Telefondagi ilovalarni translatsiya qilish"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasiga telefondagi ushbu maʼlumot uchun ruxsat bering"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Qurilmalararo xizmatlar"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Telefoningizdagi rasm, media va bildirishnomalarga kirish uchun <xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> nomidan ruxsat soʻramoqda"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasiga telefondagi ushbu maʼlumot uchun ruxsat bering"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Suratlar va media"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play xizmatlari"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"Qurilamalararo ilovalar strimingi uchun <xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> nomidan ruxsat soʻramoqda"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Ruxsat"</string>
diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml
index 5c7d600a83e6..43066140eeb8 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Truyền các ứng dụng trên điện thoại của bạn"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Cho phép &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; truy cập vào thông tin này trên điện thoại của bạn"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Dịch vụ trên nhiều thiết bị"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> để truy cập vào ảnh, nội dung nghe nhìn và thông báo trên điện thoại của bạn."</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> để truyền trực tuyến ứng dụng giữa các thiết bị của bạn"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Cho phép &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; truy cập vào thông tin này trên điện thoại của bạn"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Ảnh và nội dung nghe nhìn"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Dịch vụ Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> để truyền trực tuyến ứng dụng giữa các thiết bị của bạn"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> để truy cập vào ảnh, nội dung nghe nhìn và thông báo trên điện thoại của bạn."</string>
<string name="profile_name_generic" msgid="6851028682723034988">"thiết bị"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Cho phép"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
index 5a1017f72668..2df8b4ff3d43 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"流式传输手机上的应用"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”&lt;strong&gt;&lt;/strong&gt;访问您手机中的这项信息"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"跨设备服务"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>请求访问您手机上的照片、媒体内容和通知"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"允许 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 访问您手机中的这项信息"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"照片和媒体内容"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play 服务"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>请求在您的设备之间流式传输应用内容"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"设备"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"允许"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index 4748ece86a5a..7c2541b2a211 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"串流播放手機應用程式內容"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;存取您手機中的這項資料"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"跨裝置服務"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> 要求必要權限,以便存取手機上的相片、媒體和通知"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;存取您手機中的這項資料"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"相片和媒體"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play 服務"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> 要求必要權限,以便在裝置之間串流應用程式內容"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"允許"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
index f41896fd602f..9dd391900a8b 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"串流傳輸手機應用程式內容"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;存取手機中的這項資訊"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"跨裝置服務"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表你的「<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>」要求必要權限,以便存取手機上的相片、媒體和通知"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;存取你手機中的這項資訊"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"相片和媒體"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play 服務"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表你的「<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>」要求必要權限,以便在裝置之間串流傳輸應用程式內容"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"允許"</string>
diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml
index 87ed7e6e8adf..5fed63928a59 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -25,7 +25,8 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Sakaza ama-app wefoni yakho"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Vumela i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ifinyelele lolu lwazi kusukela efonini yakho"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Amasevisi amadivayisi amaningi"</string>
- <string name="helper_summary_app_streaming" msgid="7380294597268573523">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yakho ukuze ifinyelele izithombe zefoni yakho, imidiya nezaziso"</string>
+ <!-- no translation found for helper_summary_app_streaming (5977509499890099) -->
+ <skip />
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Vumela &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukufinyelela lolu lwazi kusuka efonini yakho"</string>
@@ -35,7 +36,8 @@
<string name="permission_storage" msgid="6831099350839392343">"Izithombe nemidiya"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Amasevisi we-Google Play"</string>
- <string name="helper_summary_computer" msgid="1676407599909474428">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yakho ukuze isakaze-bukhoma ama-app phakathi kwamadivayisi akho"</string>
+ <!-- no translation found for helper_summary_computer (9050724687678157852) -->
+ <skip />
<string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Vumela"</string>
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index a389bfc42725..cb85ae4fe66b 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -48,7 +48,7 @@
<string name="helper_title_app_streaming">Cross-device services</string>
<!-- Description of the helper dialog for APP_STREAMING profile. [CHAR LIMIT=NONE] -->
- <string name="helper_summary_app_streaming"><xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_type" example="Chromebook">%2$s</xliff:g> to access your phone\u2019s photos, media, and notifications</string>
+ <string name="helper_summary_app_streaming"><xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_type" example="Chromebook">%2$s</xliff:g> to stream apps between your devices</string>
<!-- ================= DEVICE_PROFILE_AUTOMOTIVE_PROJECTION ================= -->
@@ -82,7 +82,7 @@
<string name="helper_title_computer">Google Play services</string>
<!-- Description of the helper dialog for COMPUTER profile. [CHAR LIMIT=NONE] -->
- <string name="helper_summary_computer"> <xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_type" example="Chromebook">%2$s</xliff:g> to stream apps between your devices</string>
+ <string name="helper_summary_computer"><xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_type" example="Chromebook">%2$s</xliff:g> to access your phone\u2019s photos, media, and notifications</string>
<!-- ================= null profile ================= -->
diff --git a/packages/PackageInstaller/res/values-hi/strings.xml b/packages/PackageInstaller/res/values-hi/strings.xml
index c6a1f401d946..614fa9072706 100644
--- a/packages/PackageInstaller/res/values-hi/strings.xml
+++ b/packages/PackageInstaller/res/values-hi/strings.xml
@@ -74,7 +74,7 @@
<string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"आपकी प्रोफ़ाइल के लिए यह ऐप्लिकेशन ज़रूरी है और उसे अनइंस्टॉल नहीं किया जा सकता."</string>
<string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"आपके डिवाइस एडमिन के लिए यह ऐप्लिकेशन ज़रूरी है और इसे अनइंस्टॉल नहीं किया जा सकता."</string>
<string name="manage_device_administrators" msgid="3092696419363842816">"डिवाइस एडमिन ऐप्लिकेशन प्रबंधित करें"</string>
- <string name="manage_users" msgid="1243995386982560813">"उपयोगकर्ताओं को मैनेज करें"</string>
+ <string name="manage_users" msgid="1243995386982560813">"उपयोगकर्ताओं को प्रबंधित करें"</string>
<string name="uninstall_failed_msg" msgid="2176744834786696012">"<xliff:g id="APP_NAME">%1$s</xliff:g> को अनइंस्‍टॉल नहीं किया जा सका."</string>
<string name="Parse_error_dlg_text" msgid="1661404001063076789">"पैकेज को पार्स करने में कोई समस्‍या थी."</string>
<string name="wear_not_allowed_dlg_title" msgid="8664785993465117517">"Android Wear"</string>
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 9a80b0267976..87e61b527477 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -50,6 +50,7 @@ android_library {
"SettingsLibTwoTargetPreference",
"SettingsLibSettingsTransition",
"SettingsLibButtonPreference",
+ "SettingsLibDeviceStateRotationLock",
"setupdesign",
"zxing-core-1.7",
],
diff --git a/packages/SettingsLib/DeviceStateRotationLock/Android.bp b/packages/SettingsLib/DeviceStateRotationLock/Android.bp
new file mode 100644
index 000000000000..c642bd14ed79
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/Android.bp
@@ -0,0 +1,16 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SettingsLibDeviceStateRotationLock",
+
+ srcs: ["src/**/*.java"],
+
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/AndroidManifest.xml b/packages/SettingsLib/DeviceStateRotationLock/AndroidManifest.xml
new file mode 100644
index 000000000000..bce6599721e3
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.devicestate">
+
+</manifest>
diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/AndroidSecureSettings.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java
index 8aee576c3d04..8aee576c3d04 100644
--- a/packages/SettingsLib/src/com/android/settingslib/devicestate/AndroidSecureSettings.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java
diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
index 4ed7e19f341d..4ed7e19f341d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/SecureSettings.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
index 10528739b2b0..10528739b2b0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/devicestate/SecureSettings.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java
diff --git a/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml
index 50f69d1d692a..c629d96bcf4b 100644
--- a/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml
+++ b/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml
@@ -71,6 +71,8 @@
<TextView
android:id="@+id/entity_header_second_summary"
style="@style/TextAppearance.EntityHeaderSummary"
+ android:singleLine="false"
+ android:maxLines="4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 1992c9bb7dbf..0aa32459948d 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laai tans vinnig"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Laai tans stadig"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Laai tans draadloos"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Laaidok"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Laai nie"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Gekoppel, laai nie"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Gelaai"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Stel terug"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Verwyder"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Stel tans gassessie terug …"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Stel gastesessie terug?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Dit sal ’n nuwe gastesessie begin en alle programme en data van die huidige sessie uitvee"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Verlaat gasmodus?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Dit sal programme en data in die huidige gastesessie uitvee"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Gaan uit"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Stoor gasaktiwiteit?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Jy kan aktiwiteit in die huidige sessie stoor of alle programme en data uitvee"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Vee uit"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Stoor"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Verlaat gasmodus"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Stel jou gastesessie terug"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Verlaat gasmodus"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Alle aktiwiteit sal uitgevee word wanneer jy uitgaan"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Jy kan jou aktiwiteit stoor of uitvee wanneer jy uitgaan"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Stel terug om aktiwiteit nou uit te vee, of stoor of vee aktiwiteit uit wanneer jy uitgaan"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Neem \'n foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Kies \'n prent"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Kies foto"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 74844fc79428..a52e71899599 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ኃይል በፍጥነት በመሙላት ላይ"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ኃይል በዝግታ በመሙላት ላይ"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"በገመድ-አልባ ኃይል በመሙላት ላይ"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"የኃይል መሙያ መትከያ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ባትሪ እየሞላ አይደለም"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"ተገናኝቷል፣ ኃይል በመሙላት ላይ አይደለም"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ባትሪ ሞልቷል"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ዳግም አስጀምር"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"አስወግድ"</string>
<string name="guest_resetting" msgid="7822120170191509566">"እንግዳን ዳግም በማስጀመር ላይ…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"የእንግዳ ክፍለ-ጊዜ ዳግም ይጀመር?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ይህ አዲስ የእንግዳ ክፍለ-ጊዜ ይጀምራል እና ሁሉንም መተግበሪያዎች እና ውሂብ አሁን ካለው ክፍለ-ጊዜ ይሰርዛል"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ከእንግዳ ሁኔታ ይውጣ?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ይህ አሁን ካለው የእንግዳ ክፍለ-ጊዜ መተግበሪያዎችን እና ውሂብን ይሰርዛል"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ውጣ"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"የእንግዳ እንቅስቃሴ ይቀመጥ?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"እንቅስቃሴን አሁን ካለው ክፍለ-ጊዜ ማስቀመጥ ወይም ሁሉንም መተግበሪያዎች እና ውሂብ መሰረዝ ይችላሉ"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ሰርዝ"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"አስቀምጥ"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"ከእንግዳ ሁኔታ ውጣ"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"የእንግዳ ክፍለ-ጊዜን ዳግም አስጀምር"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"እንግዳ ያስወጡ"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"በሚወጡበት ጊዜ ሁሉም እንቅስቃሴዎች ይሰረዛሉ"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"በሚወጡበት ጊዜ እንቅስቃሴዎን ማስቀመጥ ወይም መሰረዝ ይችላሉ"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"የክፍለ-ጊዜ እንቅስቃሴን አሁን ለመሰረዝ ዳግም ያስጀምሩ፣ ወይም በሚወጡበት ጊዜ እንቅስቃሴን ማስቀመጥ ወይም መሰረዝ ይችላሉ"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ፎቶ አንሳ"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ምስል ይምረጡ"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ፎቶ ይምረጡ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index cef5d438a863..3bda387aca89 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"جارٍ الشحن سريعًا"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"جارٍ الشحن ببطء"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"جارٍ الشحن لاسلكيًا"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"وحدة الإرساء للشحن"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"لا يتم الشحن"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"الجهاز متصل بالشاحن، ولا يتم الشحن."</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"مشحونة"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"إعادة الضبط"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"إزالة"</string>
<string name="guest_resetting" msgid="7822120170191509566">"جارٍ إعادة ضبط جلسة الضيف…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"هل تريد إعادة ضبط جلسة الضيف؟"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"سيؤدي إجراء إعادة الضبط إلى بدء جلسة ضيف جديدة وحذف جميع التطبيقات والبيانات من الجلسة الحالية."</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"هل تريد الخروج من وضع الضيف؟"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"سيؤدي الخروج من وضع الضيف إلى حذف التطبيقات والبيانات من جلسة الضيف الحالية."</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"خروج"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"هل تريد حفظ النشاط في وضع الضيف؟"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"يمكنك حفظ نشاط من الجلسة الحالية أو حذف كلّ التطبيقات والبيانات."</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"حذف"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"حِفظ"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"الخروج من وضع الضيف"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"إعادة ضبط جلسة الضيف"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"الخروج من وضع الضيف"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"سيتم حذف جميع الأنشطة عند الخروج من وضع الضيف."</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"يمكنك حفظ نشاطك أو حذفه عند الخروج من وضع الضيف."</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"يمكنك إجراء إعادة ضبط لحذف نشاط الجلسة الآن، أو حِفظ النشاط أو حذفه عند الخروج من وضع الضيف."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"التقاط صورة"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"اختيار صورة"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"اختيار صورة"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 89cf67eee145..a2d5c1415a00 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্ৰুততাৰে চাৰ্জ হৈছে"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"লাহে লাহে চাৰ্জ হৈছে"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"বেতাঁৰৰ মাধ্যমেৰে চাৰ্জ হৈ আছে"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"চাৰ্জিং ড’ক"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"চ্চাৰ্জ কৰা নাই"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"সংযোগ হৈ আছে, চাৰ্জ হৈ থকা নাই"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"চাৰ্জ হ’ল"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ৰিছেট কৰক"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"আঁতৰাওক"</string>
<string name="guest_resetting" msgid="7822120170191509566">"অতিথিৰ ছেশ্বন ৰিছেট কৰি থকা হৈছে…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"অতিথিৰ ছেশ্বন ৰিছেট কৰিবনে?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"এইটোৱে এটা অতিথিৰ ছেশ্বন আৰম্ভ কৰিব আৰু বৰ্তমানৰ ছেশ্বনটোৰ পৰা আটাইবোৰ এপ্ আৰু ডেটা মচিব"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"অতিথি ম’ডৰ পৰা বাহিৰ হ’বনে?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"এইটোৱে বৰ্তমানৰ অতিথিৰ ছেশ্বনটোৰ পৰা এপ্ আৰু ডেটা মচিব"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"বাহিৰ হওক"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"অতিথিৰ কাৰ্যকলাপ ছেভ কৰিবনে?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"আপুনি বৰ্তমানৰ ছেশ্বনটোৰ পৰা কাৰ্যকলাপ ছেভ কৰিব পাৰে অথবা আটাইবোৰ এপ্ আৰু ডেটা মচিব পাৰে"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"মচক"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ছেভ কৰক"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"অতিথি ম’ডৰ পৰা বাহিৰ হওক"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"অতিথিৰ ছেশ্বন ৰিছেট কৰক"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"অতিথিৰ ছেশ্বনৰ পৰা বাহিৰ হওক"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"বাহিৰ হওঁতে আটাইবোৰ কাৰ্যকলাপ মচা হ’ব"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"আপুনি বাহিৰ হওঁতে নিজৰ কাৰ্যকলাপ ছেভ কৰিব অথবা মচিব পাৰে"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"এতিয়াই ছেশ্বনৰ কাৰ্যকলাপ ৰিছেট কৰক অথবা মচক অথবা আপুনি বাহিৰ হওঁতে কাৰ্যকলাপ ছেভ কৰিব অথবা মচিব পাৰে"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"এখন ফট’ তোলক"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ফট’ বাছনি কৰক"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index ca894e68e8f3..33c3e6a7fce4 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Sürətlə doldurulur"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Asta doldurulur"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Simsiz şarj edilir"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Şarj Doku"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Doldurulmur"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Qoşulub, şarj edilmir"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Şarj edilib"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Sıfırlayın"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Silin"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Qonaq məlumatı sıfırlanır…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Qonaq sessiyası sıfırlansın?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Bu, yeni qonaq sessiyası başladacaq və cari sessiyadan bütün tətbiqləri və datanı siləcək"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Qonaq rejimindən çıxılsın?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Bununla cari qonaq sessiyasındakı bütün tətbiqlər və data silinəcək"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Çıxın"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Qonaq fəaliyyəti saxlansın?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Cari sessiyadakı fəaliyyəti saxlaya və ya bütün tətbiq və datanı silə bilərsiniz"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Silin"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Yadda saxlayın"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Qonaq rejimindən çıxın"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Qonaq sessiyasını sıfırlayın"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Qonaq rejimindən çıxın"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Çıxış zamanı bütün fəaliyyətlər silinəcək"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Çıxışda fəaliyyətinizi saxlaya və ya silə bilərsiniz"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Sessiya fəaliyyətini indi silmək üçün sıfırlayın və ya çıxışda fəaliyyəti saxlaya və ya silə bilərsiniz"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Foto çəkin"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Şəkil seçin"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Foto seçin"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 175842e8d6dc..fb202d38f3c6 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo se puni"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sporo se puni"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bežično punjenje"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Stanica za punjenje"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, ne puni se"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetuj"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ukloni"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Sesija gosta se resetuje…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Želite da resetujete sesiju gosta?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Time ćete pokrenuti novu sesiju gosta i izbrisati sve aplikacije i podatke iz aktuelne sesije"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Izaći ćete iz režima gosta?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Time ćete izbrisati sve aplikacije i podatke iz aktuelne sesije gosta"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Izađi"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Sačuvaćete aktivnosti gosta?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Sačuvajte aktivnosti iz aktuelne sesije ili izbrišite sve aplikacije i podatke"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Izbriši"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Sačuvaj"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Izađi iz režima gosta"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Resetuj sesiju gosta"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Izađi iz režima gosta"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Sve aktivnosti će biti izbrisane pri izlazu"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Možete da sačuvate ili izbrišete aktivnosti pri izlazu"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Resetujete za brisanje aktivnosti sesije, ili sačuvajte ili izbrišite aktivnosti pri izlazu"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Slikaj"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Izaberite sliku"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 9bb441ab16fa..12b8f1bfdb09 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хуткая зарадка"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Павольная зарадка"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Бесправадная зарадка"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Зарадная док-станцыя"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не зараджаецца"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Падключана, не зараджаецца"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Зараджаны"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Скінуць"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Выдаліць"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Ідзе скід гасцявога сеанса…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Скінуць гасцявы сеанс?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Будзе запушчаны новы гасцявы сеанс. Усе праграмы і даныя бягучага сеанса будуць выдалены"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Выйсці з гасцявога рэжыму?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Будуць выдалены праграмы і даныя бягучага гасцявога сеанса"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Выйсці"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Захаваць дзеянні госця?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Можна захаваць даныя пра дзеянні ў бягучым сеансе ці выдаліць праграмы і даныя"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Выдаліць"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Захаваць"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Выйсці з гасцявога рэжыму"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Скінуць гасцявы сеанс"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Выйсці з гасцявога рэжыму"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Падчас выхаду будуць выдалены ўсе звесткі пра дзеянні"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Падчас выхаду можна захаваць ці выдаліць звесткі пра дзеянні"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Скіньце звесткі пра дзеянні падчас сеанса зараз. Вы таксама можаце захаваць ці выдаліць іх у час выхаду."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Зрабіць фота"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Выбраць відарыс"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Выбраць фота"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 96ff08b623be..b8fe104b4eb5 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Зарежда се бързо"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Зарежда се бавно"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Зарежда се безжично"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Докинг станция за зарежд."</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не се зарежда"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Свързано, не се зарежда"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Заредена"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Нулиране"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Премахване"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Сесията като гост се нулира…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Да се нулира ли сесията като гост?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Така ще стартирате нова сесия като гост и ще изтриете всички приложения и данни от текущата сесия"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Изход от режима на гост?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Така ще изтриете приложенията и данните от текущата сесия като гост"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Изход"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Запазване на активността като гост?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Можете да запазите активността от сесията или да изтриете всички прил. и данни"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Изтриване"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Запазване"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Изход от режима на гост"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Нулиране на сесията като гост"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Изход от сесията като гост"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Цялата активност ще бъде изтрита при изход"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"При изход можете да запазите активността или да я изтриете"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Нулирайте, за да изтриете активността в сесията сега. Можете също да я запазите или изтриете при изход"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Правене на снимка"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Избиране на изображение"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Избиране на снимката"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 9cc930fbbdd1..abcd1f4333e1 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্রুত চার্জ হচ্ছে"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ধীরে চার্জ হচ্ছে"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"কেবল ছাড়া চার্জ হচ্ছে"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"চার্জিং ডক"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"চার্জ হচ্ছে না"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"কানেক্ট করা থাকলেও চার্জ করা হচ্ছে না"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"চার্জ হয়েছে"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"রিসেট করুন"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"সরান"</string>
<string name="guest_resetting" msgid="7822120170191509566">"গেস্ট সেশন রিসেট করা হচ্ছে..."</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"অতিথি সেশন রিসেট করতে চান?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"এটি নতুন অতিথি সেশন চালু করবে এবং বর্তমান সেশন থেকে সব অ্যাপ ও ডেটা মুছে দেবে"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"\'অতিথি মোড\' ছেড়ে বেরিয়ে আসবেন?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"এটি বর্তমান অতিথি সেশন থেকে অ্যাপ ও ডেটা মুছে দেবে"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"বেরিয়ে আসুন"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"অতিথি মোডের অ্যাক্টিভিটি সেভ করবেন?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"আপনি বর্তমান সেশন থেকে অ্যাক্টিভিটি সেভ করতে বা সব অ্যাপ ও ডেটা মুছতে পারবেন"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"মুছুন"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"সেভ করুন"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"\'অতিথি মোড\' ছেড়ে বেরিয়ে আসুন"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"অতিথি সেশন রিসেট করুন"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"\'অতিথি মোড\' ছেড়ে বেরিয়ে আসুন"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ছেড়ে বেরিয়ে যাওয়ার সময় সব অ্যাক্টিভিটি মুছে দেওয়া হবে"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ছেড়ে বেরিয়ে যাওয়ার সময় আপনি অ্যাক্টিভিটি সেভ করতে পারবেন বা মুছতে পারবেন"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"সেশন অ্যাক্টিভিটি মুছে দিতে এখন রিসেট করুন বা ছেড়ে বেরিয়ে আসার সময় আপনি অ্যাক্টিভিটি সেভ করতে বা মুছতে পারবেন"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ফটো তুলুন"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"একটি ইমেজ বেছে নিন"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ফটো বেছে নিন"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index f8b14525d4c0..d8e0958b23d9 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sporo punjenje"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bežično punjenje"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Priključna stanica"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, ne puni se"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Poništi"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ukloni"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Poništavanje sesije gosta…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Poništiti sesiju gosta?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Ovim ćete pokrenuti novu sesiju gosta i izbrisati sve aplikacije i podatke iz trenutne sesije"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Napustiti način rada za gosta?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Ovim ćete izbrisati aplikacije i podatke iz trenutne sesije gosta"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Napusti"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Sačuvati aktivnost gosta?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Možete sačuvati aktivnost iz ove sesije ili izbrisati sve aplikacije i podatke"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Izbriši"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Sačuvaj"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Napusti način rada za gosta"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Poništi sesiju gosta"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Izlazak iz sesije gosta"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Sva aktivnost će se izbrisati pri napuštanju"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Možete sačuvati ili izbrisati svoju aktivnost pri izlasku"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Poništite da odmah izbrišete aktivnost iz sesije ili je možete sačuvati ili izbrisati pri izlasku"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Snimite fotografiju"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberite sliku"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Odabir fotografije"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 9387d0befbd4..d8201de2ed90 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregant ràpidament"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregant lentament"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carregant sense fil"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Base de càrrega"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"No s\'està carregant"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connectat; no s\'està carregant"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restableix"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Suprimeix"</string>
<string name="guest_resetting" msgid="7822120170191509566">"S\'està restablint el convidat…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Vols restablir la sessió de convidat?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Aquesta acció iniciarà una nova sessió de convidat i suprimirà totes les aplicacions i dades de la sessió actual"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Sortir del mode de convidat?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Aquesta acció suprimirà les aplicacions i dades de la sessió de convidat actual"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Surt"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Desar l\'activitat de convidat?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Pots desar l\'activitat de la sessió actual o suprimir totes les apps i dades"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Suprimeix"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Desa"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Surt del mode de convidat"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Restableix la sessió de convidat"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Surt del mode de convidat"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Se suprimirà tota l\'activitat en sortir"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Pots desar o suprimir l\'activitat en sortir"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Restableix la sessió per suprimir l\'activitat ara, o desa o suprimeix l\'activitat en sortir."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fes una foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Tria una imatge"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Selecciona una foto"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 52b492473455..0ad57cfdc82a 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rychlé nabíjení"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Pomalé nabíjení"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bezdrátové nabíjení"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Nabíjecí dok"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nenabíjí se"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Připojeno, nenabíjí se"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Nabito"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetovat"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Odstranit"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Resetování hosta…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Resetovat relaci hosta?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Tímto zahájíte novou relaci hosta a smažete všechny aplikace a data z aktuální relace"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Ukončit režim hosta?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Tímto smažete aplikace a data z aktuální relace hosta"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ukončit"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Uložit aktivitu hosta?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Aktivitu z aktuální relace můžete uložit, nebo všechny aplikace a data smazat"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Smazat"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Uložit"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Ukončit režim hosta"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Resetovat relaci hosta"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Ukončit režim hosta"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Veškerá aktivita bude při ukončení smazána"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Aktivitu můžete při ukončení uložit nebo smazat"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Aktivitu relace můžete ihned smazat resetováním, případně ji můžete uložit nebo smazat při ukončení"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Pořídit fotku"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrat obrázek"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Vybrat fotku"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 34295fda2677..6c6dd97ce79f 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Oplader hurtigt"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Oplader langsomt"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Trådløs opladning"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Dockingstation"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Oplader ikke"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Tilsluttet, oplader ikke"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Opladet"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Nulstil"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Fjern"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Nulstiller gæst…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Vil du nulstille gæstesessionen?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Denne handling starter en ny gæstesession og sletter alle apps og data fra den aktuelle session"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Vil du afslutte gæstetilstanden?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Denne handling sletter apps og data fra den aktuelle gæstesession."</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Luk"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Vil du gemme gæsteaktiviteten?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Du kan gemme aktivitet fra den aktuelle session eller slette alle apps og data"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Slet"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Gem"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Afslut gæstetilstand"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Nulstil gæstesession"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Afslut gæstesession"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Al aktivitet slettes ved afslutning"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Du kan gemme eller slette din aktivitet ved afslutning"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Nulstil for at slette sessionsaktiviteten nu, eller gem eller slet aktivitet ved afslutning"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Vælg et billede"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Vælg billede"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 63d3c1eb3e09..85e42f583b0e 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Schnelles Aufladen"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Langsames Aufladen"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kabelloses Laden"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Wird am Dock geladen"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Wird nicht geladen"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Verbunden, wird nicht geladen"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Aufgeladen"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Zurücksetzen"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Entfernen"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Gast wird zurückgesetzt…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Gastsitzung zurücksetzen?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Hierdurch wird eine neue Gastsitzung gestartet und alle Apps und Daten der aktuellen Sitzung werden gelöscht"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Gastmodus beenden?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Hierdurch werden Apps und Daten der aktuellen Gastsitzung gelöscht"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Beenden"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Gastaktivität speichern?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Speichere Aktivitäten der aktuellen Sitzung oder lösche alle Apps und Daten"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Löschen"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Speichern"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Gastmodus beenden"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Gastsitzung zurücksetzen"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Gastmodus beenden"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Beim Beenden werden alle Aktivitäten gelöscht"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Speichere oder lösche deine Aktivitäten beim Beenden"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Zurücksetzen, um jetzt die Sitzungsaktivitäten zu löschen, oder Aktivitäten beim Beenden speichern oder löschen"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Foto machen"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Bild auswählen"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Foto auswählen"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 1a7bb615b606..5241417fb9f1 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ταχεία φόρτιση"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Αργή φόρτιση"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Ασύρματη φόρτιση"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Βάση φόρτισης"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Δεν φορτίζει"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Συνδεδεμένη, δεν φορτίζει"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Φορτισμένη"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Επαναφορά"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Κατάργηση"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Επαναφορά επισκέπτη…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Επαναφορά περιόδου σύνδεσης επισκέπτη;"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Με αυτόν τον τρόπο θα ξεκινήσει μια νέα περίοδος σύνδεσης επισκέπτη και θα διαγραφούν όλες οι εφαρμογές και τα δεδομένα από την τρέχουσα περίοδο σύνδεσης"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Έξοδος από λειτ. επισκέπτη;"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Θα διαγραφούν εφαρμογές και δεδομένα από την τρέχουσα περίοδο σύνδεσης επισκέπτη"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Έξοδος"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Αποθήκευση δραστ. επισκέπτη;"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Αποθήκευση δραστ. τρέχουσας περιόδου σύνδεσης ή διαγραφή εφαρμογών και δεδομένων"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Διαγραφή"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Αποθήκευση"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Έξοδος από λειτ. επισκέπτη"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Επαναφορά περ. σύνδεσης επισκέπτη"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Έξοδος επισκέπτη"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Όλη η δραστηριότητα θα διαγραφεί κατά την έξοδο"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Αποθηκεύστε ή διαγράψτε τη δραστηριότητά σας κατά την έξοδο"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Επαναφορά για διαγραφή της δραστηριότητας της περιόδου σύνδεσης ή αποθήκευση ή διαγραφή κατά την έξοδο."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Λήψη φωτογραφίας"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Επιλογή εικόνας"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Επιλογή φωτογραφίας"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 506617b550bc..a7bbdb5110e8 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Charging dock"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset guest session?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"This will start a new guest session and delete all apps and data from the current session"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Delete"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Save"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Exit guest mode"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Reset guest session"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Exit guest"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All activity will be deleted on exit"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"You can save or delete your activity on exit"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset to delete session activity now, or you can save or delete activity on exit"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 637295a148ae..2919aa6528eb 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Charging dock"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset guest session?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"This will start a new guest session and delete all apps and data from the current session"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Delete"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Save"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Exit guest mode"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Reset guest session"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Exit guest"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All activity will be deleted on exit"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"You can save or delete your activity on exit"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset to delete session activity now, or you can save or delete activity on exit"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 506617b550bc..a7bbdb5110e8 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Charging dock"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset guest session?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"This will start a new guest session and delete all apps and data from the current session"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Delete"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Save"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Exit guest mode"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Reset guest session"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Exit guest"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All activity will be deleted on exit"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"You can save or delete your activity on exit"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset to delete session activity now, or you can save or delete activity on exit"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 506617b550bc..a7bbdb5110e8 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Charging dock"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset guest session?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"This will start a new guest session and delete all apps and data from the current session"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Delete"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Save"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Exit guest mode"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Reset guest session"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Exit guest"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All activity will be deleted on exit"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"You can save or delete your activity on exit"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset to delete session activity now, or you can save or delete activity on exit"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index bb4de3da9300..f0aa3d6575b2 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎Charging rapidly‎‏‎‎‏‎"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‏‎‎‎‎‎‎‎‎‎‏‏‎‎‏‎‏‏‏‏‎‎‏‎‏‏‏‎‎‏‏‎‏‎‎‎‏‏‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎Charging slowly‎‏‎‎‏‎"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‎‏‏‎‎‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎Charging wirelessly‎‏‎‎‏‎"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‎‏‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‏‏‎‏‎‎‏‎Charging Dock‎‏‎‎‏‎"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‏‎‎‎‏‎‏‎‏‎‎‏‎‏‏‎‎‎‎‏‏‎‎‏‎‎‎‎‎‏‎‏‎Not charging‎‏‎‎‏‎"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‎‎‎‎‏‎‏‎‏‎Connected, not charging‎‏‎‎‏‎"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‏‎‎‎‏‏‎‎‎‏‏‎‏‎‏‎‎‏‏‏‎‎‎‎‎Charged‎‏‎‎‏‎"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‏‎‏‎‏‎‏‎Reset‎‏‎‎‏‎"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‏‎‏‎‎‎‎‏‎‏‎‎‎‏‎‏‏‎‎‏‏‏‎‏‎‎‏‏‏‎‎‎‏‎‎‎‎‏‏‏‎Remove‎‏‎‎‏‎"</string>
<string name="guest_resetting" msgid="7822120170191509566">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‏‏‏‏‎‎Resetting guest…‎‏‎‎‏‎"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‎‏‏‎‎‏‎‎‏‏‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎Reset guest session?‎‏‎‎‏‎"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‏‏‎‎‏‎‎‏‎‏‏‎‎This will start a new guest session and delete all apps and data from the current session‎‏‎‎‏‎"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‎‎‎‎‎‎‎‏‎‎‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‎‏‎‏‏‎‎‎Exit guest mode?‎‏‎‎‏‎"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‎‎‏‎‏‎‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎‎‏‏‏‏‏‎‎‎‎‏‎‎‏‏‏‎This will delete apps and data from the current guest session‎‏‎‎‏‎"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‏‎‎‏‏‎‏‎‏‎‏‏‎‎‎‎‎‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‏‏‏‎‎‎Exit‎‏‎‎‏‎"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‏‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‏‎‏‎‏‎‎Save guest activity?‎‏‎‎‏‎"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‎‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‏‎‏‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‎‏‎‎‎‏‎‎‏‎‎You can save activity from the current session or delete all apps and data‎‏‎‎‏‎"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‏‎‏‎‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‎‎‏‎‏‎‏‎‎‏‎‏‏‎‎‏‏‎‎Delete‎‏‎‎‏‎"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‎‎‎‎‎‏‏‎‎‎‎‎‎‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‏‏‎Save‎‏‎‎‏‎"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‎‎‎‎‏‏‏‎‎‎‎Exit guest mode‎‏‎‎‏‎"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎‎‎‏‏‏‎‏‏‏‎Reset guest session‎‏‎‎‏‎"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‎‎‎‎‏‎‎‏‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‏‏‏‏‏‎Exit guest‎‏‎‎‏‎"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎‏‏‎‏‏‏‎‎‏‏‎‎‏‎‏‏‏‏‏‎All activity will be deleted on exit‎‏‎‎‏‎"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‎‎‎‎‎‎‎‏‎‎You can save or delete your activity on exit‎‏‎‎‏‎"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎‏‏‏‎‎‏‎‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‎‏‎‏‏‏‎‎‎Reset to delete session activity now, or you can save or delete activity on exit‎‏‎‎‏‎"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎‎Take a photo‎‏‎‎‏‎"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎Choose an image‎‏‎‎‏‎"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‏‎‏‎‏‎‎‎‎‎‎‎‎‎‏‎‏‎‏‎Select photo‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index db0a617a08bb..4672e3ec9fee 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápidamente"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carga lenta"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carga inalámbrica"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Base de carga"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"No se está cargando."</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado; no se está cargando"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restablecer"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Quitar"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Restableciendo invitado…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"¿Quieres restablecer la sesión de invitado?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Esta acción comenzará una nueva sesión de invitado y borrará todas las apps y los datos de la sesión actual"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"¿Salir del modo de invitado?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Esta acción borrará todas las apps y los datos de la sesión de invitado actual"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Salir"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"¿Guardar actividad de invitado?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Puedes guardar la actividad de la sesión actual o borrar las apps y los datos"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Borrar"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Guardar"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Salir del modo de invitado"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Restablecer la sesión de invitado"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Salir del modo de invitado"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Cuando salgas, se borrará toda la actividad"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Puedes guardar o borrar la actividad cuando salgas"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Restablece la sesión para eliminar la actividad ahora; o guarda o borra la actividad cuando salgas"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Tomar una foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Elegir una imagen"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index d69b446c9ff9..24cb2eb89e18 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carga rápida"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carga lenta"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carga inalámbrica"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Base de carga"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"No se está cargando"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado pero sin cargar"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restablecer"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Quitar"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Restableciendo invitado…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"¿Restablecer sesión de invitado?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Se iniciará una nueva sesión de invitado y se borrarán todas las aplicaciones y datos de esta sesión"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"¿Salir del modo Invitado?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Se eliminarán todas las aplicaciones y datos de la sesión de invitado actual"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Salir"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"¿Guardar actividad de invitado?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Puedes guardar la actividad de esta sesión o eliminar todas las aplicaciones y datos"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Eliminar"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Guardar"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Salir del modo Invitado"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Restablecer sesión de invitado"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Salir del modo Invitado"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toda la actividad se eliminará cuando salgas"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Puedes guardar o eliminar tu actividad al salir"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Restablece la sesión para eliminar la actividad ahora, o guarda o borra la actividad al salir"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Hacer foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Seleccionar una imagen"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 30f0350c9dd7..bc847e2b24fb 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiirlaadimine"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Aeglaselt laadimine"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Juhtmevaba laadimine"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Laadimisdokk"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ei lae"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ühendatud, ei laeta"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Laetud"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Lähtesta"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Eemalda"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Külastajaseansi lähtestamine …"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Kas lähtestada külastajaseanss?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"See alustab uut külastajaseanssi ning kustutab kõik praeguse seansi rakendused ja andmed."</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Kas väljuda külalisrežiimist?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"See kustutab praeguse külastajaseansi rakendused ja andmed"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Välju"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Kas salvestada külalise tegevus?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Võite selle seansi tegevused salvestada või kustutada kõik rakendused ja andmed."</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Kustuta"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Salvesta"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Välju külalisrežiimist"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Lähtesta külastajaseanss"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Välju külastajaseansist"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Kõik tegevused kustutatakse väljumisel"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Võite tegevused salvestada või kustutada väljumisel."</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Seansi tegevuste kohe kustutamiseks lähtestage; või salvestage või kustutage need väljumisel."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Pildistage"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Valige pilt"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Valige foto"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 3a38abdc307f..6795662cac3d 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Bizkor kargatzen"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mantso kargatzen"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Hari gabe kargatzen"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Oinarrian kargatzen"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ez da kargatzen ari"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Konektatuta dago, baina ez da kargatzen ari"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Kargatuta"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Berrezarri"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Kendu"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Gonbidatuentzako saioa berrezartzen…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Gonbidatuentzako saioa berrezarri nahi duzu?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Gonbidatuentzako saio berri bat abiaraziko da, eta saio honetako aplikazio eta datu guztiak ezabatuko"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Gonbidatu modutik irten nahi duzu?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Gonbidatuentzako saio honetako aplikazioak eta datuak ezabatuko dira"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Irten"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Gonbidatuaren jarduerak gorde nahi dituzu?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Saio honetako jarduerak gorde ditzakezu, edo aplikazio eta datu guztiak ezabatu"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Ezabatu"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Gorde"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Irten gonbidatu modutik"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Berrezarri gonbidatuentzako saioa"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Irten gonbidatu modutik"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Irtetean, jarduera guztiak ezabatuko dira"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Irtetean, jarduerak gorde edo ezabatu egin ditzakezu"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Berrezarri saioa jarduerak ezabatzeko; bestela, aukeratu jarduerak irtetean gordetzea edo ezabatzea"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Atera argazki bat"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Aukeratu irudi bat"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Hautatu argazki bat"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 76fe50189670..e6337663ad35 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"درحال شارژ شدن سریع"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"درحال شارژ شدن آهسته"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"درحال شارژ بی‌سیم"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"پایه شارژ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"شارژ نمی‌شود"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"متصل، شارژ نمی‌شود"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"شارژ کامل شد"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"بازنشانی"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"برداشتن"</string>
<string name="guest_resetting" msgid="7822120170191509566">"درحال بازنشانی مهمان…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"جلسه مهمان بازنشانی شود؟"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"با این کار، جلسه مهمان جدیدی شروع خواهد شد و همه برنامه‌ها و داده‌ها از جلسه کنونی حذف خواهند شد"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"از حالت مهمان خارج می‌شوید؟"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"با این کار، برنامه‌ها و داده‌ها از جلسه مهمان کنونی حذف خواهند شد."</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"خروج"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"فعالیت مهمان ذخیره شود؟"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"می‌توانید فعالیت جلسه کنونی را ذخیره کنید یا همه برنامه و داده‌ها را حذف کنید"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"حذف"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ذخیره"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"خروج از حالت مهمان"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"بازنشاندن جلسه مهمان"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"خروج مهمان"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"همه فعالیت‌ها هنگام خروج حذف خواهد شد"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"می‌توانید فعالیتتان را هنگام خروج ذخیره یا حذف کنید"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"برای حذف فعالیت جلسه در این لحظه، بازنشانی کنید یا می‌توانید فعالیت را هنگام خروج ذخیره یا حذف کنید"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"عکس گرفتن"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"انتخاب تصویر"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"انتخاب عکس"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 2ff1cfb5ac5f..8f6de31ac409 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Nopea lataus"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Hidas lataus"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Langaton lataus"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Latausteline"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ei laturissa"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Yhdistetty, ei ladata"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Ladattu"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Nollaa"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Poista"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Nollataan vierasta…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Nollataanko vierailija-käyttökerta?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Tämä aloittaa uuden vierailija-käyttökerran ja kaikki nykyisen istunnon sovellukset ja data poistetaan"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Poistutaanko vierastilasta?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Tämä poistaa nykyisen vierailija-käyttökerran sovellukset ja datan"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Sulje"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Tallennetaanko vierastoiminta?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Voit tallentaa tämän istunnon toimintaa tai poistaa kaikki sovellukset ja datan"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Poista"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Tallenna"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Poistu vierastilasta"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Nollaa vierailija-käyttökerta"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Kirjaa vieras ulos"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Kaikki toiminta poistetaan uloskirjaamisen aikana"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Voit tallentaa tai poistaa toiminnan poistuessasi"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Nollaa poistaaksesi istunnon toiminnan nyt, tai voit tallentaa tai poistaa toimintaa poistuessasi"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Ota kuva"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Valitse kuva"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Valitse kuva"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 5325fbc2df41..24ce0012917c 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Recharge rapide"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Recharge lente"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"En recharge sans fil"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Station de recharge"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"N\'est pas en charge"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connecté, pas en charge"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Chargée"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Réinitialiser"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Retirer"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Réinitialisation de la session Invité en cours…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Réinitialiser la session d\'invité?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Une nouvelle session d\'invité sera lancée, et toutes les applications et données de la session en cours seront supprimées"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Quitter le mode Invité?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Les applications et les données de la session d\'invité en cours seront supprimées"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Quitter"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Enregistrer l\'activité d\'invité?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Vous pouvez enregistrer l\'activité de session ou supprimer les applis et données"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Supprimer"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Enregistrer"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Quitter le mode Invité"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Réinitialiser la session d\'invité"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Quitter la session d\'invité"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toute l\'activité sera supprimée à la fin de la session"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Vous pouvez enregistrer ou supprimer votre activité à la fin"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Réinitialisez pour supprimer l\'activité de la session maintenant, ou vous pouvez enregistrer ou supprimer l\'activité à la fin de la session"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Sélectionner une image"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionnez une photo"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index b667fc0519b2..637cc8a2cc00 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charge rapide"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charge lente"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"En charge sans fil"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Station de charge"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Pas en charge"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connectée, pas en charge"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Chargée"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Réinitialiser"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Supprimer"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Réinitialisation de la session Invité…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Réinitialiser la session Invité ?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Cette action lancera une nouvelle session Invité et supprimera toutes les applis et données de la session actuelle"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Quitter le mode Invité ?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Cette action supprimera les applis et données de la session Invité actuelle."</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Quitter"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Enregistrer l\'activité ?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Enregistrez l\'activité de la session actuelle ou supprimez les applis et données"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Supprimer"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Enregistrer"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Quitter le mode Invité"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Réinitialiser la session Invité"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Quitter le mode Invité"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toute l\'activité sera supprimée à la fin de la session"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Vous pouvez enregistrer ou supprimer l\'activité en quittant"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Réinitialisez la session pour supprimer immédiatement l\'activité. Vous pourrez aussi l\'enregistrer ou la supprimer en quittant la session."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choisir une image"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionner une photo"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index da66e922543e..211f8a58f9b3 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rapidamente"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Cargando lentamente"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Cargando sen fíos"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Base de carga"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Non se está cargando"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado, sen cargar"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restablecer"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Quitar"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Restablecendo sesión de convidado…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Queres restablecer a sesión de convidado?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Iniciarase unha nova sesión de convidado e eliminaranse todas as aplicacións e datos desta sesión"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Saír do modo de convidado?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Eliminaranse as aplicacións e os datos da sesión de convidado actual"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Saír"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Gardar actividade do convidado?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Podes gardar a actividade da sesión ou eliminar todas as aplicacións e datos"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Eliminar"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Gardar"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Saír do modo de convidado"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Restablecer sesión de convidado"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Saír do modo de convidado"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Eliminarase toda a actividade ao saír"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Podes gardar ou eliminar a túa actividade ao saír"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Restablece a sesión para eliminar a actividade agora, ou ben gárdaa ou elimínaa ao saír"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Tirar foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Escoller imaxe"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index de2fc038cfd3..0344f4f0e55f 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ઝડપથી ચાર્જ થાય છે"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ધીમેથી ચાર્જ થાય છે"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"વાયરલેસથી ચાર્જિંગ"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ડૉકથી ચાર્જ થઈ રહ્યું છે"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ચાર્જ થઈ રહ્યું નથી"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"કનેક્ટ કરેલું છે, પણ ચાર્જ થઈ રહ્યું નથી"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ચાર્જ થયું"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"રીસેટ કરો"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"કાઢી નાખો"</string>
<string name="guest_resetting" msgid="7822120170191509566">"અતિથિને રીસેટ કરી રહ્યાં છીએ…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"શું અતિથિ સત્ર રીસેટ કરીએ?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"આમ કરવાથી કોઈ નવું અતિથિ સત્ર ચાલુ થશે તેમજ હાલના સત્રમાંની તમામ ઍપ અને ડેટા ડિલીટ થશે"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"શું અતિથિ મોડમાંથી બહાર નીકળીએ?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"આમ કરવાથી હાલના અતિથિ સત્રની તમામ ઍપ અને ડેટા ડિલીટ કરવામાં આવશે"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"બહાર નીકળો"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"શું અતિથિ પ્રવૃત્તિ સાચવીએ?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"તમે હાલના સત્રની પ્રવૃત્તિ સાચવી શકો છો અથવા તમામ ઍપ અને ડેટા ડિલીટ કરી શકો છો"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ડિલીટ કરો"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"સાચવો"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"અતિથિ મોડમાંથી બહાર નીકળો"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"અતિથિ સત્ર રીસેટ કરો"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"અતિથિ મોડમાંથી બહાર નીકળો"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"બહાર નીકળતી વખતે તમામ પ્રવૃત્તિ ડિલીટ કરવામાં આવશે"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"બહાર નીકળતી વખતે તમે પ્રવૃત્તિ સાચવી કે ડિલીટ કરી શકશો"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"સત્રની પ્રવૃત્તિ હમણાં ડિલીટ કરવા માટે રીસેટ કરો અથવા બહાર નીકળતી વખતે તમે પ્રવૃત્તિ સાચવી કે ડિલીટ કરી શકશો"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ફોટો લો"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"છબી પસંદ કરો"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ફોટો પસંદ કરો"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 9eccbf99e6b2..85220983f63f 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"तेज़ चार्ज हो रही है"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"धीरे चार्ज हो रही है"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेस चार्जिंग"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"चार्जिंग डॉक"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज नहीं हो रही है"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"कनेक्ट किया गया, चार्ज नहीं हो रहा है"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"बैटरी चार्ज हो गई"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"रीसेट करें"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"हटाएं"</string>
<string name="guest_resetting" msgid="7822120170191509566">"मेहमान के तौर पर ब्राउज़ करने का सेशन रीसेट किया जा रहा है…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"क्या मेहमान मोड के मौजूदा सेशन को रीसेट करना है?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ऐसा करने पर, मेहमान के तौर पर ब्राउज़ करने का एक नया सेशन शुरू हो जाएगा. साथ ही, पिछले सेशन में मौजूद डेटा और इस्तेमाल किए जा रहे ऐप्लिकेशन को मिटा दिया जाएगा"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"मेहमान मोड से बाहर निकलना है?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"इससे, मेहमान मोड के मौजूदा सेशन का डेटा और इसमें इस्तेमाल हो रहे ऐप मिट जाएंगे"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"बाहर निकलें"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"मेहमान मोड की गतिविधि को सेव करना है?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"मौजूदा सेशन की गतिविधि को सेव किया जा सकता है या सभी ऐप और डेटा को मिटाया जा सकता है"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"मिटाएं"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"सेव करें"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"मेहमान मोड से बाहर निकलें"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"मेहमान मोड के सेशन को रीसेट करें?"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"मेहमान मोड से बाहर निकलें"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"बाहर निकलने पर, सारी गतिविधि मिट जाएगी"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"बाहर निकलने पर, गतिविधि को मिटाया या सेव किया जा सकता है"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"सेशन की गतिविधि को अभी मिटाने के लिए, रीसेट करें. इसके अलावा, बाहर निकलने पर, गतिविधि को मिटाया या सेव किया जा सकता है"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"फ़ोटो खींचें"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"कोई इमेज चुनें"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"फ़ोटो चुनें"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 2714d6e787ef..7480836b9b2f 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sporo punjenje"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bežično punjenje"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Stanica za punjenje"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, ne puni se"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Poništi"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ukloni"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Poništavanje gostujuće sesije…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Želite li poništiti gostujuću sesiju?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Time će se pokrenuti nova gostujuća sesija i izbrisati sve aplikacije i podaci iz trenutačne sesije."</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Napustiti način rada za goste?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Time će se izbrisati aplikacije i podaci iz trenutačne gostujuće sesije."</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Izlaz"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Spremiti aktivnosti gosta?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Možete spremiti aktivnosti iz ove sesije ili izbrisati sve aplikacije i podatke"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Izbriši"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Spremi"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Izlaz iz načina rada za goste"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Poništi gostujuću sesiju"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Izlaz iz gostujuće sesije"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Sve će se aktivnosti izbrisati na izlasku"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Svoje aktivnosti možete spremiti ili izbrisati na izlasku."</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Poništite da odmah izbrišete aktivnost sesije. Inače je možete spremiti ili izbrisati na izlasku."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fotografiraj"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Odabir slike"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index c613d0a7197e..fb8a7372936c 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Gyorstöltés"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Lassú töltés"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Vezeték nélküli töltés"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Töltődokk"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nem tölt"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Csatlakoztatva, nem töltődik"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Feltöltve"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Visszaállítás"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Eltávolítás"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Vendég munkamenet visszaállítása…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Visszaállítja a vendégmunkamenetet?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"A művelettel új vendégmunkamenetet indít, valamint az összes alkalmazást és adatot törli az aktuális munkamenetből"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Kilép a vendég módból?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"A művelettel törlődnek az aktuális vendégmunkamenet alkalmazásai és adatai"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Kilépés"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Menti a vendégtevékenységeket?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Tevékenységeket menthet az aktuális munkamenetből, vagy minden appot és adatot törölhet"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Törlés"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Mentés"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Kilépés a vendég módból"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Vendégmunkamenet visszaállítása"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Vendég kiléptetése"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"A kilépéssel minden tevékenység törlődik"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"A kilépéskor mentheti vagy törölheti a tevékenységeket"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Visszaállítással azonnal törölheti, illetve kilépéskor mentheti vagy törölheti a tevékenységeket"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fotó készítése"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Kép kiválasztása"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Fotó kiválasztása"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index c7a8e4d3727a..7176ae500425 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Դանդաղ լիցքավորում"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Անլար լիցքավորում"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Լիցքավորում դոկ-կայանի միջոցով"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Չի լիցքավորվում"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Միացված է, չի լիցքավորվում"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Լիցքավորված է"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Զրոյացնել"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Հեռացնել"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Հյուրի աշխատաշրջանը վերակայվում է…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Զրոյացնե՞լ հյուրի աշխատաշրջանը"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Կսկսվի հյուրի նոր աշխատաշրջան, իսկ նախորդ աշխատաշրջանի բոլոր հավելվածներն ու տվյալները կջնջվեն"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Դուրս գա՞լ հյուրի ռեժիմից"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Հյուրի ընթացիկ աշխատաշրջանի բոլոր հավելվածներն ու տվյալները կջնջվեն"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Դուրս գալ"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Պահե՞լ հյուրի ռեժիմի պատմությունը"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Պահեք ընթացիկ աշխատաշրջանի պատմությունը կամ ջնջեք հավելվածներն ու տվյալները"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Ջնջել"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Պահել"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Դուրս գալ հյուրի ռեժիմից"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Զրոյացնել հյուրի աշխատաշրջանը"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Դուրս գալ հյուրի ռեժիմից"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Եթե դուրս գաք, ամբողջ պատմությունը կջնջվի"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Դուրս գալիս կարող եք պահել կամ ջնջել ձեր պատմությունը"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Վերակայեք աշխատաշրջանի պատմությունը հիմա կամ պահեք/ջնջեք այն դուրս գալիս"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Լուսանկարել"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Ընտրել պատկեր"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Ընտրեք լուսանկար"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 3104fe17908b..6e1bff8c16da 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengisi daya cepat"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mengisi daya lambat"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Mengisi daya nirkabel"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Mengisi Daya di Dok"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Tidak mengisi daya"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Terhubung, tidak mengisi daya"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Terisi"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Hapus"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Mereset tamu …"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset sesi tamu?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Tindakan ini akan memulai sesi tamu baru dan menghapus semua aplikasi serta data dari sesi saat ini"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Keluar dari mode tamu?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Tindakan ini akan menghapus aplikasi dan data dari sesi tamu saat ini"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Keluar"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Simpan aktivitas tamu?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Anda bisa menyimpan aktivitas sesi saat ini atau menghapus semua aplikasi &amp; data"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Hapus"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Simpan"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Keluar dari mode tamu"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Reset sesi tamu"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Keluar dari mode tamu"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Semua aktivitas akan dihapus saat Anda keluar"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Anda dapat menyimpan atau menghapus aktivitas saat keluar"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset untuk hapus aktivitas sesi sekarang, atau simpan atau hapus aktivitas saat keluar"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih gambar"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index c48142490512..9c8933df6abe 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hröð hleðsla"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Hæg hleðsla"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Hleður þráðlaust"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Hleður í dokku"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ekki í hleðslu"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Tengt, ekki í hleðslu"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Fullhlaðin"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Endurstilla"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Fjarlægja"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Endurstillir gest…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Endurstilla gestalotu?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Þetta opnar nýja gestalotu og eyðir öllum forritum og gögnum úr núverandi lotu"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Loka gestastillingu?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Þetta eyðir forritum og gögnum úr núverandi gestalotu"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Hætta"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Vista aðgerðir úr gestalotu?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Þú getur vistað aðgerðir úr núverandi lotu eða eytt öllum forritum og gögnum"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Eyða"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Vista"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Loka gestastillingu"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Endurstilla gestalotu"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Loka gestastillingu"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Öllum aðgerðum verður eytt þegar lotu er lokað"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Þú getur vistað eða eytt aðgerðum þegar þú lokar"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Endurstilltu til að eyða aðgerðum lotu núna, eða vistaðu eða eyddu aðgerðum þegar þú lokar"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Taka mynd"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Velja mynd"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Velja mynd"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 87fc4b7392e7..52ef9684ee77 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ricarica veloce"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Ricarica lenta"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"In carica, wireless"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"In carica nel dock"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Non in carica"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Dispositivo connesso, non in carica"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Carica"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reimposta"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Rimuovi"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Reimpostazione sessione Ospite in corso…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Vuoi reimpostare la sessione Ospite?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Verrà avviata una nuova sessione Ospite e verranno eliminati tutti i dati e le app della sessione corrente"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Vuoi uscire da modalità Ospite?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Verranno eliminati i dati e le app della sessione Ospite corrente"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Esci"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Vuoi salvare l\'attività Ospite?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Puoi salvare l\'attività della sessione corrente o eliminare tutti i dati e le app"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Elimina"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Salva"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Esci dalla modalità Ospite"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Reimposta sessione Ospite"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Esci dalla modalità Ospite"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Quando esci verrà eliminata tutta l\'attività"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Quando esci puoi salvare o eliminare la tua attività"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reimposta la sessione per eliminare subito l\'attività, oppure salvala o eliminala quando esci"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Scatta una foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Scegli un\'immagine"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Seleziona la foto"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 5c3c304ffacd..d95590a66fbf 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"הסוללה נטענת מהר"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"הסוללה נטענת לאט"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"בטעינה אלחוטית"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"אביזר עגינה לטעינה"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"לא בטעינה"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"מחובר, לא בטעינה"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"הסוללה טעונה"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"איפוס"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"הסרה"</string>
<string name="guest_resetting" msgid="7822120170191509566">"מתבצע איפוס של הגלישה כאורח…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"לאפס את הגלישה כאורח?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"הפעולה הזו תתחיל גלישה חדשה כאורח ותמחק את כל האפליקציות והנתונים מהסשן הנוכחי"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"לצאת ממצב אורח?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"הפעולה הזו תמחק את האפליקציות והנתונים מהגלישה הנוכחית כאורח"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"יציאה"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"לשמור את פעילות האורח?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"אפשר לשמור את הפעילות מהסשן הנוכחי או למחוק את כל האפליקציות והנתונים"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"מחיקה"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"שמירה"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"יציאה ממצב אורח"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"איפוס הגלישה כאורח"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"יציאה ממצב אורח"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"כל הפעילות תימחק ביציאה"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"אפשר לשמור או למחוק את הפעילות שלך ביציאה"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ניתן לאפס כדי למחוק את הפעילות מהסשן כעת, או לשמור או למחוק את הפעילות ביציאה"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"צילום תמונה"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"לבחירת תמונה"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"בחירת תמונה"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index cf112379e68a..5fa42a26d553 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"急速充電中"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"低速充電中"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ワイヤレス充電中"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"充電ホルダー"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"充電していません"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"接続済み、充電していません"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"充電が完了しました"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"リセット"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"削除"</string>
<string name="guest_resetting" msgid="7822120170191509566">"ゲストをリセットしています…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ゲスト セッションをリセットしますか?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"新しいゲスト セッションが開始し、現在のセッションのすべてのアプリとデータが削除されます"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ゲストモードを終了しますか?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"現在のゲスト セッションからすべてのアプリとデータが削除されます"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"終了"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ゲストアクティビティの保存"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"現在のセッションのアクティビティの保存や、すべてのアプリとデータの削除を行えます"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"削除"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"保存"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"ゲストモードを終了"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"ゲスト セッションをリセット"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ゲストを終了"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"終了時にすべてのアクティビティが削除されます"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"終了時にアクティビティを保存、削除できます"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"アクティビティは、リセットして今すぐ削除するか、終了時に保存または削除できます"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"写真を撮る"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"画像を選択"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"写真を選択"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 540f1fc870d6..8ea09619e3b9 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"სწრაფად იტენება"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ნელა იტენება"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"უსადენოდ დატენა"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"დამტენი სამაგრი"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"არ იტენება"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"დაკავშირებულია, არ იტენება"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"დატენილია"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"გადაყენება"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ამოშლა"</string>
<string name="guest_resetting" msgid="7822120170191509566">"მიმდინარეობს სტუმრის გადაყენება…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"გსურთ სტუმრის სესიის გადაყენება?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ამ ქმედებით დაიწყება სტუმრის ახალი სესია და წაიშლება ყველა აპი და მონაცემი მიმდინარე სესიიდან"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"გსურთ სტუმრის რეჟიმიდან გასვლა?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ეს ქმედება წაშლის აპებსა და მონაცემებს სტუმრის რეჟიმის მიმდინარე სესიიდან"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"გასვლა"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"გსურთ სტუმრის აქტივობის შენახვა?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"შეგიძლიათ შეინახოთ აქტივობა მიმდინარე სესიიდან ან წაშალოთ ყველა აპი და მონაცემი"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"წაშლა"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"შენახვა"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"სტუმრის რეჟიმიდან გასვლა"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"სტუმრის სესიის გადაყენება"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"სტუმრის გასვლა"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"გასვლისას ყველა აქტივობა წაიშლება"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"გასვლისას შეგიძლიათ შეინახოთ ან წაშალოთ თქვენი აქტივობა"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"გადააყენეთ სესიის აქტივობის ახლა წასაშლელად. შენახვა/წაშლა გასვლისასაც შეიძლება."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ფოტოს გადაღება"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"აირჩიეთ სურათი"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ფოტოს არჩევა"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 22774799a853..91aea7bd907f 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Жылдам зарядталуда"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Баяу зарядталуда"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Сымсыз зарядталуда"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Қондыру станциясы зарядталуда"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Зарядталу орындалып жатқан жоқ"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Жалғанған, зарядталып жатқан жоқ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Зарядталды"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Бастапқы күйге қайтару"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Өшіру"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Қонақ сеансы бастапқы күйге қайтарылуда…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Қонақ сеансын бастапқы күйге қайтару керек пе?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Мұндайда жаңа қонақ сеансы басталады және ағымдағы сеанстағы барлық қолданба мен дерек жойылады."</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Қонақ режимінен шығу керек пе?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Ағымдағы қонақ сеансындағы барлық қолданба мен дерек жойылады."</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Шығу"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Қонақ әрекетін сақтау керек пе?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Ағымдағы сеанстағы әрекетті сақтай не барлық қолданба мен деректі жоя аласыз."</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Жою"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Сақтау"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Қонақ режимінен шығу"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Қонақ режимін қалпына келтіру"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Қонақ режимінен шығу"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Шыққанда барлық әрекет жойылады."</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Шыққанда барлық әрекетті сақтай немесе жоя аласыз."</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Сеанс дерегін жою үшін оны бастапқы күйге қайтарыңыз. Деректі шығар кезде де сақтауға не жоюға болады."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Фотосурет таңдау"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index eb8ae476b986..fcbe6e5edb31 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"កំពុង​សាកថ្មយឺត"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"កំពុង​សាកថ្ម​ឥតខ្សែ"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ឧបករណ៍ភ្ជាប់សាកថ្ម"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"មិនកំពុង​សាក​ថ្ម"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"បានភ្ជាប់ មិនកំពុង​សាកថ្ម"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"បាន​សាក​ថ្មពេញ"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"កំណត់​ឡើងវិញ"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ដកចេញ"</string>
<string name="guest_resetting" msgid="7822120170191509566">"កំពុងកំណត់​ភ្ញៀវឡើងវិញ…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"កំណត់វគ្គភ្ញៀវឡើងវិញឬ?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ការធ្វើបែបនេះនឹងចាប់ផ្ដើមវគ្គភ្ញៀវថ្មី និងលុបកម្មវិធី និងទិន្នន័យទាំងអស់ចេញពីវគ្គបច្ចុប្បន្ន"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ចាកចេញពីមុខងារភ្ញៀវឬ?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ការធ្វើបែបនេះនឹងលុបកម្មវិធី និងទិន្នន័យចេញពីវគ្គភ្ញៀវបច្ចុប្បន្ន"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ចាកចេញ"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"រក្សាទុកសកម្មភាពភ្ញៀវឬ?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"អ្នកអាចរក្សាទុកសកម្មភាពពីវគ្គបច្ចុប្បន្ន ឬលុបកម្មវិធីនិងទិន្នន័យទាំងអស់"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"លុប"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"រក្សាទុក"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"ចាកចេញពីមុខងារភ្ញៀវ"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"កំណត់វគ្គភ្ញៀវឡើងវិញ"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ចាកចេញពីមុខងារភ្ញៀវ"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"សកម្មភាពទាំងអស់នឹងត្រូវលុបនៅពេលចាកចេញ"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"អ្នកអាចរក្សាទុក ឬលុបសកម្មភាពរបស់អ្នកនៅពេលចាកចេញ"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"កំណត់ឡើងវិញ ដើម្បីលុបសកម្មភាពក្នុងវគ្គឥឡូវនេះ ឬអ្នកអាចរក្សាទុកឬលុបសកម្មភាពនៅពេលចាកចេញ"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើស​រូបភាព"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ជ្រើសរើស​​រូបថត"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 0536960094d1..4f3b1b11410e 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ವೇಗದ ಚಾರ್ಜಿಂಗ್"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ನಿಧಾನ ಗತಿಯ ಚಾರ್ಜಿಂಗ್"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ವೈರ್‌ಲೆಸ್ ಚಾರ್ಜಿಂಗ್"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ಚಾರ್ಜಿಂಗ್ ಡಾಕ್"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ಚಾರ್ಜ್‌ ಆಗುತ್ತಿಲ್ಲ"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"ಕನೆಕ್ಟ್ ಆಗಿದೆ, ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ರೀಸೆಟ್ ಮಾಡಿ"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ತೆಗೆದುಹಾಕಿ"</string>
<string name="guest_resetting" msgid="7822120170191509566">"ಅತಿಥಿ ಬಳಕೆದಾರರ ಸೆಟ್ಟಿಂಗ್ ಅನ್ನು ರೀಸೆಟ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ಅತಿಥಿ ಸೆಷನ್ ಅನ್ನು ರೀಸೆಟ್ ಮಾಡಬೇಕೇ?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ಈ ಪ್ರಕ್ರಿಯೆಯು ಹೊಸ ಅತಿಥಿ ಸೆಶನ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸುತ್ತದೆ ಮತ್ತು ಪ್ರಸ್ತುತ ಸೆಶನ್‌ನಿಂದ ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳು ಹಾಗೂ ಡೇಟಾವನ್ನು ಅಳಿಸುತ್ತದೆ"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ಅತಿಥಿ ಮೋಡ್‌ನಿಂದ ನಿರ್ಗಮಿಸಬೇಕೆ?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ಈ ಪ್ರಕ್ರಿಯೆಯು ಪ್ರಸ್ತುತ ಅತಿಥಿ ಸೆಷನ್‌ನಿಂದ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸುತ್ತದೆ"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ನಿರ್ಗಮಿಸಿ"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ಅತಿಥಿ ಚಟುವಟಿಕೆಯನ್ನು ಉಳಿಸಬೇಕೆ?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"ನೀವು ಪ್ರಸ್ತುತ ಸೆಶನ್‌ನ ಚಟುವಟಿಕೆಯನ್ನು ಉಳಿಸಬಹುದು ಅಥವಾ ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಬಹುದು"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ಅಳಿಸಿ"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ಉಳಿಸಿ"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"ಅತಿಥಿ ಮೋಡ್‌ನಿಂದ ನಿರ್ಗಮಿಸಿ"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"ಅತಿಥಿ ಸೆಷನ್ ಅನ್ನು ರೀಸೆಟ್ ಮಾಡಿ"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ಅತಿಥಿ ಮೋಡ್‌ನಿಂದ ನಿರ್ಗಮಿಸಿ"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ನಿರ್ಗಮಿಸುವಾಗ ಎಲ್ಲಾ ಚಟುವಟಿಕೆಯನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ನಿರ್ಗಮಿಸುವಾಗ ನಿಮ್ಮ ಚಟುವಟಿಕೆಯನ್ನು ನೀವು ಉಳಿಸಬಹುದು ಅಥವಾ ಅಳಿಸಬಹುದು"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ಸೆಶನ್ ಚಟುವಟಿಕೆಯನ್ನು ಈಗ ಅಳಿಸಲು ರೀಸೆಟ್ ಮಾಡಿ ಅಥವಾ ನಿರ್ಗಮಿಸುವಾಗ ನೀವು ಚಟುವಟಿಕೆಯನ್ನು ಉಳಿಸಬಹುದು ಅಥವಾ ಅಳಿಸಬಹುದು"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ಫೋಟೋ ತೆಗೆದುಕೊಳ್ಳಿ"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ಚಿತ್ರವನ್ನು ಆರಿಸಿ"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ಫೋಟೋ ಆಯ್ಕೆಮಾಡಿ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 4fb2385aa655..2133724d8ad3 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"고속 충전 중"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"저속 충전 중"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"무선 충전 중"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"충전 도크"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"충전 안함"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"연결됨, 충전 중 아님"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"충전됨"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"재설정"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"삭제"</string>
<string name="guest_resetting" msgid="7822120170191509566">"게스트 재설정 중…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"게스트 세션을 재설정하시겠습니까?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"새로운 게스트 세션이 시작되고 기존 세션의 모든 앱과 데이터가 삭제됩니다."</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"게스트 모드를 종료하시겠습니까?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"현재 게스트 세션의 앱과 데이터가 삭제됩니다."</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"종료"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"게스트 활동을 저장하시겠습니까?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"기존 세션의 활동을 저장하거나 모든 앱과 데이터를 삭제할 수 있습니다."</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"삭제"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"저장"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"게스트 모드 종료"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"게스트 세션 재설정"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"게스트 종료"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"종료하면 모든 활동이 삭제됩니다."</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"종료 시 활동을 저장하거나 삭제할 수 있습니다."</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"지금 재설정하여 활동 세션을 삭제하거나 종료 시 활동을 저장 또는 삭제할 수 있습니다."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"사진 찍기"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"이미지 선택"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"사진 선택"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index b4f78170c548..b10056f1dea0 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ыкчам кубатталууда"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Жай кубатталууда"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Зымсыз кубатталууда"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Кубаттоо док бекети"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Кубат алган жок"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Туташты, кубатталган жок"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Кубатталды"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Баштапкы абалга келтирүү"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Өчүрүү"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Конок сеансы баштапкы абалга келтирилүүдө…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Конок сеансын баштапкы абалга келтиресизби?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Бул аракет жаңы конок сеансын баштап, учурдагы сеанстагы бардык колдонмолорду жана алардагы нерселерди жок кылат"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Конок режиминен чыгасызбы?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Бул учурдагы конок сеансындагы колдонмолорду жана алардагы нерселерди жок кылат"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Чыгуу"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Коноктун аракеттерин сактайсызбы?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Учурдагы сеанстагы аракеттерди сактап же бардык колдонмолорду жана алардагы нерселерди жок кылсаңыз болот"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Өчүрүү"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Сактоо"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Конок режиминен чыгуу"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Конок сеансын кайра коюу"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Конок режиминен чыгуу"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Чыксаңыз, бардык аракеттер өчүрүлөт"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Чыгуудан мурун аракеттериңизди сактап же жок кылсаңыз болот"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Сеанстагы аракеттерди азыр өчүрсөңүз болот же чыгып баратып өчүрүп же сактап коюңуз"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Сүрөткө тартуу"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Сүрөт тандаңыз"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Сүрөт тандаңыз"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 44621372bff4..74acbc81f606 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ກຳລັງສາກໄຟດ່ວນ"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ກຳລັງສາກໄຟຊ້າໆ"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ກຳລັງສາກໄຟໄຮ້ສາຍ"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ກຳລັງສາກໄຟຜ່ານດັອກ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ບໍ່ໄດ້ສາກໄຟ"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"ເຊື່ອມຕໍ່ແລ້ວ, ບໍ່ໄດ້ສາກໄຟ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ສາກເຕັມແລ້ວ"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ຣີເຊັດ"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ລຶບອອກ"</string>
<string name="guest_resetting" msgid="7822120170191509566">"ກຳລັງຣີເຊັດແຂກ…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ຣີເຊັດເຊດຊັນຂອງແຂກບໍ?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ນີ້ຈະເລີ່ມໄລຍະເວລາຂອງແຂກໃໝ່ ແລະ ລຶບແອັບ ແລະ ຂໍ້ມູນທັງໝົດອອກຈາກເຊດຊັນປັດຈຸບັນ"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ອອກຈາກໂໝດແຂກບໍ?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ນີ້ຈະລຶບແອັບ ແລະ ຂໍ້ມູນອອກຈາກເຊດຊັນແຂກປັດຈຸບັນ"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ອອກ"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ບັນທຶກການເຄື່ອນໄຫວແຂກບໍ?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"ທ່ານສາມາດບັນທຶກການເຄື່ອນໄຫວຈາກເຊດຊັນປັດຈຸບັນ ຫຼື ລຶບແອັບ ແລະ ຂໍ້ມູນທັງໝົດໄດ້"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ລຶບ"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ບັນທຶກ"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"ອອກຈາກໂໝດແຂກ"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"ຣີເຊັດເຊດຊັນຂອງແຂກ"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ອອກຈາກແຂກ"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ການເຄື່ອນໄຫວທັງໝົດຈະຖືກລຶບໃນຕອນອອກ"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ທ່ານສາມາດບັນທຶກ ຫຼື ລຶບການເຄື່ອນໄຫວຂອງທ່ານໃນຕອນອອກໄດ້"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ຣີເຊັດເພື່ອລຶບການເຄື່ອນໄຫວເຊດຊັນຕອນນີ້ ຫຼື ທ່ານສາມາດບັນທຶກ ຫຼື ລຶບການເຄື່ອນໄຫວໃນຕອນອອກໄດ້"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ຖ່າຍຮູບ"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ເລືອກຮູບ"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ເລືອກຮູບ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index b3866dced3e2..13eb79255918 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Greitai įkraunama"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Lėtai įkraunama"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kraunama be laidų"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Įkrovimo dokas"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nekraunama"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Prijungta, neįkraunama"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Įkrauta"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Nustatyti iš naujo"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Pašalinti"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Svečias nustatomas iš naujo…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Nustatyti svečio sesiją iš naujo?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Bus pradėta nauja svečio sesija ir iš esamos sesijos bus ištrintos visos programos ir duomenys"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Išeiti iš svečio režimo?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Bus ištrintos esamos svečio sesijos programos ir duomenys"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Išeiti"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Išsaugoti svečio veiklą?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Galite išsaugoti esamos sesijos veiklą arba ištrinti visas programas ir duomenis"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Ištrinti"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Išsaugoti"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Išeiti iš svečio režimo"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Nustatyti svečio sesiją iš naujo"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Išeiti iš svečio režimo"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Išėjus iš režimo visa veikla bus ištrinta"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Išeidami galite išsaugoti arba ištrinti savo veiklą"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Nustatykite iš naujo, jei norite ištrinti sesijos veiklą dabar, arba galite išeidami išsaugoti ar ištrinti veiklą"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fotografuoti"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Pasirinkti vaizdą"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Pasirinkti nuotrauką"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 1fa6bc8ec1c0..1a9a6c380434 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Notiek ātrā uzlāde"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Notiek lēnā uzlāde"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bezvadu uzlāde"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Uzlādes doks"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nenotiek uzlāde"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ierīce pievienota, uzlāde nenotiek"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Uzlādēts"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Atiestatīt"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Noņemt"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Notiek viesa sesijas atiestatīšana…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Vai atiestatīt viesa sesiju?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Tādējādi tiks sākta jauna viesa sesijas un visas pašreizējās sesijas lietotnes un dati tiks dzēsti"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Vai iziet no viesa režīma?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Tādējādi tiks dzēstas pašreizējās viesa sesijas lietotnes un dati."</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Iziet"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Vai saglabāt viesa darbības?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Varat saglabāt pašreizējās sesijas darbības vai dzēst visas lietotnes un datus"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Dzēst"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Saglabāt"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Iziet no viesa režīma"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Atiestatīt viesa sesiju"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Iziet no viesa režīma"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Izejot tiks dzēstas visas darbības"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Izejot varat saglabāt vai dzēst savas darbības"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Atiestatiet, lai tagad dzēstu sesijas darbības. Izejot varēsiet saglabāt vai dzēst darbības."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Uzņemt fotoattēlu"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Izvēlēties attēlu"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Atlasīt fotoattēlu"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 43fe5e4318cf..98fd0043c0d1 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо полнење"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Бавно полнење"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Се полни безжично"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Се полни на док"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не се полни"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Поврзано, не се полни"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Полна"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Ресетирај"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Отстрани"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Се ресетира гостинот…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Да се ресетира гостинската сесија?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Ова ќе започне нова гостинска сесија и ќе ги избрише сите апликации и податоци од тековната сесија"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Да се излезе од режим на гостин?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Ова ќе ги избрише сите апликации и податоци од тековната гостинска сесија"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Излези"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Да се зачува активност на гостин?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Може да зачувате активност од тековната сесија или да ги избришете сите апликации и податоци"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Избриши"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Зачувај"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Излези од режим на гостин"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Ресетирај ја гостинската сесија"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Излези од режим на гостин"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Целата активност ќе се избрише при излегувањето"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Можете да ја зачувате или избришете вашата активност при излегувањето"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Ресетирајте за да ја избришете активноста на сесијата сега или може да ја зачувате или избришете активноста при излегувањето"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Фотографирајте"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Одберете слика"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Изберете фотографија"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 64bf8f73a011..5c4cbf36c9ec 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"അതിവേഗ ചാർജിംഗ്"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"പതുക്കെയുള്ള ചാർജിംഗ്"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"വയർലെസായി ചാർജുചെയ്യുന്നു"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ചാർജിംഗ് ഡോക്ക്"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ചാർജ്ജുചെയ്യുന്നില്ല"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"കണക്റ്റ് ചെയ്‌തിരിക്കുന്നു, ചാർജ് ചെയ്യുന്നില്ല"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ചാർജായി"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"റീസെറ്റ് ചെയ്യുക"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"നീക്കം ചെയ്യുക"</string>
<string name="guest_resetting" msgid="7822120170191509566">"അതിഥിയെ റീസെറ്റ് ചെയ്യുന്നു…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"അതിഥി സെഷൻ റീസെറ്റ് ചെയ്യണോ?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ഇത് പുതിയൊരു അതിഥി സെഷൻ ആരംഭിക്കുകയും നിലവിലെ സെഷനിൽ നിന്ന് എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കുകയും ചെയ്യും"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"അതിഥി മോഡിൽ നിന്ന് പുറത്തുകടക്കണോ?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"നിലവിലെ അതിഥി സെഷനിൽ നിന്ന് ആപ്പുകളും ഡാറ്റയും ഇത് ഇല്ലാതാക്കും"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"പുറത്തുകടക്കുക"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"അതിഥി ആക്‌റ്റിവിറ്റി സംരക്ഷിക്കണോ?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"നിലവിലെ സെഷനിൽ നിന്നുള്ള ആക്‌റ്റിവിറ്റി സംരക്ഷിക്കാം അല്ലെങ്കിൽ എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കാം"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ഇല്ലാതാക്കുക"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"സംരക്ഷിക്കുക"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"പുറത്തുകടക്കുക"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"അതിഥി സെഷൻ റീസെറ്റ് ചെയ്യുക"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"അതിഥി മോഡിൽ നിന്ന് പുറത്തുകടക്കുക"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"പുറത്തുകടക്കുമ്പോൾ എല്ലാ ആക്‌റ്റിവിറ്റിയും ഇല്ലാതാക്കും"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"പുറത്തുകടക്കുമ്പോൾ ആക്‌റ്റിവിറ്റി സംരക്ഷിക്കുകയോ ഇല്ലാതാക്കുകയോ ചെയ്യാം"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"സെഷൻ ആക്‌റ്റിവിറ്റി ഇപ്പോൾ ഇല്ലാതാക്കാൻ റീസെറ്റ് ചെയ്യുക അല്ലെങ്കിൽ പുറത്തുകടക്കുമ്പോൾ ആക്‌റ്റിവിറ്റി സംരക്ഷിക്കുകയോ ഇല്ലാതാക്കുകയോ ചെയ്യാം"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ഒരു ഫോട്ടോ എടുക്കുക"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ഫോട്ടോ തിരഞ്ഞെടുക്കുക"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 76a6b66b3b3c..4b2c602f4162 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хурдан цэнэглэж байна"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Удаан цэнэглэж байна"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Утасгүй цэнэглэж байна"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Цэнэглэх холбогч"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Цэнэглэхгүй байна"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Холбогдсон, цэнэглээгүй байна"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Цэнэглэсэн"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Шинэчлэх"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Хасах"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Зочныг шинэчилж байна…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Зочны харилцан үйлдлийг шинэчлэх үү?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Энэ нь шинэ зочны харилцан үйлдэл эхлүүлж, одоогийн харилцан үйлдлээс бүх апп болон өгөгдлийг устгана"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Зочны горимоос гарах уу?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Энэ нь одоогийн зочны харилцан үйлдлээс аппууд болон өгөгдлийг устгана"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Гарах"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Зочны үйл ажиллагааг хадгалах уу?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Та одоогийн харилцан үйлдлээс үйл ажиллагаа хадгалах эсвэл бүх апп, өгөгдлийг устгаж болно"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Устгах"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Хадгалах"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Зочны горимоос гарах"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Зочны харилцан үйлдлийг шинэчлэх"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Зочны горимоос гарах"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Бүх үйл ажиллагааг гарах үед устгана"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Та гарахдаа үйл ажиллагаагаа хадгалах эсвэл устгах боломжтой"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Харилцан үйлдлийн үйл ажиллагааг одоо устгахын тулд шинэчлэх эсвэл та гарахдаа үйл ажиллагааг хадгалах, устгах боломжтой"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Зураг авах"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Зураг сонгох"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Зураг сонгох"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 1f89b8ff0892..d18a1f9a580a 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"वेगाने चार्ज होत आहे"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"हळू चार्ज होत आहे"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेसने चार्ज होत आहे"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"चार्जिंग डॉक"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज होत नाही"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"कनेक्ट केले, चार्ज होत नाही"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"चार्ज झाली"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"रीसेट करा"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"काढून टाका"</string>
<string name="guest_resetting" msgid="7822120170191509566">"अतिथीला रीसेट करत आहे…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"अतिथी सत्र रीसेट करायचे का?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"हे नवीन अतिथी सत्र सुरू करेल आणि सध्याच्या सत्रातील सर्व अ‍ॅप्स व डेटा हटवेल"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"अतिथी मोडमधून बाहेर पडायचे का?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"हे सध्याच्या अतिथी सत्रातील अ‍ॅप्स आणि डेटा हटवेल"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"बाहेर पडा"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"अतिथी अ‍ॅक्टिव्हिटी सेव्ह करायची का?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"सध्याच्या सत्रातील अ‍ॅक्टिव्हिटी सेव्ह करू किंवा सर्व अ‍ॅप्स व डेटा हटवू शकता"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"हटवा"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"सेव्ह करा"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"अतिथी मोडमधून बाहेर पडा"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"अतिथी सत्र रीसेट करा"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"अतिथी मोडमधून बाहेर पडा"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"बाहेर पडल्यावर सर्व अ‍ॅक्टिव्हिटी हटवली जाईल"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"बाहेर पडल्यावर तुमची अ‍ॅक्टिव्हिटी सेव्ह करू किंवा हटवू शकता"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"सत्र अ‍ॅक्टिव्हिटी आता हटवण्यासाठी रीसेट करा किंवा तुम्ही बाहेर पडल्यावर अ‍ॅक्टिव्हिटी सेव्ह करू अथवा हटवू शकता"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"फोटो काढा"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"इमेज निवडा"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"फोटो निवडा"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 2034af25b130..efdf98983b20 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengecas dgn cepat"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mengecas perlahan"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Mengecas tanpa wayar"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Dok Pengecasan"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Tidak mengecas"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Bersambung, tidak mengecas"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Sudah dicas"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Tetapkan semula"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Alih keluar"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Menetapkan semula tetamu…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Tetapkan semula sesi tetamu?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Tindakan ini akan memulakan sesi tetamu baharu dan memadamkan semua apl dan data daripada sesi semasa"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Keluar daripada mod tetamu?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Tindakan ini akan memadamkan apl dan data daripada sesi tetamu semasa"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Keluar"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Simpan aktiviti tetamu?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Anda boleh menyimpan aktiviti daripada sesi semasa atau memadamkan semua apl dan data"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Padam"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Simpan"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Keluar daripada mod tetamu"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Tetapkan semula sesi tetamu"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Keluar mod tetamu"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Semua aktiviti akan dipadamkan semasa keluar"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Aktiviti anda boleh disimpan atau dipadam semasa keluar"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Tetapkan semula sesi untuk memadamkan aktiviti sesi sekarang atau anda boleh menyimpan atau memadamkan aktiviti semasa keluar"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih imej"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index e7824d1696d4..1b43ad472d8b 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"အမြန် အားသွင်းနေသည်"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"နှေးကွေးစွာ အားသွင်း"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ကြိုးမဲ့ အားသွင်းနေသည်"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"အားသွင်းအထိုင်"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"အားသွင်းမနေပါ"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"ချိတ်ဆက်ထားသည်၊ အားသွင်းမနေပါ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"အားသွင်းပြီးပါပြီ"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ပြင်ဆင်သတ်မှတ်ရန်"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ဖယ်ရှားရန်"</string>
<string name="guest_resetting" msgid="7822120170191509566">"ဧည့်သည်ကို ပြင်ဆင်သတ်မှတ်နေသည်…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ဧည့်သည် စက်ရှင် ပြင်ဆင်သတ်မှတ်မလား။"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"၎င်းသည် ဧည့်သည် စက်ရှင်အသစ်ကို စတင်မည်ဖြစ်ပြီး လက်ရှိစက်ရှင်မှ အက်ပ်နှင့် ဒေတာအားလုံးကို ဖျက်ပါမည်"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ဧည့်သည်မုဒ်မှ ထွက်မလား။"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"၎င်းသည် လက်ရှိဧည့်သည် စက်ရှင်မှ အက်ပ်နှင့် ဒေတာအားလုံးကို ဖျက်လိုက်ပါမည်"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ထွက်ရန်"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ဧည့်သည်လုပ်ဆောင်ချက် သိမ်းမလား။"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"လက်ရှိစက်ရှင်မှ လုပ်ဆောင်ချက် သိမ်းနိုင်သည် (သို့) အက်ပ်နှင့် ဒေတာအားလုံး ဖျက်နိုင်သည်"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ဖျက်ရန်"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"သိမ်းရန်"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"ဧည့်သည်မုဒ်မှ ထွက်ရန်"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"ဧည့်သည် စက်ရှင် ပြင်ဆင်သတ်မှတ်ရန်"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ဧည့်သည့်မှ ထွက်ရန်"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ထွက်သည့်အခါ လုပ်ဆောင်ချက်အားလုံးကို ဖျက်လိုက်မည်"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ထွက်သည့်အခါ လုပ်ဆောင်ချက်ကို သိမ်းနိုင် (သို့) ဖျက်နိုင်သည်"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"စက်ရှင်လုပ်ဆောင်ချက်ကို ယခုဖျက်ရန် ပြင်ဆင်သတ်မှတ်နိုင်သည် (သို့) ထွက်သည့်အခါ သိမ်းနိုင်၊ ဖျက်နိုင်သည်"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ဓာတ်ပုံရိုက်ရန်"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ပုံရွေးရန်"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ဓာတ်ပုံရွေးရန်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 2f0b46f08261..f6c82f8eaabd 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Lader raskt"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Lader sakte"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Lader trådløst"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Ladedokk"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Lader ikke"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Tilkoblet, lader ikke"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Ladet"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Tilbakestill"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Fjern"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Tilbakestiller gjesten …"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Vil du tilbakestille gjesteøkten?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Dette starter en ny gjesteøkt og sletter alle apper og data fra den gjeldende økten"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Vil du avslutte gjestemodus?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Dette sletter apper og data fra den gjeldende gjesteøkten"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Avslutt"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Vil du lagre gjesteaktivitet?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Du kan lagre aktivitet fra den gjeldende økten eller slette alle apper og data"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Slett"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Lagre"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Avslutt gjestemodus"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Tilbakestill gjesteøkten"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Avslutt gjesteøkten"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All aktivitet slettes når du avslutter"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Du kan lagre eller slette aktiviteten når du avslutter"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Tilbakestill for å slette øktaktivitet nå, eller du kan lagre eller slette aktivitet når du avslutter"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Ta et bilde"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Velg et bilde"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Velg et bilde"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 34da9a7a5245..5fa4daa8fb9c 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै छ"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ढिलो चार्ज हुँदै छ"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेस तरिकाले चार्ज गरिँदै छ"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"डक चार्ज हुँदै छ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज भइरहेको छैन"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"कनेक्ट गरिएको छ, चार्ज भइरहेको छैन"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"चार्ज भयो"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"रिसेट गर्नुहोस्"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"हटाउनुहोस्"</string>
<string name="guest_resetting" msgid="7822120170191509566">"अतिथिका रूपमा ब्राउज गर्ने सेसन रिसेट गरिँदै छ…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"अतिथि सत्र रिसेट गर्ने हो?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"यसो गर्नाले नयाँ अतिथि सत्र सुरु हुने छ र हालको अतिथि सत्रका सबै एप तथा डेटा मेटिने छ"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"अतिथि मोडबाट बाहिरिने हो?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"यसो गर्नाले हालको अतिथि सत्रका एप तथा डेटा मेटिने छ"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"बाहिरिनुहोस्"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"अतिथि सत्रमा गरिएका क्रियाकलाप सेभ गर्ने हो?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"तपाईं हालको सत्रमा गरिएका क्रियाकलाप सेभ गर्न वा सबै एप तथा डेटा मेटाउन सक्नुहुन्छ"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"मेटाउनुहोस्"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"सेभ गर्नुहोस्"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"अतिथि मोडबाट बाहिरिनुहोस्"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"अतिथि सत्र रिसेट गर्नुहोस्"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"अतिथि मोडबाट बाहिरिनुहोस्"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"अतिथि मोडबाट बाहिरिँदा सबै क्रियाकलाप मेटाइने छ"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"तपाईं अतिथि मोडबाट बाहिरिँदा आफ्ना क्रियाकलाप सेभ गर्ने वा मेटाउने विकल्प रोज्न सक्नुहुन्छ"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"यो सत्रमा गरिएका क्रियाकलाप अहिले नै मेटाउन रिसेट गर्नुहोस्, अथवा तपाईं अतिथि मोडबाट बाहिरिँदा आफ्ना क्रियाकलाप सेभ गर्ने वा मेटाउने विकल्प रोज्न सक्नुहुन्छ"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"फोटो खिच्नुहोस्"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"कुनै फोटो छनौट गर्नुहोस्"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"फोटो चयन गर्नुहोस्"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index f250b604eff9..360582fb932c 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Snel opladen"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Langzaam opladen"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Draadloos opladen"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Oplaaddock"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Wordt niet opgeladen"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Verbonden, wordt niet opgeladen"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Opgeladen"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetten"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Verwijderen"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Gast resetten…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Gastsessie resetten?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Hierdoor wordt een nieuwe gastsessie gestart en worden alle apps en gegevens van de huidige sessie verwijderd"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Gastmodus sluiten?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Hierdoor worden apps en gegevens van de huidige gastsessie verwijderd"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Sluiten"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Gastactiviteit opslaan?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Sla activiteit van de huidige sessie op of verwijder alle apps en gegevens"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Verwijderen"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Opslaan"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Gastmodus sluiten"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Gastsessie resetten"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Gastmodus verlaten"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Alle activiteit wordt na het afsluiten verwijderd"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Je kunt je activiteit bij afsluiten opslaan of verwijderen"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Voer een reset uit om de sessie-activiteit nu te verwijderen of verwijder of sla je activiteit op bij afsluiten"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Foto maken"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Afbeelding kiezen"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Foto selecteren"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index e7a80f3a486c..db496bac51fe 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ଧୀରେ ଚାର୍ଜ ହେଉଛି"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ୱେୟରଲେସ ଭାବେ ଚାର୍ଜିଂ"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ଡକ ଚାର୍ଜ ହେଉଛି"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ଚାର୍ଜ ହେଉନାହିଁ"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"ସଂଯୋଗ କରାଯାଇଛି, ଚାର୍ଜ ହେଉନାହିଁ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ଚାର୍ଜ ହୋଇଯାଇଛି"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ରିସେଟ୍ କରନ୍ତୁ"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
<string name="guest_resetting" msgid="7822120170191509566">"ଅତିଥି ସେସନକୁ ରିସେଟ୍ କରାଯାଉଛି…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ଅତିଥି ସେସନକୁ ରିସେଟ କରିବେ?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ଏହା ଏକ ନୂଆ ଅତିଥି ସେସନ ଆରମ୍ଭ କରିବ ଏବଂ ବର୍ତ୍ତମାନର ସେସନରୁ ସମସ୍ତ ଆପ୍ସ ଏବଂ ଡାଟାକୁ ଡିଲିଟ କରିବ"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ଅତିଥି ମୋଡରୁ ବାହାରି ଯିବେ?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ଏହା ବର୍ତ୍ତମାନର ଅତିଥି ସେସନରୁ ଆପ୍ସ ଏବଂ ଡାଟାକୁ ଡିଲିଟ କରିବ"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ବାହାରି ଯାଆନ୍ତୁ"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ଅତିଥି କାର୍ଯ୍ୟକଳାପ ସେଭ କରିବେ?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"ଆପଣ ଏବେର ସେସନରୁ କାର୍ଯ୍ୟକଳାପକୁ ସେଭ କରିପାରିବେ ବା ସବୁ ଆପ୍ସ ଓ ଡାଟାକୁ ଡିଲିଟ କରିପାରିବେ"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ଡିଲିଟ କରନ୍ତୁ"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ସେଭ କରନ୍ତୁ"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"ଅତିଥି ମୋଡରୁ ବାହାରି ଯାଆନ୍ତୁ"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"ଅତିଥି ସେସନକୁ ରିସେଟ କରନ୍ତୁ"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ଅତିଥି ମୋଡରୁ ବାହାରି ଯାଆନ୍ତୁ"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ବାହାରିବା ସମୟରେ ସମସ୍ତ କାର୍ଯ୍ୟକଳାପକୁ ଡିଲିଟ କରାଯିବ"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ବାହାରିବା ସମୟରେ ଆପଣଙ୍କର କାର୍ଯ୍ୟକଳାପକୁ ସେଭ ବା ଡିଲିଟ କରିପାରିବେ"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ବର୍ତ୍ତମାନ ସେସନ କାର୍ଯ୍ୟକଳାପକୁ ଡିଲିଟ କରିବାକୁ ରିସେଟ କରନ୍ତୁ କିମ୍ବା ବାହାରିବା ସମୟରେ ଆପଣ କାର୍ଯ୍ୟକଳାପକୁ ସେଭ କିମ୍ବା ଡିଲିଟ କରିପାରିବେ"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ଗୋଟିଏ ଫଟୋ ଉଠାନ୍ତୁ"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ଏକ ଛବି ବାଛନ୍ତୁ"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ଫଟୋ ବାଛନ୍ତୁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 90f2b0fddb9b..ca58fe946b76 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ਬਿਨਾਂ ਤਾਰ ਤੋਂ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ਡੌਕ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"ਕਨੈਕਟ ਹੈ, ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਹੀ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ਚਾਰਜ ਹੋ ਗਈ"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ਰੀਸੈੱਟ ਕਰੋ"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ਹਟਾਓ"</string>
<string name="guest_resetting" msgid="7822120170191509566">"ਮਹਿਮਾਨ ਨੂੰ ਰੀਸੈੱਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ਕੀ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਨੂੰ ਰੀਸੈੱਟ ਕਰਨਾ ਹੈ?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ਇਸ ਨਾਲ ਨਵਾਂ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਸ਼ੁਰੂ ਹੋ ਜਾਵੇਗਾ ਅਤੇ ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਦੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਮਿਟ ਜਾਵੇਗਾ"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ਕੀ ਮਹਿਮਾਨ ਮੋਡ ਤੋਂ ਬਾਹਰ ਜਾਣਾ ਹੈ?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ਇਸ ਨਾਲ ਮੌਜੂਦਾ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਦੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਮਿਟ ਜਾਵੇਗਾ"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ਬਾਹਰ ਜਾਓ"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ਕੀ ਮਹਿਮਾਨ ਸਰਗਰਮੀ ਰੱਖਿਅਤ ਕਰਨੀ ਹੈ?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"ਤੁਸੀਂ ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਦੀ ਸਰਗਰਮੀ ਨੂੰ ਰੱਖਿਅਤ ਕਰ ਸਕਦੇ ਹੋ ਜਾਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਮਿਟਾ ਸਕਦੇ ਹੋ"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ਮਿਟਾਓ"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ਰੱਖਿਅਤ ਕਰੋ"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"ਮਹਿਮਾਨ ਮੋਡ ਤੋਂ ਬਾਹਰ ਜਾਓ"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਨੂੰ ਰੀਸੈੱਟ ਕਰੋ"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ਮਹਿਮਾਨ ਮੋਡ ਤੋਂ ਬਾਹਰ ਜਾਓ"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ਬਾਹਰ ਜਾਣ \'ਤੇ ਸਾਰੀ ਸਰਗਰਮੀ ਮਿਟਾਈ ਜਾਵੇਗੀ"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ਤੁਸੀਂ ਬਾਹਰ ਜਾਣ \'ਤੇ ਆਪਣੀ ਸਰਗਰਮੀ ਰੱਖਿਅਤ ਕਰ ਜਾਂ ਮਿਟਾ ਸਕਦੇ ਹੋ"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ਸੈਸ਼ਨ ਦੀ ਸਰਗਰਮੀ ਹੁਣੇ ਮਿਟਾਉਣ ਲਈ ਰੀਸੈੱਟ ਕਰੋ ਜਾਂ ਤੁਸੀਂ ਬਾਹਰ ਜਾਣ \'ਤੇ ਸਰਗਰਮੀ ਨੂੰ ਰੱਖਿਅਤ ਕਰ ਜਾਂ ਮਿਟਾ ਸਕਦੇ ਹੋ"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ਇੱਕ ਫ਼ੋਟੋ ਖਿੱਚੋ"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ਕੋਈ ਚਿੱਤਰ ਚੁਣੋ"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ਫ਼ੋਟੋ ਚੁਣੋ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 93e5c889b8d9..aec96a2f8ade 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Wolne ładowanie"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Ładowanie bezprzewodowe"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Ładowanie na stacji dokującej"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nie podłączony"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Podłączono, brak ładowania"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Naładowana"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetuj"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Usuń"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Resetuję sesję gościa…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Zresetować sesję gościa?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Zostanie uruchomiona nowa sesja gościa. Wszystkie aplikacje i dane z obecnej sesji zostaną usunięte."</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Zamknąć tryb gościa?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Wszystkie aplikacje i dane z obecnej sesji gościa zostaną usunięte."</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Wyjdź"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Zapisać aktywność gościa?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Możesz zapisać aktywność z obecnej sesji lub usunąć wszystkie aplikacje i dane"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Usuń"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Zapisz"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Zamknij tryb gościa"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Zresetuj sesję gościa"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Zakończ tryb gościa"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Cała aktywność zostanie usunięta po zamknięciu"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Możesz zapisać lub usunąć swoją aktywność podczas zamykania."</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Zresetuj, aby teraz usunąć aktywność z tej sesji. Możesz też ją zapisać lub usunąć podczas zamykania sesji."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Zrób zdjęcie"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Wybierz obraz"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Wybierz zdjęcie"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 861c1833fdf9..e883c8d77068 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregando devagar"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carregando sem fio"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Base de carregamento"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Não está carregando"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado sem carregar"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Redefinir"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remover"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Redefinindo visitante…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Redefinir Sessão de visitante?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Essa ação vai iniciar uma nova Sessão de visitante e excluir todos os apps e dados da sessão atual"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Sair do modo visitante?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Essa ação vai excluir apps e dados da Sessão de visitante atual"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Sair"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Salvar a atividade do visitante?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Você pode salvar a atividade da sessão atual ou excluir todos os apps e dados"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Excluir"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Salvar"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Sair do modo visitante"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Redefinir Sessão de visitante"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Sair do modo visitante"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Todas as atividades serão excluídas ao sair"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Você pode salvar ou excluir sua atividade ao sair"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Redefina para excluir a atividade da sessão agora. Salve ou exclua a atividade ao sair"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index af12d853de29..e2cb7924481c 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregamento rápido"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregamento lento"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"A carregar sem fios"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Est. ancor. carreg."</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Não está a carregar"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ligado, não está a carregar"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Repor"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remover"</string>
<string name="guest_resetting" msgid="7822120170191509566">"A repor o convidado…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Repor sessão de convidado?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Esta ação inicia uma nova sessão de convidado e elimina todas as apps e dados da sessão atual"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Sair do modo convidado?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Esta ação elimina as apps e os dados da sessão de convidado atual"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Sair"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Guardar atividade de convidado?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Pode guardar a atividade da sessão atual ou eliminar todas as apps e dados"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Eliminar"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Guardar"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Sair do modo convidado"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Repor sessão de convidado"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Sair do modo de convidado"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toda a atividade é eliminada ao sair"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Pode guardar ou eliminar a sua atividade ao sair"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reponha para eliminar agora a atividade da sessão. Pode ainda guardar ou eliminar a atividade ao sair"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 861c1833fdf9..e883c8d77068 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregando devagar"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carregando sem fio"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Base de carregamento"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Não está carregando"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado sem carregar"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Redefinir"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remover"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Redefinindo visitante…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Redefinir Sessão de visitante?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Essa ação vai iniciar uma nova Sessão de visitante e excluir todos os apps e dados da sessão atual"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Sair do modo visitante?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Essa ação vai excluir apps e dados da Sessão de visitante atual"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Sair"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Salvar a atividade do visitante?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Você pode salvar a atividade da sessão atual ou excluir todos os apps e dados"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Excluir"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Salvar"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Sair do modo visitante"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Redefinir Sessão de visitante"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Sair do modo visitante"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Todas as atividades serão excluídas ao sair"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Você pode salvar ou excluir sua atividade ao sair"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Redefina para excluir a atividade da sessão agora. Salve ou exclua a atividade ao sair"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index f84dec650e21..39f304c940f9 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Se încarcă rapid"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Se încarcă lent"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Se încarcă wireless"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Suport de încărcare"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nu se încarcă"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectat, nu se încarcă"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Încărcată"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetați"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Eliminați"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Se resetează invitatul…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Resetați sesiunea pentru invitați?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Astfel, va începe o nouă sesiune pentru invitați și se vor șterge toate aplicațiile și datele din sesiunea actuală"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Ieșiți din modul pentru invitați?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Se vor șterge toate aplicațiile și datele din sesiunea pentru invitați actuală"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ieșiți"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Salvați activitatea invitatului?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Salvați activitatea din sesiunea actuală sau ștergeți aplicațiile și datele"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Ștergeți"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Salvați"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Ieșiți din modul pentru invitați"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Resetați sesiunea pentru invitați"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Ieșiți din modul pentru invitați"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toate activitățile vor fi șterse la ieșire"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Puteți să salvați sau să ștergeți activitatea la ieșire"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Resetați pentru a șterge acum activitatea din sesiune sau salvați ori ștergeți activitatea la ieșire"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Faceți o fotografie"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Alegeți o imagine"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Selectați fotografia"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index d70e31a21c0b..68543a03811f 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Быстрая зарядка"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Медленная зарядка"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Беспроводная зарядка"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Док-станция: зарядка"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не заряжается"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Подключено, не заряжается"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Батарея заряжена"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Сбросить"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Удалить"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Сброс гостевого сеанса…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Сбросить гостевой сеанс?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"При этом начнется новый гостевой сеанс, а все данные и приложения предыдущего сеанса будут удалены."</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Выйти из гостевого режима?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Все приложения и данные текущего гостевого сеанса будут удалены."</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Выйти"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Сохранить историю сеанса?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Сохраните историю текущего сеанса или удалите данные и приложения."</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Удалить"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Сохранить"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Выйти из гостевого режима"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Сбросить гостевой сеанс"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Выйти из гостевого режима"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"История будет удалена сразу после выхода."</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"При выходе вы можете сохранить или удалить историю."</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Можно сбросить историю сеанса прямо сейчас, либо удалить или сохранить ее при выходе."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Выбрать фото"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Выбрать фотографию"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 217ba1de6812..dbc1fc2a89c4 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ශීඝ්‍ර ආරෝපණය"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"සෙමින් ආරෝපණය"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"නොරැහැන්ව ආරෝපණය වේ"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ආරෝපණ ඩොකය"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ආරෝපණය නොවේ"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"සම්බන්ධයි, ආරෝපණය නොවේ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"අරෝපිතයි"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"යළි සකසන්න"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ඉවත් කරන්න"</string>
<string name="guest_resetting" msgid="7822120170191509566">"අමුත්තා යළි සකසමින්…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ආගන්තුක සැසිය යළි සකසන්නද?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"මෙය නව ආගන්තුක සැසියක් ආරම්භ කර වත්මන් සැසියෙන් සියලු යෙදුම් සහ දත්ත මකනු ඇත"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ආගන්තුක ප්‍රකාරයෙන් පිටවන්නද?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"මෙය වත්මන් ආගන්තුක සැසියෙන් යෙදුම් සහ දත්ත මකනු ඇත"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"පිටවන්න"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ආගන්තුක ක්‍රියාකාරකම් සුරකින්නද?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"ඔබට වත්මන් සැසියෙන් ක්‍රියාකාරකම් සුරැකීමට හෝ සියලු යෙදුම් සහ දත්ත මැකීමට හැකිය"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"මකන්න"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"සුරකින්න"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"ආගන්තුක ප්‍රකාරයෙන් පිටවන්න"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"ආගන්තුක සැසිය යළි සකසන්න"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"අමුත්තා පිටවීම"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"පිටවීමේදී සියලු ක්‍රියාකාරකම් මකනු ඇත"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ඔබට පිටවීමේදී ඔබගේ ක්‍රියාකාරකම් සුරැකීමට හෝ මැකීමට හැකිය"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"දැන් සැසි ක්‍රියාකාරකම් මැකීමට යළි සකසන්න, නැතහොත් ඔබට පිටවීමේදී ක්‍රියාකාරකම් සුරැකීමට හෝ මැකීමට හැකිය"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ඡායාරූපයක් ගන්න"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"රූපයක් තෝරන්න"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ඡායාරූපය තෝරන්න"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 51588e33a97e..a3a0e2307fb1 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rýchle nabíjanie"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Pomalé nabíjanie"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Nabíja sa bezdrôtovo"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Nabíjací dok"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nenabíja sa"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Pripojené, nenabíja sa"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Nabité"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetovať"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Odstrániť"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Relácia hosťa sa resetuje…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Chcete resetovať reláciu hosťa?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Týmto sa spustí nová relácia hosťa a odstránia sa všetky aplikácie a údaje z aktuálnej relácie"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Chcete ukončiť režim pre hostí?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Ukončí sa režim pre hostí a odstránia sa aplikácie a údaje z relácie hosťa"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ukončiť"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Chcete uložiť aktivitu hosťa?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Môžte uložiť aktivitu aktuálnej relácie alebo odstrániť všetky aplikácie a údaje"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Odstrániť"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Uložiť"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Ukončiť režim pre hostí"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Resetovať reláciu hosťa"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Ukončiť režim pre hostí"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Pri ukončení sa všetka aktivita odstráni"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Aktivitu môžete pri ukončení uložiť alebo odstrániť"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Resetovaním ihneď odstránite aktivitu relácie alebo ju uložte či odstráňte pri ukončení relácie"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Odfotiť"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrať obrázok"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Vybrať fotku"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index d1c355849fe5..7e2172fb65fe 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hitro polnjenje"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Počasno polnjenje"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Brezžično polnjenje"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Polnjenje na nosilcu"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Se ne polni"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, se ne polni"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Napolnjeno"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Ponastavi"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Odstrani"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Ponastavljanje gosta …"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Želite ponastaviti sejo gosta?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"S tem boste začeli novo sejo gosta ter izbrisali vse aplikacije in podatke v trenutni seji."</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Želite zapreti način za goste?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"S tem boste izbrisali aplikacije in podatke v trenutni seji gosta."</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Zapri"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Želite shraniti dejavnost gosta?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Lahko shranite dejavnost v trenutni seji ali izbrišete vse aplikacije in podatke."</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Izbriši"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Shrani"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Zapri način za goste"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Ponastavi sejo gosta"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Zapri sejo gosta"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Ko zaprete način za goste, bo vsa dejavnost izbrisana."</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Ob zaprtju načina lahko shranite ali izbrišete dejavnost."</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Ponastavite za izbris dejavnosti v seji zdaj, lahko pa jo shranite ali izbrišete, ko zaprete način za goste."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fotografiranje"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Izberi sliko"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Izbira fotografije"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 57f059a8c0c2..e6bf1c1296b9 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Karikim i shpejtë"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Po karikohet ngadalë"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Po karikohet pa tel"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Në stacion karikimi"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nuk po karikohet"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Lidhur, jo në karikim"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Karikuar"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Rivendos"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Hiq"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Vizitori po rivendoset…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Të rivendoset sesioni për vizitorë?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Kjo do të fillojë një sesion të ri për vizitorë dhe do të fshijë të gjitha aplikacionet dhe të dhënat nga sesioni aktual"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Të hiqet modaliteti \"vizitor\"?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Kjo do të fshijë aplikacionet dhe të dhënat nga sesioni aktual për vizitorë"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Dil"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Të ruhet aktiviteti i vizitorit?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Ruaj aktivitetin nga sesioni aktual ose fshi të gjitha aplikacionet e të dhënat"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Fshi"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Ruaj"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Dil nga modaliteti \"vizitor\""</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Rivendos sesionin për vizitorë"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Dil nga modaliteti \"vizitor\""</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Të gjitha aktivitetet do të fshihen kur të dalësh"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Mund ta ruash ose ta fshish aktivitetin tënd kur të dalësh"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Rivendose për të fshirë aktivitetin e sesionit tani ose mund ta ruash ose ta fshish aktivitetin kur të dalësh"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Bëj një fotografi"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Zgjidh një imazh"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Zgjidh një fotografi"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 4d8a11f68389..e429a635ce7f 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо се пуни"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Споро се пуни"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Бежично пуњење"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Станица за пуњење"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не пуни се"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Повезано, не пуни се"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Напуњено"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Ресетуј"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Уклони"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Сесија госта се ресетује…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Желите да ресетујете сесију госта?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Тиме ћете покренути нову сесију госта и избрисати све апликације и податке из актуелне сесије"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Изаћи ћете из режима госта?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Тиме ћете избрисати све апликације и податке из актуелне сесије госта"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Изађи"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Сачуваћете активности госта?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Сачувајте активности из актуелне сесије или избришите све апликације и податке"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Избриши"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Сачувај"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Изађи из режима госта"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Ресетуј сесију госта"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Изађи из режима госта"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Све активности ће бити избрисане при излазу"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Можете да сачувате или избришете активности при излазу"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Ресетујете за брисање активности сесије, или сачувајте или избришите активности при излазу"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Сликај"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Одабери слику"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Изаберите слику"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index ea859e492af0..faf7c4b9e81e 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Laddas långsamt"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Laddas trådlöst"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Dockningsstation"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Laddar inte"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ansluten, laddas inte"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Laddat"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Återställ"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ta bort"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Gästsessionen återställs …"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Vill du återställa gästsessionen?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"En ny gästsession startas och alla appar och all data från den pågående sessionen raderas"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Vill du avsluta gästläget?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Appar och data från den pågående gästsessionen raderas"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Avsluta"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Vill du spara gästaktivitet?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Du kan spara aktivitet från den pågående sessionen eller radera appar och data"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Radera"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Spara"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Avsluta gästläget"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Återställ gästsession"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Avsluta gästsession"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All aktivitet raderas när du avslutar"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Du kan spara eller radera aktivitet när du avslutar"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Återställ om du vill radera sessionsaktiviteten nu, eller spara eller radera aktivitet när du avslutar"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Ta ett foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Välj en bild"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Välj foto"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 2a3a6fbff4c7..71eb1a818f56 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Inachaji kwa kasi"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Inachaji pole pole"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Inachaji bila kutumia waya"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Kituo cha Kuchaji"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Haichaji"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Imeunganishwa, haichaji"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Imechajiwa"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Badilisha"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ondoa"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Inabadilisha kipindi cha mgeni…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Ungependa kuweka upya kipindi cha mgeni?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Hii itaanzisha upya kipindi cha mgeni na kufuta programu na data yote kwenye kipindi cha sasa"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Utafunga matumizi ya wageni?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Hatua hii itafuta programu na data kutoka kwenye kipindi cha mgeni cha sasa"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Funga"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Utahifadhi shughuli za mgeni?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Unaweza kuhifadhi shughuli kutoka kipindi cha sasa au kufuta programu na data yote"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Futa"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Hifadhi"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Funga matumizi ya wageni"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Weka upya kipindi cha mgeni"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Funga utumiaji wa mgeni"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Shughuli zote zitafutwa wakati wa kufunga"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Unaweza kuhifadhi au kufuta shughuli zako wakati wa kufunga"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Weka upya ili ufute shughuli za kipindi sasa au unaweza kuhifadhi au kufuta shughuli wakati wa kufunga"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Chagua picha"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Chagua picha"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 6d4c57955b2f..1c60acc19aff 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"வேகமாக சார்ஜாகிறது"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"மெதுவாக சார்ஜாகிறது"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"வயரின்றி சார்ஜாகிறது"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"சார்ஜிங் டாக்"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"சார்ஜ் செய்யப்படவில்லை"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"இணைக்கப்பட்டுள்ளது, சார்ஜாகவில்லை"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"சார்ஜாகிவிட்டது"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"மீட்டமை"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"அகற்று"</string>
<string name="guest_resetting" msgid="7822120170191509566">"கெஸ்ட்டை மீட்டமைக்கிறது…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"கெஸ்ட் அமர்வை ரீசெட் செய்யவா?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"புதிய கெஸ்ட் அமர்வு தொடங்கப்படும், மேலும் தற்போதைய கெஸ்ட் அமர்வின் ஆப்ஸ் மற்றும் தரவு அனைத்தும் நீக்கப்படும்"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"கெஸ்ட் முறையிலிருந்து வெளியேறவா?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"தற்போதைய கெஸ்ட் அமர்வின் ஆப்ஸ் மற்றும் தரவு அனைத்தும் நீக்கப்படும்"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"வெளியேறு"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"கெஸ்ட் செயல்பாடுகளைச் சேமிக்கவா?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"தற்போதைய அமர்வின் செயல்பாடுகளைச் சேமிக்கலாம் அல்லது ஆப்ஸையும் தரவையும் நீக்கலாம்"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"நீக்கு"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"சேமி"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"கெஸ்ட் பயன்முறையிலிருந்து வெளியேறு"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"கெஸ்ட் அமர்வை ரீசெட் செய்"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"கெஸ்ட் பயன்முறையிலிருந்து வெளியேறு"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"வெளியேறியவுடன் அனைத்துச் செயல்பாடுகளும் நீக்கப்படும்"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"வெளியேறும்போது செயல்பாடுகளைச் சேமிக்கலாம் அல்லது நீக்கலாம்"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"அமர்வின் செயல்பாடுகளை இப்போதே நீக்க ரீசெட் செய்யவும் அல்லது வெளியேறும்போது செயல்பாடுகளைச் சேமிக்கலாம் அல்லது நீக்கலாம்"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"படமெடுங்கள்"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"படத்தைத் தேர்வுசெய்யுங்கள்"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"படத்தைத் தேர்ந்தெடுங்கள்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 4df4089241dc..e9d3fd0e84ca 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"వేగవంతమైన ఛార్జింగ్"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"నెమ్మదిగా ఛార్జింగ్"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"వైర్‌లెస్ ఛార్జింగ్"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ఛార్జింగ్ డాక్"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ఛార్జ్ కావడం లేదు"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"కనెక్ట్ చేయబడింది, ఛార్జ్ చేయబడలేదు"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ఛార్జ్ చేయబడింది"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"రీసెట్ చేయండి"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"తీసివేయండి"</string>
<string name="guest_resetting" msgid="7822120170191509566">"గెస్ట్ సెషన్‌ను రీసెట్ చేస్తోంది…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"గెస్ట్ సెషన్‌ను రీసెట్ చేయాలా?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ఇది కొత్త గెస్ట్ సెషన్‌ను ప్రారంభిస్తుంది, ప్రస్తుత సెషన్ నుండి అన్ని యాప్‌లు, డేటాను తొలగిస్తుంది."</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"గెస్ట్ మోడ్ నిష్క్రమించాలా?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ఇది ప్రస్తుత గెస్ట్ సెషన్ నుండి యాప్‌లను వాటితో పాటు డేటాను తొలగిస్తుంది"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"నిష్క్రమించండి"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"గెస్ట్ యాక్టివిటీని సేవ్ చేయాలా?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"మీరు సెషన్ నుండి యాక్టివిటీని సేవ్ చేయవచ్చు, అన్ని యాప్‌లు, డేటాను తొలగించవచ్చు"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"తొలగించండి"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"సేవ్ చేయండి"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"గెస్ట్ మోడ్ నుండి వైదొలగండి"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"గెస్ట్ సెషన్‌ను రీసెట్ చేయండి"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"గెస్ట్ మోడ్ నుండి నిష్క్రమించండి"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"నిష్క్రమణ సమయంలో మొత్తం యాక్టివిటీ తొలగించబడుతుంది"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"మీ నిష్క్రమణలో, యాక్టివిటీని సేవ్ చేయవచ్చు లేదా తొలగించవచ్చు"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"సెషన్ యాక్టివిటీని తొలగించడానికి ఇప్పుడే రీసెట్ చేయండి లేదా మీరు నిష్క్రమించేటప్పుడు యాక్టివిటీని సేవ్ చేయవచ్చు లేదా తొలగించవచ్చు"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ఒక ఫోటో తీయండి"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ఇమేజ్‌ను ఎంచుకోండి"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ఫోటోను ఎంచుకోండి"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 772a9795ba1d..e46bdc481a00 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"กำลังชาร์จอย่างเร็ว"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"กำลังชาร์จอย่างช้าๆ"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"กำลังชาร์จแบบไร้สาย"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"กำลังชาร์จบนแท่น"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ไม่ได้ชาร์จ"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"เชื่อมต่ออยู่ ไม่ได้ชาร์จ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ชาร์จแล้ว"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"รีเซ็ต"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"นำออก"</string>
<string name="guest_resetting" msgid="7822120170191509566">"กำลังรีเซ็ตผู้เข้าร่วม…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"รีเซ็ตเซสชันผู้ใช้ชั่วคราวไหม"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"การดำเนินการนี้จะเริ่มเซสชันผู้ใช้ชั่วคราวใหม่ และจะลบแอปและข้อมูลทั้งหมดจากเซสชันปัจจุบัน"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ออกจากโหมดผู้ใช้ชั่วคราวไหม"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"การดำเนินการนี้จะลบแอปและข้อมูลออกจากเซสชันผู้ใช้ชั่วคราวในปัจจุบัน"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ออก"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"บันทึกกิจกรรมของผู้ใช้ชั่วคราวไหม"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"คุณสามารถบันทึกกิจกรรมจากเซสชันปัจจุบันหรือจะลบแอปและข้อมูลทั้งหมดก็ได้"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ลบ"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"บันทึก"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"ออกจากโหมดผู้ใช้ชั่วคราว"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"รีเซ็ตเซสชันผู้ใช้ชั่วคราว"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ออกจากโหมดผู้ใช้ชั่วคราว"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ระบบจะลบกิจกรรมทั้งหมดเมื่อออก"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"คุณสามารถบันทึกหรือลบกิจกรรมเมื่อออก"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"รีเซ็ตเพื่อลบกิจกรรมของเซสชันตอนนี้เลย หรือจะ​บันทึกหรือลบกิจกรรมเมื่อออกก็ได้"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ถ่ายรูป"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"เลือกรูปภาพ"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"เลือกรูปภาพ"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index d9d4e3a1117b..21d07c1a314a 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mabilis na charge"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mabagal na charge"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Wireless na nagcha-charge"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Charging Dock"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Hindi nagcha-charge"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Nakakonekta, hindi nagcha-charge"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Nasingil"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"I-reset"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Alisin"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Nire-reset ang bisita…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"I-reset ang session ng bisita?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Magsisimula ito ng bagong session ng bisita at made-delete ang lahat ng app at data mula sa kasalukuyang session"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Umalis sa guest mode?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Ide-delete nito ang mga app at data mula sa kasalukuyang session ng bisita"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Umalis"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"I-save ang aktibidad ng bisita?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Puwedeng i-save ang aktibidad ng session ngayon o i-delete lahat ng app at data"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"I-delete"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"I-save"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Umalis sa guest mode"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"I-reset ang session ng bisita"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Umalis sa pagiging bisita"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Made-delete ang lahat ng aktibidad kapag umalis"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Puwede mong i-save o i-delete ang aktibidad pagkaalis"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Mag-reset para mag-delete ng aktibidad ng session ngayon, o puwede kang mag-save o mag-delete ng aktibidad pagkaalis"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Kumuha ng larawan"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Pumili ng larawan"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Pumili ng larawan"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index c47e8eea521a..0a5f7da5a00d 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hızlı şarj oluyor"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Yavaş şarj oluyor"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kablosuz şarj oluyor"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Şarj Yuvası"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Şarj olmuyor"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Bağlandı, şarj olmuyor"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Şarj oldu"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Sıfırla"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Kaldır"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Misafir oturumu sıfırlanıyor…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Misafir oturumu sıfırlansın mı?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Bu işlem, yeni bir misafir oturumu başlatarak mevcut oturumdaki tüm uygulamaları ve verileri siler"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Misafir modundan çıkılsın mı?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Bu işlem mevcut misafir oturumundaki tüm uygulamaları ve verileri siler"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Çık"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Misafir etkinliği kaydedilsin mi?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Oturumdaki etkinliği kaydedebilir ya da tüm uygulama ve verileri silebilirsiniz"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Sil"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Kaydet"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Misafir modundan çık"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Misafir oturumunu sıfırla"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Misafir modundan çık"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Çıkış yapıldığında tüm etkinlikler silinir"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Etkinliklerinizi çıkarken kaydedebilir veya silebilirsiniz"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Oturum etkinliklerini silmek için sıfırlayabilir ya da çıkarken kaydedebilir veya silebilirsiniz"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fotoğraf çek"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Resim seç"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Fotoğraf seç"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 00c2153cb4b8..769aab78a995 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Повільне заряджання"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Бездротове заряджання"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Зарядка: док-станція"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не заряджається"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Підключено, не заряджається"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Заряджено"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Скинути"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Вилучити"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Скидання сеансу в режимі \"Гість\"…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Скинути сеанс у режимі гостя?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Почнеться новий сеанс у режимі гостя, а всі додатки й дані з поточного сеансу буде видалено"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Вийти з режиму гостя?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Усі додатки й дані з поточного сеансу в режимі гостя буде видалено."</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Вийти"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Зберегти дії в режимі гостя?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Ви можете зберегти дії з поточного сеансу або видалити всі додатки й дані"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Видалити"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Зберегти"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Вийти з режиму гостя"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Скинути сеанс у режимі гостя"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Вийти з режиму гостя"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Під час виходу буде видалено історію всіх дій"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Під час виходу можна зберегти або видалити ваші дії"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Можна скинути історію сеансу просто зараз або видалити чи зберегти її під час виходу."</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Зробити фотографію"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Вибрати зображення"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Вибрати фотографію"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 43dee94bd9a6..5d7928bfe9e9 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"تیزی سے چارج ہو رہا ہے"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"آہستہ چارج ہو رہی ہے"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"وائرلیس طریقے سے چارج ہو رہی ہے"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"چارجنگ ڈاک"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"چارج نہیں ہو رہا ہے"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"منسلک ہے، چارج نہیں ہو رہی ہے"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"چارج ہو گئی"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ری سیٹ کریں"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ہٹائیں"</string>
<string name="guest_resetting" msgid="7822120170191509566">"مہمان کو ری سیٹ کرنا…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"مہمان سیشن کو ری سیٹ کریں؟"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"اس سے ایک نیا مہمان سیشن شروع ہو گا اور موجودہ سیشن سے تمام ایپس اور ڈیٹا حذف ہو جائے گا"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"مہمان وضع سے باہر نکلیں؟"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"یہ موجودہ مہمان سیشن سے ایپس اور ڈیٹا کو حذف کر دے گا"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"باہر نکلیں"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"مہمان کی سرگرمی محفوظ کریں؟"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"آپ موجودہ سیشن سے سرگرمی کو محفوظ یا تمام ایپس اور ڈیٹا کو حذف کر سکتے ہیں"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"حذف کریں"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"محفوظ کریں"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"مہمان وضع سے باہر نکلیں"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"مہمان سیشن کو ری سیٹ کریں"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"مہمان وضع سے باہر نکلیں"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"باہر نکلنے پر تمام سرگرمیاں حذف کر دی جائیں گی"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"باہر نکلنے پر آپ اپنی سرگرمی کو محفوظ یا حذف کر سکتے ہیں"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"سیشن کی سرگرمی کو ابھی حذف کرنے کے لیے ری سیٹ کریں، یا باہر نکلنے پر آپ اپنی سرگرمی کو محفوظ یا حذف کر سکتے ہیں"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ایک تصویر لیں"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ایک تصویر منتخب کریں"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"تصویر منتخب کریں"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 22251c3c186d..46cd59690f0b 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Tezkor quvvat olmoqda"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sekin quvvat olmoqda"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Simsiz quvvat olmoqda"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Quvvatlash doki"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Quvvat olmayapti"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ulangan, quvvat olmayapti"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Quvvat oldi"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Tiklash"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Olib tashlash"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Mehmon seansi tiklanmoqda…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Mehmon seansi tiklansinmi?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Bunda yangi mehmon seansi ishga tushadi va joriy seans ilova va maʼlumotlari tozalanadi"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Mehmon rejimidan chiqasizmi?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Bunda joriy mehmon seansidagi ilova va ularning maʼlumotlari tozalanadi"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Chiqish"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Mehmon faoliyati saqlansinmi?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Joriy seansdagi faoliyatni saqlash yoki barcha ilova va maʼlumotlarni oʻchirib tashlashingiz mumkin"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Oʻchirish"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Saqlash"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Mehmon rejimidan chiqish"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Mehmon seansini tiklash"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Mehmon rejimidan chiqish"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Chiqishda faolliklar tarixi tozalab tashlanadi"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Chiqish vaqtida faoliyatni saqlash yoki tozalash mumkin"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Faoliyat hozir tozalanib tiklanishi yoki chiqish vaqtida saqlanishi yoki tozalanishi mumkin"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Surat tanlash"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index cb9a18fe5950..22ead805c61b 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Đang sạc nhanh"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Đang sạc chậm"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Đang sạc không dây"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Đế sạc"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Hiện không sạc"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Đã kết nối nhưng chưa sạc"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Đã sạc"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Đặt lại"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Xoá"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Đang đặt lại phiên khách…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Đặt lại phiên khách?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Thao tác này sẽ bắt đầu một phiên khách mới và xoá mọi ứng dụng cũng như dữ liệu trong phiên hiện tại"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Thoát khỏi chế độ khách?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Thao tác này sẽ xoá các ứng dụng và dữ liệu trong phiên khách hiện tại"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Thoát"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Lưu hoạt động ở chế độ khách?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Bạn có thể lưu hoạt động trong phiên hiện tại hoặc xoá mọi ứng dụng và dữ liệu"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Xoá"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Lưu"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Thoát khỏi chế độ khách"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Đặt lại phiên khách"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Thoát khỏi chế độ khách"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Mọi hoạt động sẽ bị xoá khi thoát"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Bạn có thể lưu hoặc xoá hoạt động của mình khi thoát"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Đặt lại để xoá hoạt động trong phiên ngay bây giờ, hoặc bạn có thể lưu hoặc xoá hoạt động khi thoát"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Chụp ảnh"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Chọn một hình ảnh"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Chọn ảnh"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index e5dd6a014ddc..1a44ac25bbad 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充电"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"正在慢速充电"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"正在无线充电"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"充电基座"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"未在充电"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"已连接,未充电"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"已充满电"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"重置"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"移除"</string>
<string name="guest_resetting" msgid="7822120170191509566">"正在重置访客会话…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"要重置访客会话吗?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"此操作会开始新的访客会话,并删除当前会话中的所有应用和数据"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"要退出访客模式吗?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"此操作会删除当前访客会话中的所有应用和数据"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"退出"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"要保存访客活动记录吗?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"您可以保存当前会话中的活动记录,也可以删除所有应用和数据"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"删除"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"保存"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"退出访客模式"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"重置访客会话"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"退出访客模式"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"退出时所有活动记录都将被删除"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"您可以在退出时保存或删除您的活动记录"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"请立即重置以删除会话活动记录;或者,您也可以在退出时保存或删除活动记录"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"拍摄照片"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"选择图片"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"选择照片"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index cfa3c0618d19..c497b58a1911 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"慢速充電中"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"無線充電中"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"充電插座"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"非充電中"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"已連接,非充電中"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"已充滿電"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"重設"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"移除"</string>
<string name="guest_resetting" msgid="7822120170191509566">"正在重設訪客…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"要重設訪客工作階段嗎?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"此操作會開始新的訪客工作階段,並刪除目前工作階段的所有應用程式和資料"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"要結束訪客模式嗎?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"此操作會刪除目前訪客工作階段中的所有應用程式和資料"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"結束"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"要儲存訪客活動嗎?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"您可儲存目前工作階段中的活動或刪除所有應用程式和資料"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"刪除"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"儲存"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"結束訪客模式"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"重設訪客工作階段"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"結束訪客模式"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"結束時將會刪除所有活動"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"您可以在結束時儲存或刪除活動"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"重設可立即刪除工作階段活動,或者您可以在結束時儲存或刪除活動"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"揀相"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index e439ef5d6edf..bb126c27d10a 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"慢速充電中"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"正在進行無線充電"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"充電座架"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"非充電中"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"已連接,尚未充電"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"充電完成"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"重設"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"移除"</string>
<string name="guest_resetting" msgid="7822120170191509566">"正在重設訪客…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"要重設訪客工作階段嗎?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"這麼做將開始新的訪客工作階段,並刪除目前工作階段中的所有應用程式和資料"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"要結束訪客模式嗎?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"這麼做將刪除目前訪客工作階段中的所有應用程式和資料"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"結束"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"要儲存訪客活動嗎?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"你可以儲存目前工作階段中的活動,也可以刪除所有應用程式和資料"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"刪除"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"儲存"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"結束訪客模式"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"重設訪客工作階段"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"結束訪客模式"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"結束時將刪除所有活動"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"你可以在結束時儲存或刪除活動"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"重設即可立即刪除工作階段活動,你也可以在結束時儲存或刪除活動"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"選取相片"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index ebc9cb7927ff..c9d4b630a7e8 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -482,6 +482,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ishaja ngokushesha"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Ishaja kancane"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Iyashaja ngaphandle kwentambo"</string>
+ <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Idokhu yokushaja"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Ayishaji"</string>
<string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ixhunyiwe, ayishaji"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Kushajiwe"</string>
@@ -598,6 +599,21 @@
<string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Setha kabusha"</string>
<string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Susa"</string>
<string name="guest_resetting" msgid="7822120170191509566">"Ukusetha kabusha isimenywa…"</string>
+ <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Sesha kabusha isikhathi sesihambeli?"</string>
+ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Lokhu kuzoqala isikhathi sesihambeli esisha futhi kusule wonke ama-app nedatha kusuka esikhathini samanje"</string>
+ <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Phuma kumodi yesihambeli?"</string>
+ <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Lokhu kuzosula ama-app nedatha kusuka esikhathini sesihambeli samanje"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Phuma"</string>
+ <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Londoloza umsebenzi wesihambeli?"</string>
+ <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Ungalondoloza umsebenzi kusuka esikhathini samanje noma usule wonke ama-app nedatha"</string>
+ <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Sula"</string>
+ <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Londoloza"</string>
+ <string name="guest_exit_button" msgid="5774985819191803960">"Phuma kumodi yesivakashi"</string>
+ <string name="guest_reset_button" msgid="2515069346223503479">"Setha kabusha isikhathi sesihambeli"</string>
+ <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Phuma kusivakashi"</string>
+ <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Wonke umsebenzi uzosulwa lapho uphuma"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Ungalondoloza noma usule umsebenzi wakho lapho uphuma"</string>
+ <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Setha kabusha ukuze usule umsebenzi wesikhathi manje, noma ungalondoloza noma usule umsebenzi lapho uphuma"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Thatha isithombe"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Khetha isithombe"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Khetha isithombe"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 847f1dc541e8..4f84c8c817cb 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -246,9 +246,9 @@
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the PAN profile (accessing Internet through remote device). [CHAR LIMIT=40] -->
<string name="bluetooth_profile_pan">Internet access</string>
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the PBAP profile. [CHAR LIMIT=40] -->
- <string name="bluetooth_profile_pbap">Contact sharing</string>
+ <string name="bluetooth_profile_pbap">Contacts and call history sharing</string>
<!-- Bluetooth settings. The user-visible summary string that is used whenever referring to the PBAP profile (sharing contacts). [CHAR LIMIT=60] -->
- <string name="bluetooth_profile_pbap_summary">Use for contact sharing</string>
+ <string name="bluetooth_profile_pbap_summary">Use for contacts and call history sharing</string>
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the PAN profile (sharing this device's Internet connection). [CHAR LIMIT=40] -->
<string name="bluetooth_profile_pan_nap">Internet connection sharing</string>
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the map profile. -->
@@ -1134,6 +1134,8 @@
<string name="battery_info_status_charging_slow">Charging slowly</string>
<!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging wirelessly. -->
<string name="battery_info_status_charging_wireless">Charging wirelessly</string>
+ <!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when the device is dock charging. -->
+ <string name="battery_info_status_charging_dock">Charging Dock</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_discharging">Not charging</string>
<!-- Battery Info screen. Value for a status item. A state which device is connected with any charger(e.g. USB, Adapter or Wireless) but not charging yet. Used for diagnostic info screens, precise translation isn't needed -->
@@ -1438,6 +1440,44 @@
<string name="guest_remove_guest_confirm_button">Remove</string>
<!-- Status message indicating the device is in the process of resetting the guest user. [CHAR_LIMIT=NONE] -->
<string name="guest_resetting">Resetting guest\u2026</string>
+ <!-- Dialog title on action reset and restart guest [CHAR LIMIT=60] -->
+ <string name="guest_reset_and_restart_dialog_title">Reset guest session?</string>
+ <!-- Dialog message on action reset and restart guest [CHAR LIMIT=160] -->
+ <string name="guest_reset_and_restart_dialog_message">This will start a new guest
+ session and delete all apps and data from the current session</string>
+ <!-- Dialog title on action exit guest (ephemeral guest) [CHAR LIMIT=32] -->
+ <string name="guest_exit_dialog_title">Exit guest mode?</string>
+ <!-- Dialog message on action exit guest (ephemeral guest) [CHAR LIMIT=80] -->
+ <string name="guest_exit_dialog_message">This will delete
+ apps and data from the current guest session</string>
+ <!-- Dialog button on action exit guest (ephemeral guest) [CHAR LIMIT=80] -->
+ <string name="guest_exit_dialog_button">Exit</string>
+ <!-- Dialog title on action exit guest (non-ephemeral guest) [CHAR LIMIT=32] -->
+ <string name="guest_exit_dialog_title_non_ephemeral">Save guest activity?</string>
+ <!-- Dialog message on action exit guest (non-ephemeral guest) [CHAR LIMIT=80] -->
+ <string name="guest_exit_dialog_message_non_ephemeral">You can save activity from
+ the current session or delete all apps and data</string>
+ <!-- Button on guest exit, clear data (non-ephemeral guest) [CHAR LIMIT=80] -->
+ <string name="guest_exit_clear_data_button">Delete</string>
+ <!-- Button on guest exit, save data (non-ephemeral guest) [CHAR LIMIT=80] -->
+ <string name="guest_exit_save_data_button">Save</string>
+ <!-- Label for button in confirmation dialog when exiting guest user [CHAR LIMIT=35] -->
+ <string name="guest_exit_button">Exit guest mode</string>
+ <!-- Label for button in confirmation dialog when resetting guest user [CHAR LIMIT=35] -->
+ <string name="guest_reset_button">Reset guest session</string>
+ <!-- Label for guest icon in quick settings user switcher [CHAR LIMIT=35] -->
+ <string name="guest_exit_quick_settings_button">Exit guest</string>
+ <!-- Message of the notification when guest mode is entered
+ and it's a ephemeral guest [CHAR LIMIT=60] -->
+ <string name="guest_notification_ephemeral">All activity will be deleted on exit</string>
+ <!-- Message of the notification when guest mode is entered
+ and it's not a ephemeral guest and it's a first time guest login [CHAR LIMIT=60] -->
+ <string name="guest_notification_non_ephemeral">You can save or delete your activity on exit</string>
+ <!-- Message of the notification when guest mode is entered
+ and it's not a ephemeral guest and it's not a first time guest login [CHAR LIMIT=NONE] -->
+ <string name="guest_notification_non_ephemeral_non_first_login">Reset to delete session
+ activity now, or you can save or delete activity on exit</string>
+
<!-- An option in a photo selection dialog to take a new photo [CHAR LIMIT=50] -->
<string name="user_image_take_photo">Take a photo</string>
<!-- An option in a photo selection dialog to choose a pre-existing image [CHAR LIMIT=50] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index feb4212035bc..b9c4030d9d0e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -239,6 +239,8 @@ public class Utils {
statusString = res.getString(R.string.battery_info_status_charging);
break;
}
+ } else if (batteryStatus.isPluggedInDock()) {
+ statusString = res.getString(R.string.battery_info_status_charging_dock);
} else {
statusString = res.getString(R.string.battery_info_status_charging_wireless);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index bf9debf5ccce..bf6975714acd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -87,9 +87,11 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
if (DEBUG) {
Log.d(TAG, "Bluetooth service connected");
}
- mService = (BluetoothLeBroadcast) proxy;
- mIsProfileReady = true;
- registerServiceCallBack(mExecutor, mBroadcastCallback);
+ if(!mIsProfileReady) {
+ mService = (BluetoothLeBroadcast) proxy;
+ mIsProfileReady = true;
+ registerServiceCallBack(mExecutor, mBroadcastCallback);
+ }
}
@Override
@@ -97,8 +99,10 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
if (DEBUG) {
Log.d(TAG, "Bluetooth service disconnected");
}
- mIsProfileReady = false;
- unregisterServiceCallBack(mBroadcastCallback);
+ if(mIsProfileReady) {
+ mIsProfileReady = false;
+ unregisterServiceCallBack(mBroadcastCallback);
+ }
}
};
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
index 4939e04dad6a..132a631e25cc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
@@ -135,7 +135,7 @@ public class BatteryStatus {
* @return true if the device is charged
*/
public boolean isCharged() {
- return status == BATTERY_STATUS_FULL || level >= 100;
+ return isCharged(status, level);
}
/**
@@ -177,4 +177,31 @@ public class BatteryStatus {
return "BatteryStatus{status=" + status + ",level=" + level + ",plugged=" + plugged
+ ",health=" + health + ",maxChargingWattage=" + maxChargingWattage + "}";
}
+
+ /**
+ * Whether or not the device is charged. Note that some devices never return 100% for
+ * battery level, so this allows either battery level or status to determine if the
+ * battery is charged.
+ *
+ * @param batteryChangedIntent ACTION_BATTERY_CHANGED intent
+ * @return true if the device is charged
+ */
+ public static boolean isCharged(Intent batteryChangedIntent) {
+ int status = batteryChangedIntent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
+ int level = batteryChangedIntent.getIntExtra(EXTRA_LEVEL, 0);
+ return isCharged(status, level);
+ }
+
+ /**
+ * Whether or not the device is charged. Note that some devices never return 100% for
+ * battery level, so this allows either battery level or status to determine if the
+ * battery is charged.
+ *
+ * @param status values for "status" field in the ACTION_BATTERY_CHANGED Intent
+ * @param level values from 0 to 100
+ * @return true if the device is charged
+ */
+ public static boolean isCharged(int status, int level) {
+ return status == BATTERY_STATUS_FULL || level >= 100;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index d6d73046bed3..281501e6043c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -22,6 +22,7 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
import android.media.RoutingSessionInfo;
import android.os.Build;
import android.text.TextUtils;
@@ -83,6 +84,7 @@ public class LocalMediaManager implements BluetoothCallback {
private InfoMediaManager mInfoMediaManager;
private String mPackageName;
private MediaDevice mOnTransferBluetoothDevice;
+ private AudioManager mAudioManager;
@VisibleForTesting
List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
@@ -126,6 +128,7 @@ public class LocalMediaManager implements BluetoothCallback {
mPackageName = packageName;
mLocalBluetoothManager =
LocalBluetoothManager.getInstance(context, /* onInitCallback= */ null);
+ mAudioManager = context.getSystemService(AudioManager.class);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mLocalBluetoothManager == null) {
Log.e(TAG, "Bluetooth is not supported on this device");
@@ -148,6 +151,7 @@ public class LocalMediaManager implements BluetoothCallback {
mInfoMediaManager = infoMediaManager;
mPackageName = packageName;
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ mAudioManager = context.getSystemService(AudioManager.class);
}
/**
@@ -527,13 +531,16 @@ public class LocalMediaManager implements BluetoothCallback {
synchronized (mMediaDevicesLock) {
mMediaDevices.clear();
mMediaDevices.addAll(devices);
- // Add disconnected bluetooth devices only when phone output device is available.
+ // Add muting expected bluetooth devices only when phone output device is available.
for (MediaDevice device : devices) {
final int type = device.getDeviceType();
if (type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE
|| type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE
|| type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE) {
- mMediaDevices.addAll(buildDisconnectedBluetoothDevice());
+ MediaDevice mutingExpectedDevice = getMutingExpectedDevice();
+ if (mutingExpectedDevice != null) {
+ mMediaDevices.add(mutingExpectedDevice);
+ }
break;
}
}
@@ -552,6 +559,34 @@ public class LocalMediaManager implements BluetoothCallback {
}
}
+ private MediaDevice getMutingExpectedDevice() {
+ if (mBluetoothAdapter == null
+ || mAudioManager.getMutingExpectedDevice() == null) {
+ Log.w(TAG, "BluetoothAdapter is null or muting expected device not exist");
+ return null;
+ }
+ final List<BluetoothDevice> bluetoothDevices =
+ mBluetoothAdapter.getMostRecentlyConnectedDevices();
+ final CachedBluetoothDeviceManager cachedDeviceManager =
+ mLocalBluetoothManager.getCachedDeviceManager();
+ for (BluetoothDevice device : bluetoothDevices) {
+ final CachedBluetoothDevice cachedDevice =
+ cachedDeviceManager.findDevice(device);
+ if (isBondedMediaDevice(cachedDevice) && isMutingExpectedDevice(cachedDevice)) {
+ return new BluetoothMediaDevice(mContext,
+ cachedDevice,
+ null, null, mPackageName);
+ }
+ }
+ return null;
+ }
+
+ private boolean isMutingExpectedDevice(CachedBluetoothDevice cachedDevice) {
+ return mAudioManager.getMutingExpectedDevice() != null
+ && cachedDevice.getAddress().equals(
+ mAudioManager.getMutingExpectedDevice().getAddress());
+ }
+
private List<MediaDevice> buildDisconnectedBluetoothDevice() {
if (mBluetoothAdapter == null) {
Log.w(TAG, "buildDisconnectedBluetoothDevice() BluetoothAdapter is null");
@@ -595,6 +630,13 @@ public class LocalMediaManager implements BluetoothCallback {
return new ArrayList<>(mDisconnectedMediaDevices);
}
+ private boolean isBondedMediaDevice(CachedBluetoothDevice cachedDevice) {
+ return cachedDevice != null
+ && cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED
+ && !cachedDevice.isConnected()
+ && isMediaDevice(cachedDevice);
+ }
+
private boolean isMediaDevice(CachedBluetoothDevice device) {
for (LocalBluetoothProfile profile : device.getConnectableProfiles()) {
if (profile instanceof A2dpProfile || profile instanceof HearingAidProfile ||
diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp
index 64563be78fbb..d463170fae8c 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -25,7 +25,7 @@ android_test {
name: "SettingsLibTests",
defaults: [
"SettingsLibDefaults",
- "framework-wifi-test-defaults"
+ "framework-wifi-test-defaults",
],
certificate: "platform",
@@ -47,6 +47,7 @@ android_test {
"androidx.test.espresso.core",
"mockito-target-minus-junit4",
"truth-prebuilt",
+ "SettingsLibDeviceStateRotationLock",
"SettingsLibSettingsSpinner",
"SettingsLibUsageProgressBarPreference",
],
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 09b2a2e73c5b..336cdd3f259f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -365,6 +365,17 @@ public class UtilsTest {
}
@Test
+ public void getBatteryStatus_chargingDock_returnDockChargingString() {
+ final Intent intent = new Intent();
+ intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
+ intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_DOCK);
+ final Resources resources = mContext.getResources();
+
+ assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false)).isEqualTo(
+ resources.getString(R.string.battery_info_status_charging_dock));
+ }
+
+ @Test
public void getBatteryStatus_chargingWireless_returnWirelessChargingString() {
final Intent intent = new Intent();
intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index f490c87aa4ec..4a104276c18f 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -32,6 +32,7 @@ android_app {
],
static_libs: [
"junit",
+ "SettingsLibDeviceStateRotationLock",
"SettingsLibDisplayDensityUtils",
],
platform_apis: true,
@@ -55,6 +56,7 @@ android_test {
static_libs: [
"androidx.test.rules",
"mockito-target-minus-junit4",
+ "SettingsLibDeviceStateRotationLock",
"SettingsLibDisplayDensityUtils",
"platform-test-annotations",
"truth-prebuilt",
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/DeviceSpecificSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/DeviceSpecificSettings.java
index e425790110aa..3cb143966a7c 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/DeviceSpecificSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/DeviceSpecificSettings.java
@@ -32,5 +32,6 @@ public class DeviceSpecificSettings {
*/
public static final String[] DEVICE_SPECIFIC_SETTINGS_TO_BACKUP = {
Settings.Secure.DISPLAY_DENSITY_FORCED,
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index dc166b4c2349..162bb2c720e8 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -78,6 +78,7 @@ public class GlobalSettings {
Settings.Global.POWER_BUTTON_LONG_PRESS,
Settings.Global.AUTOMATIC_POWER_SAVE_MODE,
Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT,
+ Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED,
Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS,
Settings.Global.USER_PREFERRED_REFRESH_RATE,
Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 5eaf553a2047..ce331607c531 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -37,6 +37,7 @@ public class SecureSettings {
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+ Settings.Secure.ADAPTIVE_CHARGING_ENABLED,
Settings.Secure.ADAPTIVE_SLEEP,
Settings.Secure.CAMERA_AUTOROTATE,
Settings.Secure.AUTOFILL_SERVICE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index acb33c356a8b..e82bf0427509 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -155,6 +155,7 @@ public class GlobalSettingsValidators {
VALIDATORS.put(Global.DEVICE_CONFIG_SYNC_DISABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.AUTOMATIC_POWER_SAVE_MODE, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.ADVANCED_BATTERY_USAGE_AMOUNT, PERCENTAGE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.POWER_BUTTON_LONG_PRESS_DURATION_MS, NONE_NEGATIVE_LONG_VALIDATOR);
VALIDATORS.put(Global.Wearable.HAS_PAY_TOKENS, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 9ee7b654046f..5d773789b206 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -59,6 +59,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.ADAPTIVE_CHARGING_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.CAMERA_AUTOROTATE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.AUTOFILL_SERVICE, NULLABLE_COMPONENT_NAME_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 440bb67dc788..808ea9ede9dc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -41,6 +41,7 @@ import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.LocalePicker;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import java.util.ArrayList;
import java.util.HashMap;
@@ -200,6 +201,9 @@ public class SettingsHelper {
} else if (Settings.Global.POWER_BUTTON_LONG_PRESS.equals(name)) {
setLongPressPowerBehavior(cr, value);
return;
+ } else if (Settings.System.ACCELEROMETER_ROTATION.equals(name)
+ && shouldSkipAutoRotateRestore()) {
+ return;
}
// Default case: write the restored value to settings
@@ -236,6 +240,12 @@ public class SettingsHelper {
}
}
+ private boolean shouldSkipAutoRotateRestore() {
+ // When device state based auto rotation settings are available, let's skip the restoring
+ // of the standard auto rotation settings to avoid conflicting setting values.
+ return DeviceStateRotationLockSettingsManager.isDeviceStateRotationLockEnabled(mContext);
+ }
+
public String onBackupValue(String name, String value) {
// Special processing for backing up ringtones & notification sounds
if (Settings.System.RINGTONE.equals(name)
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index cce515444c1f..b1ce3bf28001 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -110,7 +110,6 @@ public class SettingsBackupTest {
newHashSet(
Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED,
- Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED,
Settings.Global.ADB_ALLOWED_CONNECTION_TIME,
Settings.Global.ADB_ENABLED,
Settings.Global.ADB_WIFI_ENABLED,
@@ -422,6 +421,7 @@ public class SettingsBackupTest {
Settings.Global.RADIO_NFC,
Settings.Global.RADIO_WIFI,
Settings.Global.RADIO_WIMAX,
+ Settings.Global.REMOVE_GUEST_ON_EXIT,
Settings.Global.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS,
Settings.Global.READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT,
Settings.Global.RESTRICTED_NETWORKING_MODE,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
index 4f7b494c0e71..ee76dbf8ce70 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
@@ -73,6 +73,8 @@ public class SettingsHelperTest {
when(mContext.getSystemService(eq(Context.TELEPHONY_SERVICE))).thenReturn(
mTelephonyManager);
when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getApplicationContext()).thenReturn(mContext);
+ when(mContext.getContentResolver()).thenReturn(getContentResolver());
mSettingsHelper = spy(new SettingsHelper(mContext));
}
@@ -305,6 +307,63 @@ public class SettingsHelperTest {
new String[] { "he-IL", "id-ID", "yi" })); // supported
}
+ @Test
+ public void restoreValue_autoRotation_deviceStateAutoRotationDisabled_restoresValue() {
+ when(mResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
+ .thenReturn(new String[]{});
+ int previousValue = 0;
+ int newValue = 1;
+ setAutoRotationSettingValue(previousValue);
+
+ restoreAutoRotationSetting(newValue);
+
+ assertThat(getAutoRotationSettingValue()).isEqualTo(newValue);
+ }
+
+ @Test
+ public void restoreValue_autoRotation_deviceStateAutoRotationEnabled_doesNotRestoreValue() {
+ when(mResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults))
+ .thenReturn(new String[]{"0:1", "1:1"});
+ int previousValue = 0;
+ int newValue = 1;
+ setAutoRotationSettingValue(previousValue);
+
+ restoreAutoRotationSetting(newValue);
+
+ assertThat(getAutoRotationSettingValue()).isEqualTo(previousValue);
+ }
+
+ private int getAutoRotationSettingValue() {
+ return Settings.System.getInt(
+ getContentResolver(),
+ Settings.System.ACCELEROMETER_ROTATION,
+ /* default= */ -1);
+ }
+
+ private void setAutoRotationSettingValue(int value) {
+ Settings.System.putInt(
+ getContentResolver(),
+ Settings.System.ACCELEROMETER_ROTATION,
+ value
+ );
+ }
+
+ private void restoreAutoRotationSetting(int newValue) {
+ mSettingsHelper.restoreValue(
+ mContext,
+ getContentResolver(),
+ new ContentValues(),
+ /* destination= */ Settings.System.CONTENT_URI,
+ /* name= */ Settings.System.ACCELEROMETER_ROTATION,
+ /* value= */ String.valueOf(newValue),
+ /* restoredFromSdkInt= */ 0);
+ }
+
+ private ContentResolver getContentResolver() {
+ return InstrumentationRegistry.getInstrumentation().getTargetContext()
+ .getContentResolver();
+ }
+
private void clearLongPressPowerValues() {
ContentResolver cr = InstrumentationRegistry.getInstrumentation().getTargetContext()
.getContentResolver();
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index f05c1e2e76f2..ffd6b522e394 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -93,6 +93,7 @@ android_library {
"SystemUISharedLib",
"SystemUI-statsd",
"SettingsLib",
+ "androidx.core_core-ktx",
"androidx.viewpager2_viewpager2",
"androidx.legacy_legacy-support-v4",
"androidx.recyclerview_recyclerview",
@@ -109,6 +110,7 @@ android_library {
"androidx.arch.core_core-runtime",
"androidx.lifecycle_lifecycle-common-java8",
"androidx.lifecycle_lifecycle-extensions",
+ "androidx.lifecycle_lifecycle-runtime-ktx",
"androidx.dynamicanimation_dynamicanimation",
"androidx-constraintlayout_constraintlayout",
"androidx.exifinterface_exifinterface",
@@ -217,6 +219,7 @@ android_library {
"androidx.arch.core_core-runtime",
"androidx.lifecycle_lifecycle-common-java8",
"androidx.lifecycle_lifecycle-extensions",
+ "androidx.lifecycle_lifecycle-runtime-ktx",
"androidx.dynamicanimation_dynamicanimation",
"androidx-constraintlayout_constraintlayout",
"androidx.exifinterface_exifinterface",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6f22b49fc6a6..6edf13addbca 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -618,6 +618,17 @@
android:excludeFromRecents="true"
android:visibleToInstantApps="true"/>
+ <activity
+ android:name=".media.MediaProjectionAppSelectorActivity"
+ android:theme="@style/Theme.SystemUI.MediaProjectionAppSelector"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true"
+ android:documentLaunchMode="never"
+ android:relinquishTaskIdentity="true"
+ android:configChanges=
+ "screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
+ android:visibleToInstantApps="true"/>
+
<!-- started from TvNotificationPanel -->
<activity
android:name=".statusbar.tv.notifications.TvNotificationPanelActivity"
@@ -713,7 +724,7 @@
<service
android:name=".dreams.DreamOverlayService"
- android:enabled="@bool/config_dreamOverlayServiceEnabled"
+ android:enabled="false"
android:exported="true" />
<activity android:name=".keyguard.WorkLockActivity"
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index edb5ee30524f..154a6fca9d09 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -52,6 +52,17 @@
]
},
{
+ "name": "SystemUIGoogleScreenshotTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
// Permission indicators
"name": "CtsPermission4TestCases",
"options": [
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index bb6eb78aac65..8ddd430dadbc 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -59,31 +59,31 @@ class ActivityLaunchAnimator(
companion object {
/** The timings when animating a View into an app. */
@JvmField
- val TIMINGS = LaunchAnimator.Timings(
- totalDuration = 500L,
- contentBeforeFadeOutDelay = 0L,
- contentBeforeFadeOutDuration = 150L,
- contentAfterFadeInDelay = 150L,
- contentAfterFadeInDuration = 183L
- )
+ val TIMINGS =
+ LaunchAnimator.Timings(
+ totalDuration = 500L,
+ contentBeforeFadeOutDelay = 0L,
+ contentBeforeFadeOutDuration = 150L,
+ contentAfterFadeInDelay = 150L,
+ contentAfterFadeInDuration = 183L
+ )
/**
* The timings when animating a Dialog into an app. We need to wait at least 200ms before
* showing the app (which is under the dialog window) so that the dialog window dim is fully
* faded out, to avoid flicker.
*/
- val DIALOG_TIMINGS = TIMINGS.copy(
- contentBeforeFadeOutDuration = 200L,
- contentAfterFadeInDelay = 200L
- )
+ val DIALOG_TIMINGS =
+ TIMINGS.copy(contentBeforeFadeOutDuration = 200L, contentAfterFadeInDelay = 200L)
/** The interpolators when animating a View or a dialog into an app. */
- val INTERPOLATORS = LaunchAnimator.Interpolators(
- positionInterpolator = Interpolators.EMPHASIZED,
- positionXInterpolator = createPositionXInterpolator(),
- contentBeforeFadeOutInterpolator = Interpolators.LINEAR_OUT_SLOW_IN,
- contentAfterFadeInInterpolator = PathInterpolator(0f, 0f, 0.6f, 1f)
- )
+ val INTERPOLATORS =
+ LaunchAnimator.Interpolators(
+ positionInterpolator = Interpolators.EMPHASIZED,
+ positionXInterpolator = createPositionXInterpolator(),
+ contentBeforeFadeOutInterpolator = Interpolators.LINEAR_OUT_SLOW_IN,
+ contentAfterFadeInInterpolator = PathInterpolator(0f, 0f, 0.6f, 1f)
+ )
/** Durations & interpolators for the navigation bar fading in & out. */
private const val ANIMATION_DURATION_NAV_FADE_IN = 266L
@@ -98,11 +98,12 @@ class ActivityLaunchAnimator(
private const val LAUNCH_TIMEOUT = 1000L
private fun createPositionXInterpolator(): Interpolator {
- val path = Path().apply {
- moveTo(0f, 0f)
- cubicTo(0.1217f, 0.0462f, 0.15f, 0.4686f, 0.1667f, 0.66f)
- cubicTo(0.1834f, 0.8878f, 0.1667f, 1f, 1f, 1f)
- }
+ val path =
+ Path().apply {
+ moveTo(0f, 0f)
+ cubicTo(0.1217f, 0.0462f, 0.15f, 0.4686f, 0.1667f, 0.66f)
+ cubicTo(0.1834f, 0.8878f, 0.1667f, 1f, 1f, 1f)
+ }
return PathInterpolator(path)
}
}
@@ -150,29 +151,37 @@ class ActivityLaunchAnimator(
return
}
- val callback = this.callback ?: throw IllegalStateException(
- "ActivityLaunchAnimator.callback must be set before using this animator")
+ val callback =
+ this.callback
+ ?: throw IllegalStateException(
+ "ActivityLaunchAnimator.callback must be set before using this animator"
+ )
val runner = Runner(controller)
val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen
// Pass the RemoteAnimationAdapter to the intent starter only if we are not hiding the
// keyguard with the animation
- val animationAdapter = if (!hideKeyguardWithAnimation) {
- RemoteAnimationAdapter(
- runner,
- TIMINGS.totalDuration,
- TIMINGS.totalDuration - 150 /* statusBarTransitionDelay */
- )
- } else {
- null
- }
+ val animationAdapter =
+ if (!hideKeyguardWithAnimation) {
+ RemoteAnimationAdapter(
+ runner,
+ TIMINGS.totalDuration,
+ TIMINGS.totalDuration - 150 /* statusBarTransitionDelay */
+ )
+ } else {
+ null
+ }
// Register the remote animation for the given package to also animate trampoline
// activity launches.
if (packageName != null && animationAdapter != null) {
try {
- ActivityTaskManager.getService().registerRemoteAnimationForNextActivityStart(
- packageName, animationAdapter, null /* launchCookie */)
+ ActivityTaskManager.getService()
+ .registerRemoteAnimationForNextActivityStart(
+ packageName,
+ animationAdapter,
+ null /* launchCookie */
+ )
} catch (e: RemoteException) {
Log.w(TAG, "Unable to register the remote animation", e)
}
@@ -184,12 +193,15 @@ class ActivityLaunchAnimator(
// keyguard.
val willAnimate =
launchResult == ActivityManager.START_TASK_TO_FRONT ||
- launchResult == ActivityManager.START_SUCCESS ||
- (launchResult == ActivityManager.START_DELIVERED_TO_TOP &&
- hideKeyguardWithAnimation)
-
- Log.i(TAG, "launchResult=$launchResult willAnimate=$willAnimate " +
- "hideKeyguardWithAnimation=$hideKeyguardWithAnimation")
+ launchResult == ActivityManager.START_SUCCESS ||
+ (launchResult == ActivityManager.START_DELIVERED_TO_TOP &&
+ hideKeyguardWithAnimation)
+
+ Log.i(
+ TAG,
+ "launchResult=$launchResult willAnimate=$willAnimate " +
+ "hideKeyguardWithAnimation=$hideKeyguardWithAnimation"
+ )
controller.callOnIntentStartedOnMainThread(willAnimate)
// If we expect an animation, post a timeout to cancel it in case the remote animation is
@@ -206,9 +218,7 @@ class ActivityLaunchAnimator(
private fun Controller.callOnIntentStartedOnMainThread(willAnimate: Boolean) {
if (Looper.myLooper() != Looper.getMainLooper()) {
- this.launchContainer.context.mainExecutor.execute {
- this.onIntentStarted(willAnimate)
- }
+ this.launchContainer.context.mainExecutor.execute { this.onIntentStarted(willAnimate) }
} else {
this.onIntentStarted(willAnimate)
}
@@ -246,8 +256,7 @@ class ActivityLaunchAnimator(
}
/** Create a new animation [Runner] controlled by [controller]. */
- @VisibleForTesting
- fun createRunner(controller: Controller): Runner = Runner(controller)
+ @VisibleForTesting fun createRunner(controller: Controller): Runner = Runner(controller)
interface PendingIntentStarter {
/**
@@ -271,19 +280,16 @@ class ActivityLaunchAnimator(
interface Listener {
/** Called when an activity launch animation started. */
- @JvmDefault
- fun onLaunchAnimationStart() {}
+ @JvmDefault fun onLaunchAnimationStart() {}
/**
* Called when an activity launch animation is finished. This will be called if and only if
* [onLaunchAnimationStart] was called earlier.
*/
- @JvmDefault
- fun onLaunchAnimationEnd() {}
+ @JvmDefault fun onLaunchAnimationEnd() {}
/** Called when an activity launch animation made progress. */
- @JvmDefault
- fun onLaunchAnimationProgress(linearProgress: Float) {}
+ @JvmDefault fun onLaunchAnimationProgress(linearProgress: Float) {}
}
/**
@@ -327,6 +333,17 @@ class ActivityLaunchAnimator(
get() = false
/**
+ * Whether the expandable controller by this [Controller] is below the launching window that
+ * is going to be animated.
+ *
+ * This should be `false` when launching an app from the shade or status bar, given that
+ * they are drawn above all apps. This is usually `true` when using this launcher in a
+ * normal app or a launcher, that are drawn below the animating activity/window.
+ */
+ val isBelowAnimatingWindow: Boolean
+ get() = false
+
+ /**
* The intent was started. If [willAnimate] is false, nothing else will happen and the
* animation will not be started.
*/
@@ -394,9 +411,7 @@ class ActivityLaunchAnimator(
return
}
- context.mainExecutor.execute {
- startAnimation(apps, nonApps, iCallback)
- }
+ context.mainExecutor.execute { startAnimation(apps, nonApps, iCallback) }
}
private fun startAnimation(
@@ -408,9 +423,7 @@ class ActivityLaunchAnimator(
Log.d(TAG, "Remote animation started")
}
- val window = apps?.firstOrNull {
- it.mode == RemoteAnimationTarget.MODE_OPENING
- }
+ val window = apps?.firstOrNull { it.mode == RemoteAnimationTarget.MODE_OPENING }
if (window == null) {
Log.i(TAG, "Aborting the animation as no window is opening")
@@ -420,83 +433,100 @@ class ActivityLaunchAnimator(
return
}
- val navigationBar = nonApps?.firstOrNull {
- it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
- }
+ val navigationBar =
+ nonApps?.firstOrNull {
+ it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
+ }
val windowBounds = window.screenSpaceBounds
- val endState = LaunchAnimator.State(
- top = windowBounds.top,
- bottom = windowBounds.bottom,
- left = windowBounds.left,
- right = windowBounds.right
- )
+ val endState =
+ LaunchAnimator.State(
+ top = windowBounds.top,
+ bottom = windowBounds.bottom,
+ left = windowBounds.left,
+ right = windowBounds.right
+ )
val callback = this@ActivityLaunchAnimator.callback!!
- val windowBackgroundColor = window.taskInfo?.let { callback.getBackgroundColor(it) }
- ?: window.backgroundColor
+ val windowBackgroundColor =
+ window.taskInfo?.let { callback.getBackgroundColor(it) } ?: window.backgroundColor
// Make sure we use the modified timings when animating a dialog into an app.
- val launchAnimator = if (controller.isDialogLaunch) {
- dialogToAppAnimator
- } else {
- launchAnimator
- }
+ val launchAnimator =
+ if (controller.isDialogLaunch) {
+ dialogToAppAnimator
+ } else {
+ launchAnimator
+ }
// TODO(b/184121838): We should somehow get the top and bottom radius of the window
// instead of recomputing isExpandingFullyAbove here.
val isExpandingFullyAbove =
launchAnimator.isExpandingFullyAbove(controller.launchContainer, endState)
- val endRadius = if (isExpandingFullyAbove) {
- // Most of the time, expanding fully above the root view means expanding in full
- // screen.
- ScreenDecorationsUtils.getWindowCornerRadius(context)
- } else {
- // This usually means we are in split screen mode, so 2 out of 4 corners will have
- // a radius of 0.
- 0f
- }
+ val endRadius =
+ if (isExpandingFullyAbove) {
+ // Most of the time, expanding fully above the root view means expanding in full
+ // screen.
+ ScreenDecorationsUtils.getWindowCornerRadius(context)
+ } else {
+ // This usually means we are in split screen mode, so 2 out of 4 corners will
+ // have
+ // a radius of 0.
+ 0f
+ }
endState.topCornerRadius = endRadius
endState.bottomCornerRadius = endRadius
// We animate the opening window and delegate the view expansion to [this.controller].
val delegate = this.controller
- val controller = object : LaunchAnimator.Controller by delegate {
- override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
- listeners.forEach { it.onLaunchAnimationStart() }
- delegate.onLaunchAnimationStart(isExpandingFullyAbove)
- }
-
- override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
- listeners.forEach { it.onLaunchAnimationEnd() }
- iCallback?.invoke()
- delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
- }
+ val controller =
+ object : Controller by delegate {
+ override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+ listeners.forEach { it.onLaunchAnimationStart() }
+ delegate.onLaunchAnimationStart(isExpandingFullyAbove)
+ }
- override fun onLaunchAnimationProgress(
- state: LaunchAnimator.State,
- progress: Float,
- linearProgress: Float
- ) {
- // Apply the state to the window only if it is visible, i.e. when the expanding
- // view is *not* visible.
- if (!state.visible) {
- applyStateToWindow(window, state)
+ override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ listeners.forEach { it.onLaunchAnimationEnd() }
+ iCallback?.invoke()
+ delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
}
- navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
- listeners.forEach { it.onLaunchAnimationProgress(linearProgress) }
- delegate.onLaunchAnimationProgress(state, progress, linearProgress)
+ override fun onLaunchAnimationProgress(
+ state: LaunchAnimator.State,
+ progress: Float,
+ linearProgress: Float
+ ) {
+ // Apply the state to the window only if it is visible, i.e. when the
+ // expanding view is *not* visible.
+ if (!state.visible) {
+ applyStateToWindow(window, state, linearProgress)
+ }
+ navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
+
+ listeners.forEach { it.onLaunchAnimationProgress(linearProgress) }
+ delegate.onLaunchAnimationProgress(state, progress, linearProgress)
+ }
}
- }
- animation = launchAnimator.startAnimation(
- controller, endState, windowBackgroundColor, drawHole = true)
+ animation =
+ launchAnimator.startAnimation(
+ controller,
+ endState,
+ windowBackgroundColor,
+ fadeOutWindowBackgroundLayer = !controller.isBelowAnimatingWindow,
+ drawHole = !controller.isBelowAnimatingWindow,
+ )
}
- private fun applyStateToWindow(window: RemoteAnimationTarget, state: LaunchAnimator.State) {
- if (transactionApplierView.viewRootImpl == null) {
- // If the view root we synchronize with was detached, don't apply any transaction
- // (as [SyncRtSurfaceTransactionApplier.scheduleApply] would otherwise throw).
+ private fun applyStateToWindow(
+ window: RemoteAnimationTarget,
+ state: LaunchAnimator.State,
+ linearProgress: Float,
+ ) {
+ if (transactionApplierView.viewRootImpl == null || !window.leash.isValid) {
+ // Don't apply any transaction if the view root we synchronize with was detached or
+ // if the SurfaceControl associated with [window] is not valid, as
+ // [SyncRtSurfaceTransactionApplier.scheduleApply] would otherwise throw.
return
}
@@ -535,19 +565,38 @@ class ActivityLaunchAnimator(
windowCropF.bottom.roundToInt()
)
+ // The alpha of the opening window. If it opens above the expandable, then it should
+ // fade in progressively. Otherwise, it should be fully opaque and will be progressively
+ // revealed as the window background color layer above the window fades out.
+ val alpha =
+ if (controller.isBelowAnimatingWindow) {
+ val windowProgress =
+ LaunchAnimator.getProgress(
+ TIMINGS,
+ linearProgress,
+ TIMINGS.contentAfterFadeInDelay,
+ TIMINGS.contentAfterFadeInDuration
+ )
+
+ INTERPOLATORS.contentAfterFadeInInterpolator.getInterpolation(windowProgress)
+ } else {
+ 1f
+ }
+
// The scale will also be applied to the corner radius, so we divide by the scale to
// keep the original radius. We use the max of (topCornerRadius, bottomCornerRadius) to
// make sure that the window does not draw itself behind the expanding view. This is
// especially important for lock screen animations, where the window is not clipped by
// the shade.
val cornerRadius = maxOf(state.topCornerRadius, state.bottomCornerRadius) / scale
- val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(window.leash)
- .withAlpha(1f)
- .withMatrix(matrix)
- .withWindowCrop(windowCrop)
- .withCornerRadius(cornerRadius)
- .withVisibility(true)
- .build()
+ val params =
+ SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(window.leash)
+ .withAlpha(alpha)
+ .withMatrix(matrix)
+ .withWindowCrop(windowCrop)
+ .withCornerRadius(cornerRadius)
+ .withVisibility(true)
+ .build()
transactionApplier.scheduleApply(params)
}
@@ -557,20 +606,28 @@ class ActivityLaunchAnimator(
state: LaunchAnimator.State,
linearProgress: Float
) {
- if (transactionApplierView.viewRootImpl == null) {
- // If the view root we synchronize with was detached, don't apply any transaction
- // (as [SyncRtSurfaceTransactionApplier.scheduleApply] would otherwise throw).
+ if (transactionApplierView.viewRootImpl == null || !navigationBar.leash.isValid) {
+ // Don't apply any transaction if the view root we synchronize with was detached or
+ // if the SurfaceControl associated with [navigationBar] is not valid, as
+ // [SyncRtSurfaceTransactionApplier.scheduleApply] would otherwise throw.
return
}
- val fadeInProgress = LaunchAnimator.getProgress(TIMINGS, linearProgress,
- ANIMATION_DELAY_NAV_FADE_IN, ANIMATION_DURATION_NAV_FADE_OUT)
+ val fadeInProgress =
+ LaunchAnimator.getProgress(
+ TIMINGS,
+ linearProgress,
+ ANIMATION_DELAY_NAV_FADE_IN,
+ ANIMATION_DURATION_NAV_FADE_OUT
+ )
val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(navigationBar.leash)
if (fadeInProgress > 0) {
matrix.reset()
matrix.setTranslate(
- 0f, (state.top - navigationBar.sourceContainerBounds.top).toFloat())
+ 0f,
+ (state.top - navigationBar.sourceContainerBounds.top).toFloat()
+ )
windowCrop.set(state.left, 0, state.right, state.height)
params
.withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress))
@@ -578,8 +635,13 @@ class ActivityLaunchAnimator(
.withWindowCrop(windowCrop)
.withVisibility(true)
} else {
- val fadeOutProgress = LaunchAnimator.getProgress(TIMINGS, linearProgress, 0,
- ANIMATION_DURATION_NAV_FADE_OUT)
+ val fadeOutProgress =
+ LaunchAnimator.getProgress(
+ TIMINGS,
+ linearProgress,
+ 0,
+ ANIMATION_DURATION_NAV_FADE_OUT
+ )
params.withAlpha(1f - NAV_FADE_OUT_INTERPOLATOR.getInterpolation(fadeOutProgress))
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
index 258ca6bdf79b..b879ba0b1ece 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
@@ -23,4 +23,4 @@ package com.android.systemui.animation
*/
open class DelegateLaunchAnimatorController(
protected val delegate: ActivityLaunchAnimator.Controller
-) : ActivityLaunchAnimator.Controller by delegate \ No newline at end of file
+) : ActivityLaunchAnimator.Controller by delegate
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 63276c971c95..2f36ab9aa93d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -34,6 +34,9 @@ import android.view.WindowInsets
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
import android.widget.FrameLayout
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.jank.InteractionJankMonitor.Configuration
+import com.android.internal.jank.InteractionJankMonitor.CujType
import kotlin.math.roundToInt
private const val TAG = "DialogLaunchAnimator"
@@ -48,8 +51,11 @@ private const val TAG = "DialogLaunchAnimator"
* @see showFromDialog
* @see createActivityLaunchController
*/
-class DialogLaunchAnimator @JvmOverloads constructor(
+class DialogLaunchAnimator
+@JvmOverloads
+constructor(
private val dreamManager: IDreamManager,
+ private val interactionJankMonitor: InteractionJankMonitor,
private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
private val isForTesting: Boolean = false
) {
@@ -58,9 +64,10 @@ class DialogLaunchAnimator @JvmOverloads constructor(
// We use the same interpolator for X and Y axis to make sure the dialog does not move out
// of the screen bounds during the animation.
- private val INTERPOLATORS = ActivityLaunchAnimator.INTERPOLATORS.copy(
- positionXInterpolator = ActivityLaunchAnimator.INTERPOLATORS.positionInterpolator
- )
+ private val INTERPOLATORS =
+ ActivityLaunchAnimator.INTERPOLATORS.copy(
+ positionXInterpolator = ActivityLaunchAnimator.INTERPOLATORS.positionInterpolator
+ )
private val TAG_LAUNCH_ANIMATION_RUNNING = R.id.tag_launch_animation_running
}
@@ -89,18 +96,22 @@ class DialogLaunchAnimator @JvmOverloads constructor(
fun showFromView(
dialog: Dialog,
view: View,
- animateBackgroundBoundsChange: Boolean = false
+ cuj: DialogCuj? = null,
+ animateBackgroundBoundsChange: Boolean = false,
) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw IllegalStateException(
"showFromView must be called from the main thread and dialog must be created in " +
- "the main thread")
+ "the main thread"
+ )
}
// If the view we are launching from belongs to another dialog, then this means the caller
// intent is to launch a dialog from another dialog.
- val animatedParent = openedDialogs
- .firstOrNull { it.dialog.window.decorView.viewRootImpl == view.viewRootImpl }
+ val animatedParent =
+ openedDialogs.firstOrNull {
+ it.dialog.window.decorView.viewRootImpl == view.viewRootImpl
+ }
val animateFrom = animatedParent?.dialogContentWithBackground ?: view
// Make sure we don't run the launch animation from the same view twice at the same time.
@@ -112,16 +123,19 @@ class DialogLaunchAnimator @JvmOverloads constructor(
animateFrom.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true)
- val animatedDialog = AnimatedDialog(
+ val animatedDialog =
+ AnimatedDialog(
launchAnimator,
dreamManager,
+ interactionJankMonitor,
animateFrom,
onDialogDismissed = { openedDialogs.remove(it) },
dialog = dialog,
animateBackgroundBoundsChange,
animatedParent,
- isForTesting
- )
+ isForTesting,
+ cuj
+ )
openedDialogs.add(animatedDialog)
animatedDialog.start()
@@ -136,15 +150,16 @@ class DialogLaunchAnimator @JvmOverloads constructor(
fun showFromDialog(
dialog: Dialog,
animateFrom: Dialog,
+ cuj: DialogCuj? = null,
animateBackgroundBoundsChange: Boolean = false
) {
- val view = openedDialogs
- .firstOrNull { it.dialog == animateFrom }
- ?.dialogContentWithBackground
- ?: throw IllegalStateException(
- "The animateFrom dialog was not animated using " +
- "DialogLaunchAnimator.showFrom(View|Dialog)")
- showFromView(dialog, view, animateBackgroundBoundsChange)
+ val view =
+ openedDialogs.firstOrNull { it.dialog == animateFrom }?.dialogContentWithBackground
+ ?: throw IllegalStateException(
+ "The animateFrom dialog was not animated using " +
+ "DialogLaunchAnimator.showFrom(View|Dialog)")
+ showFromView(
+ dialog, view, animateBackgroundBoundsChange = animateBackgroundBoundsChange, cuj = cuj)
}
/**
@@ -166,9 +181,11 @@ class DialogLaunchAnimator @JvmOverloads constructor(
view: View,
cujType: Int? = null
): ActivityLaunchAnimator.Controller? {
- val animatedDialog = openedDialogs
- .firstOrNull { it.dialog.window.decorView.viewRootImpl == view.viewRootImpl }
- ?: return null
+ val animatedDialog =
+ openedDialogs.firstOrNull {
+ it.dialog.window.decorView.viewRootImpl == view.viewRootImpl
+ }
+ ?: return null
// At this point, we know that the intent of the caller is to dismiss the dialog to show
// an app, so we disable the exit animation into the touch surface because we will never
@@ -231,7 +248,7 @@ class DialogLaunchAnimator @JvmOverloads constructor(
}
private fun disableDialogDismiss() {
- dialog.setDismissOverride { /* Do nothing */ }
+ dialog.setDismissOverride { /* Do nothing */}
}
private fun enableDialogDismiss() {
@@ -248,9 +265,9 @@ class DialogLaunchAnimator @JvmOverloads constructor(
* Ensure that all dialogs currently shown won't animate into their touch surface when
* dismissed.
*
- * This is a temporary API meant to be called right before we both dismiss a dialog and start
- * an activity, which currently does not look good if we animate the dialog into the touch
- * surface at the same time as the activity starts.
+ * This is a temporary API meant to be called right before we both dismiss a dialog and start an
+ * activity, which currently does not look good if we animate the dialog into the touch surface
+ * at the same time as the activity starts.
*
* TODO(b/193634619): Remove this function and animate dialog into opening activity instead.
*/
@@ -270,16 +287,24 @@ class DialogLaunchAnimator @JvmOverloads constructor(
}
}
+/**
+ * The CUJ interaction associated with opening the dialog.
+ *
+ * The optional tag indicates the specific dialog being opened.
+ */
+data class DialogCuj(@CujType val cujType: Int, val tag: String? = null)
+
private class AnimatedDialog(
private val launchAnimator: LaunchAnimator,
private val dreamManager: IDreamManager,
+ private val interactionJankMonitor: InteractionJankMonitor,
/** The view that triggered the dialog after being tapped. */
var touchSurface: View,
/**
- * A callback that will be called with this [AnimatedDialog] after the dialog was
- * dismissed and the exit animation is done.
+ * A callback that will be called with this [AnimatedDialog] after the dialog was dismissed and
+ * the exit animation is done.
*/
private val onDialogDismissed: (AnimatedDialog) -> Unit,
@@ -295,14 +320,17 @@ private class AnimatedDialog(
/**
* Whether synchronization should be disabled, which can be useful if we are running in a test.
*/
- private val forceDisableSynchronization: Boolean
+ private val forceDisableSynchronization: Boolean,
+
+ /** Interaction to which the dialog animation is associated. */
+ private val cuj: DialogCuj? = null
) {
/**
* The DecorView of this dialog window.
*
* Note that we access this DecorView lazily to avoid accessing it before the dialog is created,
* which can sometimes cause crashes (e.g. with the Cast dialog).
- */
+ */
private val decorView by lazy { dialog.window!!.decorView as ViewGroup }
/**
@@ -313,9 +341,7 @@ private class AnimatedDialog(
*/
var dialogContentWithBackground: ViewGroup? = null
- /**
- * The background color of [dialog], taking into consideration its window background color.
- */
+ /** The background color of [dialog], taking into consideration its window background color. */
private var originalDialogBackgroundColor = Color.BLACK
/**
@@ -333,11 +359,12 @@ private class AnimatedDialog(
private var isOriginalDialogViewLaidOut = false
/** A layout listener to animate the dialog height change. */
- private val backgroundLayoutListener = if (animateBackgroundBoundsChange) {
- AnimatedBoundsLayoutListener()
- } else {
- null
- }
+ private val backgroundLayoutListener =
+ if (animateBackgroundBoundsChange) {
+ AnimatedBoundsLayoutListener()
+ } else {
+ null
+ }
/*
* A layout listener in case the dialog (window) size changes (for instance because of a
@@ -346,6 +373,14 @@ private class AnimatedDialog(
private var decorViewLayoutListener: View.OnLayoutChangeListener? = null
fun start() {
+ if (cuj != null) {
+ val config = Configuration.Builder.withView(cuj.cujType, touchSurface)
+ if (cuj.tag != null) {
+ config.setTag(cuj.tag)
+ }
+ interactionJankMonitor.begin(config)
+ }
+
// Create the dialog so that its onCreate() method is called, which usually sets the dialog
// content.
dialog.create()
@@ -353,99 +388,117 @@ private class AnimatedDialog(
val window = dialog.window!!
val isWindowFullScreen =
window.attributes.width == MATCH_PARENT && window.attributes.height == MATCH_PARENT
- val dialogContentWithBackground = if (isWindowFullScreen) {
- // If the dialog window is already fullscreen, then we look for the first ViewGroup that
- // has a background (and is not the DecorView, which always has a background) and
- // animate towards that ViewGroup given that this is probably what represents the actual
- // dialog view.
- var viewGroupWithBackground: ViewGroup? = null
- for (i in 0 until decorView.childCount) {
- viewGroupWithBackground = findFirstViewGroupWithBackground(decorView.getChildAt(i))
- if (viewGroupWithBackground != null) {
- break
+ val dialogContentWithBackground =
+ if (isWindowFullScreen) {
+ // If the dialog window is already fullscreen, then we look for the first ViewGroup
+ // that has a background (and is not the DecorView, which always has a background)
+ // and animate towards that ViewGroup given that this is probably what represents
+ // the actual dialog view.
+ var viewGroupWithBackground: ViewGroup? = null
+ for (i in 0 until decorView.childCount) {
+ viewGroupWithBackground =
+ findFirstViewGroupWithBackground(decorView.getChildAt(i))
+ if (viewGroupWithBackground != null) {
+ break
+ }
}
- }
-
- // Animate that view with the background. Throw if we didn't find one, because otherwise
- // it's not clear what we should animate.
- viewGroupWithBackground
- ?: throw IllegalStateException("Unable to find ViewGroup with background")
- } else {
- // We will make the dialog window (and therefore its DecorView) fullscreen to make it
- // possible to animate outside its bounds.
- //
- // Before that, we add a new View as a child of the DecorView with the same size and
- // gravity as that DecorView, then we add all original children of the DecorView to that
- // new View. Finally we remove the background of the DecorView and add it to the new
- // View, then we make the DecorView fullscreen. This new View now acts as a fake (non
- // fullscreen) window.
- //
- // On top of that, we also add a fullscreen transparent background between the DecorView
- // and the view that we added so that we can dismiss the dialog when this view is
- // clicked. This is necessary because DecorView overrides onTouchEvent and therefore we
- // can't set the click listener directly on the (now fullscreen) DecorView.
- val fullscreenTransparentBackground = FrameLayout(dialog.context)
- decorView.addView(
- fullscreenTransparentBackground,
- 0 /* index */,
- FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
- )
- val dialogContentWithBackground = FrameLayout(dialog.context)
- dialogContentWithBackground.background = decorView.background
-
- // Make the window background transparent. Note that setting the window (or DecorView)
- // background drawable to null leads to issues with background color (not being
- // transparent) or with insets that are not refreshed. Therefore we need to set it to
- // something not null, hence we are using android.R.color.transparent here.
- window.setBackgroundDrawableResource(android.R.color.transparent)
-
- // Close the dialog when clicking outside of it.
- fullscreenTransparentBackground.setOnClickListener { dialog.dismiss() }
- dialogContentWithBackground.isClickable = true
-
- // Make sure the transparent and dialog backgrounds are not focusable by accessibility
- // features.
- fullscreenTransparentBackground.importantForAccessibility =
- View.IMPORTANT_FOR_ACCESSIBILITY_NO
- dialogContentWithBackground.importantForAccessibility =
- View.IMPORTANT_FOR_ACCESSIBILITY_NO
-
- fullscreenTransparentBackground.addView(
- dialogContentWithBackground,
- FrameLayout.LayoutParams(
- window.attributes.width,
- window.attributes.height,
- window.attributes.gravity
+ // Animate that view with the background. Throw if we didn't find one, because
+ // otherwise
+ // it's not clear what we should animate.
+ viewGroupWithBackground
+ ?: throw IllegalStateException("Unable to find ViewGroup with background")
+ } else {
+ // We will make the dialog window (and therefore its DecorView) fullscreen to make
+ // it possible to animate outside its bounds.
+ //
+ // Before that, we add a new View as a child of the DecorView with the same size and
+ // gravity as that DecorView, then we add all original children of the DecorView to
+ // that new View. Finally we remove the background of the DecorView and add it to
+ // the new View, then we make the DecorView fullscreen. This new View now acts as a
+ // fake (non fullscreen) window.
+ //
+ // On top of that, we also add a fullscreen transparent background between the
+ // DecorView and the view that we added so that we can dismiss the dialog when this
+ // view is clicked. This is necessary because DecorView overrides onTouchEvent and
+ // therefore we can't set the click listener directly on the (now fullscreen)
+ // DecorView.
+ val fullscreenTransparentBackground = FrameLayout(dialog.context)
+ decorView.addView(
+ fullscreenTransparentBackground,
+ 0 /* index */,
+ FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
)
- )
- // Move all original children of the DecorView to the new View we just added.
- for (i in 1 until decorView.childCount) {
- val view = decorView.getChildAt(1)
- decorView.removeViewAt(1)
- dialogContentWithBackground.addView(view)
- }
+ val dialogContentWithBackground = FrameLayout(dialog.context)
+ dialogContentWithBackground.background = decorView.background
+
+ // Make the window background transparent. Note that setting the window (or
+ // DecorView) background drawable to null leads to issues with background color (not
+ // being transparent) or with insets that are not refreshed. Therefore we need to
+ // set it to something not null, hence we are using android.R.color.transparent
+ // here.
+ window.setBackgroundDrawableResource(android.R.color.transparent)
+
+ // Close the dialog when clicking outside of it.
+ fullscreenTransparentBackground.setOnClickListener { dialog.dismiss() }
+ dialogContentWithBackground.isClickable = true
+
+ // Make sure the transparent and dialog backgrounds are not focusable by
+ // accessibility
+ // features.
+ fullscreenTransparentBackground.importantForAccessibility =
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ dialogContentWithBackground.importantForAccessibility =
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO
+
+ fullscreenTransparentBackground.addView(
+ dialogContentWithBackground,
+ FrameLayout.LayoutParams(
+ window.attributes.width,
+ window.attributes.height,
+ window.attributes.gravity
+ )
+ )
- // Make the window fullscreen and add a layout listener to ensure it stays fullscreen.
- window.setLayout(MATCH_PARENT, MATCH_PARENT)
- decorViewLayoutListener = View.OnLayoutChangeListener {
- v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
- if (window.attributes.width != MATCH_PARENT ||
- window.attributes.height != MATCH_PARENT) {
- // The dialog size changed, copy its size to dialogContentWithBackground and
- // make the dialog window full screen again.
- val layoutParams = dialogContentWithBackground.layoutParams
- layoutParams.width = window.attributes.width
- layoutParams.height = window.attributes.height
- dialogContentWithBackground.layoutParams = layoutParams
- window.setLayout(MATCH_PARENT, MATCH_PARENT)
+ // Move all original children of the DecorView to the new View we just added.
+ for (i in 1 until decorView.childCount) {
+ val view = decorView.getChildAt(1)
+ decorView.removeViewAt(1)
+ dialogContentWithBackground.addView(view)
}
- }
- decorView.addOnLayoutChangeListener(decorViewLayoutListener)
- dialogContentWithBackground
- }
+ // Make the window fullscreen and add a layout listener to ensure it stays
+ // fullscreen.
+ window.setLayout(MATCH_PARENT, MATCH_PARENT)
+ decorViewLayoutListener =
+ View.OnLayoutChangeListener {
+ v,
+ left,
+ top,
+ right,
+ bottom,
+ oldLeft,
+ oldTop,
+ oldRight,
+ oldBottom ->
+ if (
+ window.attributes.width != MATCH_PARENT ||
+ window.attributes.height != MATCH_PARENT
+ ) {
+ // The dialog size changed, copy its size to dialogContentWithBackground
+ // and make the dialog window full screen again.
+ val layoutParams = dialogContentWithBackground.layoutParams
+ layoutParams.width = window.attributes.width
+ layoutParams.height = window.attributes.height
+ dialogContentWithBackground.layoutParams = layoutParams
+ window.setLayout(MATCH_PARENT, MATCH_PARENT)
+ }
+ }
+ decorView.addOnLayoutChangeListener(decorViewLayoutListener)
+
+ dialogContentWithBackground
+ }
this.dialogContentWithBackground = dialogContentWithBackground
dialogContentWithBackground.setTag(R.id.tag_dialog_background, true)
@@ -453,7 +506,8 @@ private class AnimatedDialog(
originalDialogBackgroundColor =
GhostedViewLaunchAnimatorController.findGradientDrawable(background)
?.color
- ?.defaultColor ?: Color.BLACK
+ ?.defaultColor
+ ?: Color.BLACK
// Make the background view invisible until we start the animation. We use the transition
// visibility like GhostView does so that we don't mess up with the accessibility tree (see
@@ -461,11 +515,20 @@ private class AnimatedDialog(
dialogContentWithBackground.setTransitionVisibility(View.INVISIBLE)
// Make sure the dialog is visible instantly and does not do any window animation.
- window.attributes.windowAnimations = R.style.Animation_LaunchAnimation
+ val attributes = window.attributes
+ attributes.windowAnimations = R.style.Animation_LaunchAnimation
// Ensure that the animation is not clipped by the display cut-out when animating this
// dialog into an app.
- window.attributes.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ attributes.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+
+ // Ensure that the animation is not clipped by the navigation/task bars when animating this
+ // dialog into an app.
+ val wasFittingNavigationBars =
+ attributes.fitInsetsTypes and WindowInsets.Type.navigationBars() != 0
+ attributes.fitInsetsTypes =
+ attributes.fitInsetsTypes and WindowInsets.Type.navigationBars().inv()
+
window.attributes = window.attributes
// We apply the insets ourselves to make sure that the paddings are set on the correct
@@ -473,30 +536,38 @@ private class AnimatedDialog(
window.setDecorFitsSystemWindows(false)
val viewWithInsets = (dialogContentWithBackground.parent as ViewGroup)
viewWithInsets.setOnApplyWindowInsetsListener { view, windowInsets ->
- val insets = windowInsets.getInsets(WindowInsets.Type.displayCutout())
+ val type = if (wasFittingNavigationBars) {
+ WindowInsets.Type.displayCutout() or WindowInsets.Type.navigationBars()
+ } else {
+ WindowInsets.Type.displayCutout()
+ }
+
+ val insets = windowInsets.getInsets(type)
view.setPadding(insets.left, insets.top, insets.right, insets.bottom)
WindowInsets.CONSUMED
}
// Start the animation once the background view is properly laid out.
- dialogContentWithBackground.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
- override fun onLayoutChange(
- v: View,
- left: Int,
- top: Int,
- right: Int,
- bottom: Int,
- oldLeft: Int,
- oldTop: Int,
- oldRight: Int,
- oldBottom: Int
- ) {
- dialogContentWithBackground.removeOnLayoutChangeListener(this)
-
- isOriginalDialogViewLaidOut = true
- maybeStartLaunchAnimation()
+ dialogContentWithBackground.addOnLayoutChangeListener(
+ object : View.OnLayoutChangeListener {
+ override fun onLayoutChange(
+ v: View,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ dialogContentWithBackground.removeOnLayoutChangeListener(this)
+
+ isOriginalDialogViewLaidOut = true
+ maybeStartLaunchAnimation()
+ }
}
- })
+ )
// Disable the dim. We will enable it once we start the animation.
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
@@ -522,10 +593,12 @@ private class AnimatedDialog(
// Create a ghost of the touch surface (which will make the touch surface invisible) and add
// it to the host dialog. We trigger a one off synchronization to make sure that this is
// done in sync between the two different windows.
- synchronizeNextDraw(then = {
- isTouchSurfaceGhostDrawn = true
- maybeStartLaunchAnimation()
- })
+ synchronizeNextDraw(
+ then = {
+ isTouchSurfaceGhostDrawn = true
+ maybeStartLaunchAnimation()
+ }
+ )
GhostView.addGhost(touchSurface, decorView)
// The ghost of the touch surface was just created, so the touch surface is currently
@@ -587,14 +660,13 @@ private class AnimatedDialog(
onLaunchAnimationEnd = {
touchSurface.setTag(R.id.tag_launch_animation_running, null)
- // We hide the touch surface when the dialog is showing. We will make this
- // view visible again when dismissing the dialog.
+ // We hide the touch surface when the dialog is showing. We will make this view
+ // visible again when dismissing the dialog.
touchSurface.visibility = View.INVISIBLE
isLaunching = false
- // dismiss was called during the animation, dismiss again now to actually
- // dismiss.
+ // dismiss was called during the animation, dismiss again now to actually dismiss.
if (dismissRequested) {
dialog.dismiss()
}
@@ -603,9 +675,11 @@ private class AnimatedDialog(
// at the end of the launch animation, because the lauch animation already correctly
// handles bounds changes.
if (backgroundLayoutListener != null) {
- dialogContentWithBackground!!
- .addOnLayoutChangeListener(backgroundLayoutListener)
+ dialogContentWithBackground!!.addOnLayoutChangeListener(
+ backgroundLayoutListener
+ )
}
+ cuj?.run { interactionJankMonitor.end(cujType) }
}
)
}
@@ -681,16 +755,19 @@ private class AnimatedDialog(
dialogContentWithBackground.visibility = View.INVISIBLE
if (backgroundLayoutListener != null) {
- dialogContentWithBackground
- .removeOnLayoutChangeListener(backgroundLayoutListener)
+ dialogContentWithBackground.removeOnLayoutChangeListener(
+ backgroundLayoutListener
+ )
}
// Make sure that the removal of the ghost and making the touch surface visible is
// done at the same time.
- synchronizeNextDraw(then = {
- onAnimationFinished(true /* instantDismiss */)
- onDialogDismissed(this@AnimatedDialog)
- })
+ synchronizeNextDraw(
+ then = {
+ onAnimationFinished(true /* instantDismiss */)
+ onDialogDismissed(this@AnimatedDialog)
+ }
+ )
}
)
}
@@ -710,55 +787,56 @@ private class AnimatedDialog(
endViewController.launchContainer = decorView
val endState = endViewController.createAnimatorState()
- val controller = object : LaunchAnimator.Controller {
- override var launchContainer: ViewGroup
- get() = startViewController.launchContainer
- set(value) {
- startViewController.launchContainer = value
- endViewController.launchContainer = value
+ val controller =
+ object : LaunchAnimator.Controller {
+ override var launchContainer: ViewGroup
+ get() = startViewController.launchContainer
+ set(value) {
+ startViewController.launchContainer = value
+ endViewController.launchContainer = value
+ }
+
+ override fun createAnimatorState(): LaunchAnimator.State {
+ return startViewController.createAnimatorState()
}
- override fun createAnimatorState(): LaunchAnimator.State {
- return startViewController.createAnimatorState()
- }
+ override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+ // During launch, onLaunchAnimationStart will be used to remove the temporary
+ // touch surface ghost so it is important to call this before calling
+ // onLaunchAnimationStart on the controller (which will create its own ghost).
+ onLaunchAnimationStart()
- override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
- // During launch, onLaunchAnimationStart will be used to remove the temporary touch
- // surface ghost so it is important to call this before calling
- // onLaunchAnimationStart on the controller (which will create its own ghost).
- onLaunchAnimationStart()
+ startViewController.onLaunchAnimationStart(isExpandingFullyAbove)
+ endViewController.onLaunchAnimationStart(isExpandingFullyAbove)
+ }
- startViewController.onLaunchAnimationStart(isExpandingFullyAbove)
- endViewController.onLaunchAnimationStart(isExpandingFullyAbove)
- }
+ override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ startViewController.onLaunchAnimationEnd(isExpandingFullyAbove)
+ endViewController.onLaunchAnimationEnd(isExpandingFullyAbove)
- override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
- startViewController.onLaunchAnimationEnd(isExpandingFullyAbove)
- endViewController.onLaunchAnimationEnd(isExpandingFullyAbove)
-
- onLaunchAnimationEnd()
- }
+ onLaunchAnimationEnd()
+ }
- override fun onLaunchAnimationProgress(
- state: LaunchAnimator.State,
- progress: Float,
- linearProgress: Float
- ) {
- startViewController.onLaunchAnimationProgress(state, progress, linearProgress)
-
- // The end view is visible only iff the starting view is not visible.
- state.visible = !state.visible
- endViewController.onLaunchAnimationProgress(state, progress, linearProgress)
-
- // If the dialog content is complex, its dimension might change during the launch
- // animation. The animation end position might also change during the exit
- // animation, for instance when locking the phone when the dialog is open. Therefore
- // we update the end state to the new position/size. Usually the dialog dimension or
- // position will change in the early frames, so changing the end state shouldn't
- // really be noticeable.
- endViewController.fillGhostedViewState(endState)
+ override fun onLaunchAnimationProgress(
+ state: LaunchAnimator.State,
+ progress: Float,
+ linearProgress: Float
+ ) {
+ startViewController.onLaunchAnimationProgress(state, progress, linearProgress)
+
+ // The end view is visible only iff the starting view is not visible.
+ state.visible = !state.visible
+ endViewController.onLaunchAnimationProgress(state, progress, linearProgress)
+
+ // If the dialog content is complex, its dimension might change during the
+ // launch animation. The animation end position might also change during the
+ // exit animation, for instance when locking the phone when the dialog is open.
+ // Therefore we update the end state to the new position/size. Usually the
+ // dialog dimension or position will change in the early frames, so changing the
+ // end state shouldn't really be noticeable.
+ endViewController.fillGhostedViewState(endState)
+ }
}
- }
launchAnimator.startAnimation(controller, endState, originalDialogBackgroundColor)
}
@@ -791,7 +869,7 @@ private class AnimatedDialog(
return (touchSurface.parent as? View)?.isShown ?: true
}
- /** A layout listener to animate the change of bounds of the dialog background. */
+ /** A layout listener to animate the change of bounds of the dialog background. */
class AnimatedBoundsLayoutListener : View.OnLayoutChangeListener {
companion object {
private const val ANIMATION_DURATION = 500L
@@ -836,32 +914,35 @@ private class AnimatedDialog(
currentAnimator?.cancel()
currentAnimator = null
- val animator = ValueAnimator.ofFloat(0f, 1f).apply {
- duration = ANIMATION_DURATION
- interpolator = Interpolators.STANDARD
-
- addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- currentAnimator = null
+ val animator =
+ ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = ANIMATION_DURATION
+ interpolator = Interpolators.STANDARD
+
+ addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ currentAnimator = null
+ }
+ }
+ )
+
+ addUpdateListener { animatedValue ->
+ val progress = animatedValue.animatedFraction
+
+ // Compute new bounds.
+ bounds.left = MathUtils.lerp(startLeft, left, progress).roundToInt()
+ bounds.top = MathUtils.lerp(startTop, top, progress).roundToInt()
+ bounds.right = MathUtils.lerp(startRight, right, progress).roundToInt()
+ bounds.bottom = MathUtils.lerp(startBottom, bottom, progress).roundToInt()
+
+ // Set the new bounds.
+ view.left = bounds.left
+ view.top = bounds.top
+ view.right = bounds.right
+ view.bottom = bounds.bottom
}
- })
-
- addUpdateListener { animatedValue ->
- val progress = animatedValue.animatedFraction
-
- // Compute new bounds.
- bounds.left = MathUtils.lerp(startLeft, left, progress).roundToInt()
- bounds.top = MathUtils.lerp(startTop, top, progress).roundToInt()
- bounds.right = MathUtils.lerp(startRight, right, progress).roundToInt()
- bounds.bottom = MathUtils.lerp(startBottom, bottom, progress).roundToInt()
-
- // Set the new bounds.
- view.left = bounds.left
- view.top = bounds.top
- view.right = bounds.right
- view.bottom = bounds.bottom
}
- }
currentAnimator = animator
animator.start()
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 3f7e0f0fb527..eb000ad312d7 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -49,17 +49,16 @@ private const val TAG = "GhostedViewLaunchAnimatorController"
* Note: Avoid instantiating this directly and call [ActivityLaunchAnimator.Controller.fromView]
* whenever possible instead.
*/
-open class GhostedViewLaunchAnimatorController(
+open class GhostedViewLaunchAnimatorController @JvmOverloads constructor(
/** The view that will be ghosted and from which the background will be extracted. */
private val ghostedView: View,
/** The [InteractionJankMonitor.CujType] associated to this animation. */
private val cujType: Int? = null,
- private var interactionJankMonitor: InteractionJankMonitor? = null
+ private var interactionJankMonitor: InteractionJankMonitor =
+ InteractionJankMonitor.getInstance(),
) : ActivityLaunchAnimator.Controller {
- constructor(view: View, type: Int) : this(view, type, null)
-
/** The container to which we will add the ghost view and expanding background. */
override var launchContainer = ghostedView.rootView as ViewGroup
private val launchContainerOverlay: ViewGroupOverlay
@@ -105,9 +104,7 @@ open class GhostedViewLaunchAnimatorController(
}
// Perform a BFS to find the largest View with background.
- val views = LinkedList<View>().apply {
- add(view)
- }
+ val views = LinkedList<View>().apply { add(view) }
while (views.isNotEmpty()) {
val v = views.removeFirst()
@@ -161,10 +158,11 @@ open class GhostedViewLaunchAnimatorController(
}
override fun createAnimatorState(): LaunchAnimator.State {
- val state = LaunchAnimator.State(
- topCornerRadius = getCurrentTopCornerRadius(),
- bottomCornerRadius = getCurrentBottomCornerRadius()
- )
+ val state =
+ LaunchAnimator.State(
+ topCornerRadius = getCurrentTopCornerRadius(),
+ bottomCornerRadius = getCurrentBottomCornerRadius()
+ )
fillGhostedViewState(state)
return state
}
@@ -204,7 +202,7 @@ open class GhostedViewLaunchAnimatorController(
val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX
matrix.getValues(initialGhostViewMatrixValues)
- cujType?.let { interactionJankMonitor?.begin(ghostedView, it) }
+ cujType?.let { interactionJankMonitor.begin(ghostedView, it) }
}
override fun onLaunchAnimationProgress(
@@ -255,13 +253,14 @@ open class GhostedViewLaunchAnimatorController(
launchContainer.getLocationOnScreen(launchContainerLocation)
ghostViewMatrix.postScale(
- scale, scale,
+ scale,
+ scale,
ghostedViewState.centerX - launchContainerLocation[0],
ghostedViewState.centerY - launchContainerLocation[1]
)
ghostViewMatrix.postTranslate(
- (leftChange + rightChange) / 2f,
- (topChange + bottomChange) / 2f
+ (leftChange + rightChange) / 2f,
+ (topChange + bottomChange) / 2f
)
ghostView.animationMatrix = ghostViewMatrix
@@ -289,7 +288,7 @@ open class GhostedViewLaunchAnimatorController(
return
}
- cujType?.let { interactionJankMonitor?.end(it) }
+ cujType?.let { interactionJankMonitor.end(it) }
backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
index a4c5c3025220..9668066be125 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
@@ -34,10 +34,7 @@ import kotlin.math.roundToInt
private const val TAG = "LaunchAnimator"
/** A base class to animate a window launch (activity or dialog) from a view . */
-class LaunchAnimator(
- private val timings: Timings,
- private val interpolators: Interpolators
-) {
+class LaunchAnimator(private val timings: Timings, private val interpolators: Interpolators) {
companion object {
internal const val DEBUG = false
private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
@@ -75,10 +72,10 @@ class LaunchAnimator(
* with the opening window.
*
* This will be used to:
- * - Get the associated [Context].
- * - Compute whether we are expanding fully above the launch container.
- * - Get to overlay to which we initially put the window background layer, until the
- * opening window is made visible (see [openingWindowSyncView]).
+ * - Get the associated [Context].
+ * - Compute whether we are expanding fully above the launch container.
+ * - Get to overlay to which we initially put the window background layer, until the opening
+ * window is made visible (see [openingWindowSyncView]).
*
* This container can be changed to force this [Controller] to animate the expanding view
* inside a different location, for instance to ensure correct layering during the
@@ -132,7 +129,6 @@ class LaunchAnimator(
var bottom: Int = 0,
var left: Int = 0,
var right: Int = 0,
-
var topCornerRadius: Float = 0f,
var bottomCornerRadius: Float = 0f
) {
@@ -202,18 +198,20 @@ class LaunchAnimator(
)
/**
- * Start a launch animation controlled by [controller] towards [endState]. An intermediary
- * layer with [windowBackgroundColor] will fade in then fade out above the expanding view, and
- * should be the same background color as the opening (or closing) window. If [drawHole] is
- * true, then this intermediary layer will be drawn with SRC blending mode while it fades out.
+ * Start a launch animation controlled by [controller] towards [endState]. An intermediary layer
+ * with [windowBackgroundColor] will fade in then (optionally) fade out above the expanding
+ * view, and should be the same background color as the opening (or closing) window.
*
- * TODO(b/184121838): Remove [drawHole] and instead make the StatusBar draw this hole instead.
+ * If [fadeOutWindowBackgroundLayer] is true, then this intermediary layer will fade out during
+ * the second half of the animation, and will have SRC blending mode (ultimately punching a hole
+ * in the [launch container][Controller.launchContainer]) iff [drawHole] is true.
*/
fun startAnimation(
controller: Controller,
endState: State,
windowBackgroundColor: Int,
- drawHole: Boolean = false
+ fadeOutWindowBackgroundLayer: Boolean = true,
+ drawHole: Boolean = false,
): Animation {
val state = controller.createAnimatorState()
@@ -238,8 +236,12 @@ class LaunchAnimator(
val endBottomCornerRadius = endState.bottomCornerRadius
fun maybeUpdateEndState() {
- if (endTop != endState.top || endBottom != endState.bottom ||
- endLeft != endState.left || endRight != endState.right) {
+ if (
+ endTop != endState.top ||
+ endBottom != endState.bottom ||
+ endLeft != endState.left ||
+ endRight != endState.right
+ ) {
endTop = endState.top
endBottom = endState.bottom
endLeft = endState.left
@@ -256,10 +258,11 @@ class LaunchAnimator(
// color, which is usually the same color of the app background. We first fade in this layer
// to hide the expanding view, then we fade it out with SRC mode to draw a hole in the
// launch container and reveal the opening window.
- val windowBackgroundLayer = GradientDrawable().apply {
- setColor(windowBackgroundColor)
- alpha = 0
- }
+ val windowBackgroundLayer =
+ GradientDrawable().apply {
+ setColor(windowBackgroundColor)
+ alpha = 0
+ }
// Update state.
val animator = ValueAnimator.ofFloat(0f, 1f)
@@ -270,38 +273,41 @@ class LaunchAnimator(
// [Controller.openingWindowSyncView] once the opening app window starts to be visible.
val openingWindowSyncView = controller.openingWindowSyncView
val openingWindowSyncViewOverlay = openingWindowSyncView?.overlay
- val moveBackgroundLayerWhenAppIsVisible = openingWindowSyncView != null &&
- openingWindowSyncView.viewRootImpl != controller.launchContainer.viewRootImpl
+ val moveBackgroundLayerWhenAppIsVisible =
+ openingWindowSyncView != null &&
+ openingWindowSyncView.viewRootImpl != controller.launchContainer.viewRootImpl
val launchContainerOverlay = launchContainer.overlay
var cancelled = false
var movedBackgroundLayer = false
- animator.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
- if (DEBUG) {
- Log.d(TAG, "Animation started")
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
+ if (DEBUG) {
+ Log.d(TAG, "Animation started")
+ }
+ controller.onLaunchAnimationStart(isExpandingFullyAbove)
+
+ // Add the drawable to the launch container overlay. Overlays always draw
+ // drawables after views, so we know that it will be drawn above any view added
+ // by the controller.
+ launchContainerOverlay.add(windowBackgroundLayer)
}
- controller.onLaunchAnimationStart(isExpandingFullyAbove)
- // Add the drawable to the launch container overlay. Overlays always draw
- // drawables after views, so we know that it will be drawn above any view added
- // by the controller.
- launchContainerOverlay.add(windowBackgroundLayer)
- }
-
- override fun onAnimationEnd(animation: Animator?) {
- if (DEBUG) {
- Log.d(TAG, "Animation ended")
- }
- controller.onLaunchAnimationEnd(isExpandingFullyAbove)
- launchContainerOverlay.remove(windowBackgroundLayer)
+ override fun onAnimationEnd(animation: Animator?) {
+ if (DEBUG) {
+ Log.d(TAG, "Animation ended")
+ }
+ controller.onLaunchAnimationEnd(isExpandingFullyAbove)
+ launchContainerOverlay.remove(windowBackgroundLayer)
- if (moveBackgroundLayerWhenAppIsVisible) {
- openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
+ if (moveBackgroundLayerWhenAppIsVisible) {
+ openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
+ }
}
}
- })
+ )
animator.addUpdateListener { animation ->
if (cancelled) {
@@ -333,12 +339,13 @@ class LaunchAnimator(
// The expanding view can/should be hidden once it is completely covered by the opening
// window.
- state.visible = getProgress(
- timings,
- linearProgress,
- timings.contentBeforeFadeOutDelay,
- timings.contentBeforeFadeOutDuration
- ) < 1
+ state.visible =
+ getProgress(
+ timings,
+ linearProgress,
+ timings.contentBeforeFadeOutDelay,
+ timings.contentBeforeFadeOutDuration
+ ) < 1
if (moveBackgroundLayerWhenAppIsVisible && !state.visible && !movedBackgroundLayer) {
// The expanding view is not visible, so the opening app is visible. If this is the
@@ -352,17 +359,19 @@ class LaunchAnimator(
ViewRootSync.synchronizeNextDraw(launchContainer, openingWindowSyncView, then = {})
}
- val container = if (movedBackgroundLayer) {
- openingWindowSyncView!!
- } else {
- controller.launchContainer
- }
+ val container =
+ if (movedBackgroundLayer) {
+ openingWindowSyncView!!
+ } else {
+ controller.launchContainer
+ }
applyStateToWindowBackgroundLayer(
windowBackgroundLayer,
state,
linearProgress,
container,
+ fadeOutWindowBackgroundLayer,
drawHole
)
controller.onLaunchAnimationProgress(state, progress, linearProgress)
@@ -391,6 +400,7 @@ class LaunchAnimator(
state: State,
linearProgress: Float,
launchContainer: View,
+ fadeOutWindowBackgroundLayer: Boolean,
drawHole: Boolean
) {
// Update position.
@@ -415,23 +425,25 @@ class LaunchAnimator(
// We first fade in the background layer to hide the expanding view, then fade it out
// with SRC mode to draw a hole punch in the status bar and reveal the opening window.
- val fadeInProgress = getProgress(
- timings,
- linearProgress,
- timings.contentBeforeFadeOutDelay,
- timings.contentBeforeFadeOutDuration
- )
+ val fadeInProgress =
+ getProgress(
+ timings,
+ linearProgress,
+ timings.contentBeforeFadeOutDelay,
+ timings.contentBeforeFadeOutDuration
+ )
if (fadeInProgress < 1) {
val alpha =
interpolators.contentBeforeFadeOutInterpolator.getInterpolation(fadeInProgress)
drawable.alpha = (alpha * 0xFF).roundToInt()
- } else {
- val fadeOutProgress = getProgress(
- timings,
- linearProgress,
- timings.contentAfterFadeInDelay,
- timings.contentAfterFadeInDuration
- )
+ } else if (fadeOutWindowBackgroundLayer) {
+ val fadeOutProgress =
+ getProgress(
+ timings,
+ linearProgress,
+ timings.contentAfterFadeInDelay,
+ timings.contentAfterFadeInDuration
+ )
val alpha =
1 - interpolators.contentAfterFadeInInterpolator.getInterpolation(fadeOutProgress)
drawable.alpha = (alpha * 0xFF).roundToInt()
@@ -439,6 +451,8 @@ class LaunchAnimator(
if (drawHole) {
drawable.setXfermode(SRC_MODE)
}
+ } else {
+ drawable.alpha = 0xFF
}
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
index 80a3eb839940..7499302c06b2 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
@@ -27,4 +27,4 @@ interface LaunchableView {
* [transition][android.view.View.setTransitionVisibility] visibility changes must be blocked.
*/
fun setShouldBlockVisibilityChanges(block: Boolean)
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
index 47c11010c072..f9c6841f96b5 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
@@ -40,6 +40,7 @@ class RemoteTransitionAdapter {
companion object {
/**
* Almost a copy of Transitions#setupStartState.
+ *
* TODO: remove when there is proper cross-process transaction sync.
*/
@SuppressLint("NewApi")
@@ -50,7 +51,8 @@ class RemoteTransitionAdapter {
info: TransitionInfo,
t: SurfaceControl.Transaction
) {
- val isOpening = info.type == WindowManager.TRANSIT_OPEN ||
+ val isOpening =
+ info.type == WindowManager.TRANSIT_OPEN ||
info.type == WindowManager.TRANSIT_TO_FRONT
// Put animating stuff above this line and put static stuff below it.
val zSplitLine = info.changes.size
@@ -59,15 +61,19 @@ class RemoteTransitionAdapter {
// Launcher animates leaf tasks directly, so always reparent all task leashes to root.
t.reparent(leash, info.rootLeash)
- t.setPosition(leash, (change.startAbsBounds.left - info.rootOffset.x).toFloat(), (
- change.startAbsBounds.top - info.rootOffset.y).toFloat())
+ t.setPosition(
+ leash,
+ (change.startAbsBounds.left - info.rootOffset.x).toFloat(),
+ (change.startAbsBounds.top - info.rootOffset.y).toFloat()
+ )
t.show(leash)
// Put all the OPEN/SHOW on top
if (mode == WindowManager.TRANSIT_OPEN || mode == WindowManager.TRANSIT_TO_FRONT) {
if (isOpening) {
t.setLayer(leash, zSplitLine + info.changes.size - layer)
- if (change.flags
- and TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT == 0) {
+ if (
+ change.flags and TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT == 0
+ ) {
// if transferred, it should be left visible.
t.setAlpha(leash, 0f)
}
@@ -75,8 +81,9 @@ class RemoteTransitionAdapter {
// put on bottom and leave it visible
t.setLayer(leash, zSplitLine - layer)
}
- } else if (mode == WindowManager.TRANSIT_CLOSE ||
- mode == WindowManager.TRANSIT_TO_BACK) {
+ } else if (
+ mode == WindowManager.TRANSIT_CLOSE || mode == WindowManager.TRANSIT_TO_BACK
+ ) {
if (isOpening) {
// put on bottom and leave visible
t.setLayer(leash, zSplitLine - layer)
@@ -102,10 +109,15 @@ class RemoteTransitionAdapter {
// making leashes means we have to handle them specially.
return change.leash
}
- val leashSurface = SurfaceControl.Builder()
+ val leashSurface =
+ SurfaceControl.Builder()
.setName(change.leash.toString() + "_transition-leash")
- .setContainerLayer().setParent(if (change.parent == null)
- info.rootLeash else info.getChange(change.parent!!)!!.leash).build()
+ .setContainerLayer()
+ .setParent(
+ if (change.parent == null) info.rootLeash
+ else info.getChange(change.parent!!)!!.leash
+ )
+ .build()
// Copied Transitions setup code (which expects bottom-to-top order, so we swap here)
setupLeash(leashSurface, change, info.changes.size - order, info, t)
t.reparent(change.leash, leashSurface)
@@ -118,10 +130,10 @@ class RemoteTransitionAdapter {
private fun newModeToLegacyMode(newMode: Int): Int {
return when (newMode) {
- WindowManager.TRANSIT_OPEN, WindowManager.TRANSIT_TO_FRONT
- -> RemoteAnimationTarget.MODE_OPENING
- WindowManager.TRANSIT_CLOSE, WindowManager.TRANSIT_TO_BACK
- -> RemoteAnimationTarget.MODE_CLOSING
+ WindowManager.TRANSIT_OPEN,
+ WindowManager.TRANSIT_TO_FRONT -> RemoteAnimationTarget.MODE_OPENING
+ WindowManager.TRANSIT_CLOSE,
+ WindowManager.TRANSIT_TO_BACK -> RemoteAnimationTarget.MODE_CLOSING
else -> RemoteAnimationTarget.MODE_CHANGING
}
}
@@ -138,12 +150,13 @@ class RemoteTransitionAdapter {
info: TransitionInfo,
t: SurfaceControl.Transaction
): RemoteAnimationTarget {
- val target = RemoteAnimationTarget(
+ val target =
+ RemoteAnimationTarget(
/* taskId */ if (change.taskInfo != null) change.taskInfo!!.taskId else -1,
/* mode */ newModeToLegacyMode(change.mode),
/* leash */ createLeash(info, change, order, t),
/* isTranslucent */ (change.flags and TransitionInfo.FLAG_TRANSLUCENT != 0 ||
- change.flags and TransitionInfo.FLAG_SHOW_WALLPAPER != 0),
+ change.flags and TransitionInfo.FLAG_SHOW_WALLPAPER != 0),
/* clipRect */ null,
/* contentInsets */ Rect(0, 0, 0, 0),
/* prefixOrderIndex */ order,
@@ -151,15 +164,16 @@ class RemoteTransitionAdapter {
/* localBounds */ rectOffsetTo(change.endAbsBounds, change.endRelOffset),
/* screenSpaceBounds */ Rect(change.endAbsBounds),
/* windowConfig */ if (change.taskInfo != null)
- change.taskInfo!!.configuration.windowConfiguration else
- WindowConfiguration(),
- /* isNotInRecents */ if (change.taskInfo != null)
- !change.taskInfo!!.isRunning else true,
+ change.taskInfo!!.configuration.windowConfiguration
+ else WindowConfiguration(),
+ /* isNotInRecents */ if (change.taskInfo != null) !change.taskInfo!!.isRunning
+ else true,
/* startLeash */ null,
/* startBounds */ Rect(change.startAbsBounds),
/* taskInfo */ change.taskInfo,
/* allowEnterPip */ change.allowEnterPip,
- /* windowType */ WindowManager.LayoutParams.INVALID_WINDOW_TYPE)
+ /* windowType */ WindowManager.LayoutParams.INVALID_WINDOW_TYPE
+ )
target.backgroundColor = change.backgroundColor
return target
}
@@ -192,9 +206,7 @@ class RemoteTransitionAdapter {
}
@JvmStatic
- fun adaptRemoteRunner(
- runner: IRemoteAnimationRunner
- ): IRemoteTransition.Stub {
+ fun adaptRemoteRunner(runner: IRemoteAnimationRunner): IRemoteTransition.Stub {
return object : IRemoteTransition.Stub() {
override fun startAnimation(
token: IBinder,
@@ -218,18 +230,24 @@ class RemoteTransitionAdapter {
var displayH = 0f
for (i in info.changes.indices.reversed()) {
val change = info.changes[i]
- if (change.taskInfo != null &&
- change.taskInfo!!.activityType
- == WindowConfiguration.ACTIVITY_TYPE_HOME) {
- isReturnToHome = (change.mode == WindowManager.TRANSIT_OPEN ||
+ if (
+ change.taskInfo != null &&
+ change.taskInfo!!.activityType ==
+ WindowConfiguration.ACTIVITY_TYPE_HOME
+ ) {
+ isReturnToHome =
+ (change.mode == WindowManager.TRANSIT_OPEN ||
change.mode == WindowManager.TRANSIT_TO_FRONT)
launcherTask = change
launcherLayer = info.changes.size - i
} else if (change.flags and TransitionInfo.FLAG_IS_WALLPAPER != 0) {
wallpaper = change
}
- if (change.parent == null && change.endRotation >= 0 &&
- change.endRotation != change.startRotation) {
+ if (
+ change.parent == null &&
+ change.endRotation >= 0 &&
+ change.endRotation != change.startRotation
+ ) {
rotateDelta = change.endRotation - change.startRotation
displayW = change.endAbsBounds.width().toFloat()
displayH = change.endAbsBounds.height().toFloat()
@@ -240,8 +258,13 @@ class RemoteTransitionAdapter {
val counterLauncher = CounterRotator()
val counterWallpaper = CounterRotator()
if (launcherTask != null && rotateDelta != 0 && launcherTask.parent != null) {
- counterLauncher.setup(t, info.getChange(launcherTask.parent!!)!!.leash,
- rotateDelta, displayW, displayH)
+ counterLauncher.setup(
+ t,
+ info.getChange(launcherTask.parent!!)!!.leash,
+ rotateDelta,
+ displayW,
+ displayH
+ )
if (counterLauncher.surface != null) {
t.setLayer(counterLauncher.surface!!, launcherLayer)
}
@@ -257,8 +280,10 @@ class RemoteTransitionAdapter {
val mode = info.changes[i].mode
// Only deal with independent layers
if (!TransitionInfo.isIndependent(change, info)) continue
- if (mode == WindowManager.TRANSIT_CLOSE ||
- mode == WindowManager.TRANSIT_TO_BACK) {
+ if (
+ mode == WindowManager.TRANSIT_CLOSE ||
+ mode == WindowManager.TRANSIT_TO_BACK
+ ) {
t.setLayer(leash!!, info.changes.size * 3 - i)
counterLauncher.addChild(t, leash)
}
@@ -273,8 +298,13 @@ class RemoteTransitionAdapter {
counterLauncher.addChild(t, leashMap[launcherTask.leash])
}
if (wallpaper != null && rotateDelta != 0 && wallpaper.parent != null) {
- counterWallpaper.setup(t, info.getChange(wallpaper.parent!!)!!.leash,
- rotateDelta, displayW, displayH)
+ counterWallpaper.setup(
+ t,
+ info.getChange(wallpaper.parent!!)!!.leash,
+ rotateDelta,
+ displayW,
+ displayH
+ )
if (counterWallpaper.surface != null) {
t.setLayer(counterWallpaper.surface!!, -1)
counterWallpaper.addChild(t, leashMap[wallpaper.leash])
@@ -282,37 +312,47 @@ class RemoteTransitionAdapter {
}
}
t.apply()
- val animationFinishedCallback = object : IRemoteAnimationFinishedCallback {
- override fun onAnimationFinished() {
- val finishTransaction = SurfaceControl.Transaction()
- counterLauncher.cleanUp(finishTransaction)
- counterWallpaper.cleanUp(finishTransaction)
- // Release surface references now. This is apparently to free GPU memory
- // while doing quick operations (eg. during CTS).
- for (i in info.changes.indices.reversed()) {
- info.changes[i].leash.release()
- }
- for (i in leashMap.size - 1 downTo 0) {
- leashMap.valueAt(i).release()
- }
- try {
- finishCallback.onTransitionFinished(null /* wct */,
- finishTransaction)
- } catch (e: RemoteException) {
- Log.e("ActivityOptionsCompat", "Failed to call app controlled" +
- " animation finished callback", e)
+ val animationFinishedCallback =
+ object : IRemoteAnimationFinishedCallback {
+ override fun onAnimationFinished() {
+ val finishTransaction = SurfaceControl.Transaction()
+ counterLauncher.cleanUp(finishTransaction)
+ counterWallpaper.cleanUp(finishTransaction)
+ // Release surface references now. This is apparently to free GPU
+ // memory while doing quick operations (eg. during CTS).
+ for (i in info.changes.indices.reversed()) {
+ info.changes[i].leash.release()
+ }
+ for (i in leashMap.size - 1 downTo 0) {
+ leashMap.valueAt(i).release()
+ }
+ try {
+ finishCallback.onTransitionFinished(
+ null /* wct */,
+ finishTransaction
+ )
+ } catch (e: RemoteException) {
+ Log.e(
+ "ActivityOptionsCompat",
+ "Failed to call app controlled" +
+ " animation finished callback",
+ e
+ )
+ }
}
- }
- override fun asBinder(): IBinder? {
- return null
+ override fun asBinder(): IBinder? {
+ return null
+ }
}
- }
// TODO(bc-unlcok): Pass correct transit type.
runner.onAnimationStart(
- WindowManager.TRANSIT_OLD_NONE,
- appsCompat, wallpapersCompat, nonAppsCompat,
- animationFinishedCallback)
+ WindowManager.TRANSIT_OLD_NONE,
+ appsCompat,
+ wallpapersCompat,
+ nonAppsCompat,
+ animationFinishedCallback
+ )
}
override fun mergeAnimation(
@@ -329,18 +369,14 @@ class RemoteTransitionAdapter {
}
@JvmStatic
- fun adaptRemoteAnimation(
- adapter: RemoteAnimationAdapter
- ): RemoteTransition {
+ fun adaptRemoteAnimation(adapter: RemoteAnimationAdapter): RemoteTransition {
return RemoteTransition(adaptRemoteRunner(adapter.runner), adapter.callingApplication)
}
}
- /**
- * Utility class that takes care of counter-rotating surfaces during a transition animation.
- */
+ /** Utility class that takes care of counter-rotating surfaces during a transition animation. */
class CounterRotator {
- /** Gets the surface with the counter-rotation. */
+ /** Gets the surface with the counter-rotation. */
var surface: SurfaceControl? = null
private set
@@ -358,7 +394,8 @@ class RemoteTransitionAdapter {
parentH: Float
) {
if (rotateDelta == 0) return
- val surface = SurfaceControl.Builder()
+ val surface =
+ SurfaceControl.Builder()
.setName("Transition Unrotate")
.setContainerLayer()
.setParent(parent)
@@ -378,21 +415,19 @@ class RemoteTransitionAdapter {
t.show(surface)
}
- /**
- * Adds a surface that needs to be counter-rotate.
- */
+ /** Adds a surface that needs to be counter-rotate. */
fun addChild(t: SurfaceControl.Transaction, child: SurfaceControl?) {
if (surface == null) return
t.reparent(child!!, surface)
}
/**
- * Clean-up. Since finishTransaction should reset all change leashes, we only need to remove the
- * counter rotation surface.
+ * Clean-up. Since finishTransaction should reset all change leashes, we only need to remove
+ * the counter rotation surface.
*/
fun cleanUp(finishTransaction: SurfaceControl.Transaction) {
if (surface == null) return
finishTransaction.remove(surface!!)
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt
index 0ee2bfea55c5..a96f893a8db4 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt
@@ -31,7 +31,7 @@ object ShadeInterpolation {
} else {
val oneMinusFrac = 1f - mappedFraction
(1f - 0.5f * (1f - Math.cos((3.14159f * oneMinusFrac * oneMinusFrac).toDouble())))
- .toFloat()
+ .toFloat()
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
index 4c3cd3c2b441..cc7d23e97d0c 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
@@ -42,12 +42,13 @@ class ViewHierarchyAnimator {
private val DEFAULT_FADE_IN_INTERPOLATOR = Interpolators.ALPHA_IN
/** The properties used to animate the view bounds. */
- private val PROPERTIES = mapOf(
- Bound.LEFT to createViewProperty(Bound.LEFT),
- Bound.TOP to createViewProperty(Bound.TOP),
- Bound.RIGHT to createViewProperty(Bound.RIGHT),
- Bound.BOTTOM to createViewProperty(Bound.BOTTOM)
- )
+ private val PROPERTIES =
+ mapOf(
+ Bound.LEFT to createViewProperty(Bound.LEFT),
+ Bound.TOP to createViewProperty(Bound.TOP),
+ Bound.RIGHT to createViewProperty(Bound.RIGHT),
+ Bound.BOTTOM to createViewProperty(Bound.BOTTOM)
+ )
private fun createViewProperty(bound: Bound): IntProperty<View> {
return object : IntProperty<View>(bound.label) {
@@ -104,7 +105,8 @@ class ViewHierarchyAnimator {
duration: Long,
ephemeral: Boolean
): Boolean {
- if (!isVisible(
+ if (
+ !occupiesSpace(
rootView.visibility,
rootView.left,
rootView.top,
@@ -132,11 +134,7 @@ class ViewHierarchyAnimator {
duration: Long,
ephemeral: Boolean
): View.OnLayoutChangeListener {
- return createListener(
- interpolator,
- duration,
- ephemeral
- )
+ return createListener(interpolator, duration, ephemeral)
}
/**
@@ -178,7 +176,8 @@ class ViewHierarchyAnimator {
includeFadeIn: Boolean = false,
fadeInInterpolator: Interpolator = DEFAULT_FADE_IN_INTERPOLATOR
): Boolean {
- if (isVisible(
+ if (
+ occupiesSpace(
rootView.visibility,
rootView.left,
rootView.top,
@@ -189,9 +188,13 @@ class ViewHierarchyAnimator {
return false
}
- val listener = createAdditionListener(
- origin, interpolator, duration, ignorePreviousValues = !includeMargins
- )
+ val listener =
+ createAdditionListener(
+ origin,
+ interpolator,
+ duration,
+ ignorePreviousValues = !includeMargins
+ )
addListener(rootView, listener, recursive = true)
if (!includeFadeIn) {
@@ -292,7 +295,7 @@ class ViewHierarchyAnimator {
(view.getTag(R.id.tag_animator) as? ObjectAnimator)?.cancel()
- if (!isVisible(view.visibility, left, top, right, bottom)) {
+ if (!occupiesSpace(view.visibility, left, top, right, bottom)) {
setBound(view, Bound.LEFT, left)
setBound(view, Bound.TOP, top)
setBound(view, Bound.RIGHT, right)
@@ -300,24 +303,26 @@ class ViewHierarchyAnimator {
return
}
- val startValues = processStartValues(
- origin,
- left,
- top,
- right,
- bottom,
- startLeft,
- startTop,
- startRight,
- startBottom,
- ignorePreviousValues
- )
- val endValues = mapOf(
- Bound.LEFT to left,
- Bound.TOP to top,
- Bound.RIGHT to right,
- Bound.BOTTOM to bottom
- )
+ val startValues =
+ processStartValues(
+ origin,
+ left,
+ top,
+ right,
+ bottom,
+ startLeft,
+ startTop,
+ startRight,
+ startBottom,
+ ignorePreviousValues
+ )
+ val endValues =
+ mapOf(
+ Bound.LEFT to left,
+ Bound.TOP to top,
+ Bound.RIGHT to right,
+ Bound.BOTTOM to bottom
+ )
val boundsToAnimate = mutableSetOf<Bound>()
if (startValues.getValue(Bound.LEFT) != left) boundsToAnimate.add(Bound.LEFT)
@@ -356,7 +361,8 @@ class ViewHierarchyAnimator {
interpolator: Interpolator = DEFAULT_REMOVAL_INTERPOLATOR,
duration: Long = DEFAULT_DURATION
): Boolean {
- if (!isVisible(
+ if (
+ !occupiesSpace(
rootView.visibility,
rootView.left,
rootView.top,
@@ -370,11 +376,7 @@ class ViewHierarchyAnimator {
val parent = rootView.parent as ViewGroup
// Ensure that rootView's siblings animate nicely around the removal.
- val listener = createUpdateListener(
- interpolator,
- duration,
- ephemeral = true
- )
+ val listener = createUpdateListener(interpolator, duration, ephemeral = true)
for (i in 0 until parent.childCount) {
val child = parent.getChildAt(i)
if (child == rootView) continue
@@ -389,19 +391,21 @@ class ViewHierarchyAnimator {
// them manually during the animation.
parent.overlay.add(rootView)
- val startValues = mapOf(
- Bound.LEFT to rootView.left,
- Bound.TOP to rootView.top,
- Bound.RIGHT to rootView.right,
- Bound.BOTTOM to rootView.bottom
- )
- val endValues = processEndValuesForRemoval(
- destination,
- rootView.left,
- rootView.top,
- rootView.right,
- rootView.bottom
- )
+ val startValues =
+ mapOf(
+ Bound.LEFT to rootView.left,
+ Bound.TOP to rootView.top,
+ Bound.RIGHT to rootView.right,
+ Bound.BOTTOM to rootView.bottom
+ )
+ val endValues =
+ processEndValuesForRemoval(
+ destination,
+ rootView.left,
+ rootView.top,
+ rootView.right,
+ rootView.bottom
+ )
val boundsToAnimate = mutableSetOf<Bound>()
if (rootView.left != endValues.getValue(Bound.LEFT)) boundsToAnimate.add(Bound.LEFT)
@@ -443,20 +447,24 @@ class ViewHierarchyAnimator {
(animation.animatedValue as Float) * startAlphas[i]
}
}
- animator.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- rootView.animate()
- .alpha(0f)
- .setInterpolator(Interpolators.ALPHA_OUT)
- .setDuration(duration / 2)
- .withEndAction { parent.overlay.remove(rootView) }
- .start()
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ rootView
+ .animate()
+ .alpha(0f)
+ .setInterpolator(Interpolators.ALPHA_OUT)
+ .setDuration(duration / 2)
+ .withEndAction { parent.overlay.remove(rootView) }
+ .start()
+ }
}
- })
+ )
animator.start()
} else {
// Fade out the view during the second half of the removal.
- rootView.animate()
+ rootView
+ .animate()
.alpha(0f)
.setInterpolator(Interpolators.ALPHA_OUT)
.setDuration(duration / 2)
@@ -483,21 +491,23 @@ class ViewHierarchyAnimator {
) {
for (i in 0 until rootView.childCount) {
val child = rootView.getChildAt(i)
- val childStartValues = mapOf(
- Bound.LEFT to child.left,
- Bound.TOP to child.top,
- Bound.RIGHT to child.right,
- Bound.BOTTOM to child.bottom
- )
- val childEndValues = processChildEndValuesForRemoval(
- destination,
- child.left,
- child.top,
- child.right,
- child.bottom,
- endValues.getValue(Bound.RIGHT) - endValues.getValue(Bound.LEFT),
- endValues.getValue(Bound.BOTTOM) - endValues.getValue(Bound.TOP)
- )
+ val childStartValues =
+ mapOf(
+ Bound.LEFT to child.left,
+ Bound.TOP to child.top,
+ Bound.RIGHT to child.right,
+ Bound.BOTTOM to child.bottom
+ )
+ val childEndValues =
+ processChildEndValuesForRemoval(
+ destination,
+ child.left,
+ child.top,
+ child.right,
+ child.bottom,
+ endValues.getValue(Bound.RIGHT) - endValues.getValue(Bound.LEFT),
+ endValues.getValue(Bound.BOTTOM) - endValues.getValue(Bound.TOP)
+ )
val boundsToAnimate = mutableSetOf<Bound>()
if (child.left != endValues.getValue(Bound.LEFT)) boundsToAnimate.add(Bound.LEFT)
@@ -520,17 +530,17 @@ class ViewHierarchyAnimator {
}
/**
- * Returns whether the given [visibility] and bounds are consistent with a view being
- * currently visible on screen.
+ * Returns whether the given [visibility] and bounds are consistent with a view being a
+ * contributing part of the hierarchy.
*/
- private fun isVisible(
+ private fun occupiesSpace(
visibility: Int,
left: Int,
top: Int,
right: Int,
bottom: Int
): Boolean {
- return visibility == View.VISIBLE && left != right && top != bottom
+ return visibility != View.GONE && left != right && top != bottom
}
/**
@@ -543,6 +553,7 @@ class ViewHierarchyAnimator {
* not newly introduced margins are included.
*
* Base case
+ * ```
* 1) origin=TOP
* x---------x x---------x x---------x x---------x x---------x
* x---------x | | | | | |
@@ -561,11 +572,11 @@ class ViewHierarchyAnimator {
* x -> x---x -> | | -> | | -> | |
* x-----x x-------x | |
* x---------x
- *
+ * ```
* In case the start and end values differ in the direction of the origin, and
* [ignorePreviousValues] is false, the previous values are used and a translation is
* included in addition to the view expansion.
- *
+ * ```
* origin=TOP_LEFT - (0,0,0,0) -> (30,30,70,70)
* x
* x--x
@@ -574,6 +585,7 @@ class ViewHierarchyAnimator {
* x----x | |
* | |
* x------x
+ * ```
*/
private fun processStartValues(
origin: Hotspot?,
@@ -598,42 +610,54 @@ class ViewHierarchyAnimator {
var bottom = startBottom
if (origin != null) {
- left = when (origin) {
- Hotspot.CENTER -> (newLeft + newRight) / 2
- Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> min(startLeft, newLeft)
- Hotspot.TOP, Hotspot.BOTTOM -> newLeft
- Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> max(
- startRight,
- newRight
- )
- }
- top = when (origin) {
- Hotspot.CENTER -> (newTop + newBottom) / 2
- Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> min(startTop, newTop)
- Hotspot.LEFT, Hotspot.RIGHT -> newTop
- Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> max(
- startBottom,
- newBottom
- )
- }
- right = when (origin) {
- Hotspot.CENTER -> (newLeft + newRight) / 2
- Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> max(
- startRight,
- newRight
- )
- Hotspot.TOP, Hotspot.BOTTOM -> newRight
- Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> min(startLeft, newLeft)
- }
- bottom = when (origin) {
- Hotspot.CENTER -> (newTop + newBottom) / 2
- Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> max(
- startBottom,
- newBottom
- )
- Hotspot.LEFT, Hotspot.RIGHT -> newBottom
- Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> min(startTop, newTop)
- }
+ left =
+ when (origin) {
+ Hotspot.CENTER -> (newLeft + newRight) / 2
+ Hotspot.BOTTOM_LEFT,
+ Hotspot.LEFT,
+ Hotspot.TOP_LEFT -> min(startLeft, newLeft)
+ Hotspot.TOP,
+ Hotspot.BOTTOM -> newLeft
+ Hotspot.TOP_RIGHT,
+ Hotspot.RIGHT,
+ Hotspot.BOTTOM_RIGHT -> max(startRight, newRight)
+ }
+ top =
+ when (origin) {
+ Hotspot.CENTER -> (newTop + newBottom) / 2
+ Hotspot.TOP_LEFT,
+ Hotspot.TOP,
+ Hotspot.TOP_RIGHT -> min(startTop, newTop)
+ Hotspot.LEFT,
+ Hotspot.RIGHT -> newTop
+ Hotspot.BOTTOM_RIGHT,
+ Hotspot.BOTTOM,
+ Hotspot.BOTTOM_LEFT -> max(startBottom, newBottom)
+ }
+ right =
+ when (origin) {
+ Hotspot.CENTER -> (newLeft + newRight) / 2
+ Hotspot.TOP_RIGHT,
+ Hotspot.RIGHT,
+ Hotspot.BOTTOM_RIGHT -> max(startRight, newRight)
+ Hotspot.TOP,
+ Hotspot.BOTTOM -> newRight
+ Hotspot.BOTTOM_LEFT,
+ Hotspot.LEFT,
+ Hotspot.TOP_LEFT -> min(startLeft, newLeft)
+ }
+ bottom =
+ when (origin) {
+ Hotspot.CENTER -> (newTop + newBottom) / 2
+ Hotspot.BOTTOM_RIGHT,
+ Hotspot.BOTTOM,
+ Hotspot.BOTTOM_LEFT -> max(startBottom, newBottom)
+ Hotspot.LEFT,
+ Hotspot.RIGHT -> newBottom
+ Hotspot.TOP_LEFT,
+ Hotspot.TOP,
+ Hotspot.TOP_RIGHT -> min(startTop, newTop)
+ }
}
return mapOf(
@@ -649,6 +673,7 @@ class ViewHierarchyAnimator {
* view's starting bounds.
*
* Examples:
+ * ```
* 1) destination=TOP
* x---------x x---------x x---------x x---------x x---------x
* | | | | | | x---------x
@@ -667,6 +692,7 @@ class ViewHierarchyAnimator {
* | | -> | | -> | | -> x---x -> x
* | | x-------x x-----x
* x---------x
+ * ```
*/
private fun processEndValuesForRemoval(
destination: Hotspot,
@@ -675,32 +701,54 @@ class ViewHierarchyAnimator {
right: Int,
bottom: Int
): Map<Bound, Int> {
- val endLeft = when (destination) {
- Hotspot.CENTER -> (left + right) / 2
- Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT, Hotspot.TOP ->
- left
- Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> right
- }
- val endTop = when (destination) {
- Hotspot.CENTER -> (top + bottom) / 2
- Hotspot.LEFT, Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT, Hotspot.RIGHT ->
- top
- Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> bottom
- }
- val endRight = when (destination) {
- Hotspot.CENTER -> (left + right) / 2
- Hotspot.TOP, Hotspot.TOP_RIGHT, Hotspot.RIGHT,
- Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM ->
- right
- Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> left
- }
- val endBottom = when (destination) {
- Hotspot.CENTER -> (top + bottom) / 2
- Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM,
- Hotspot.BOTTOM_LEFT, Hotspot.LEFT ->
- bottom
- Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> top
- }
+ val endLeft =
+ when (destination) {
+ Hotspot.CENTER -> (left + right) / 2
+ Hotspot.BOTTOM,
+ Hotspot.BOTTOM_LEFT,
+ Hotspot.LEFT,
+ Hotspot.TOP_LEFT,
+ Hotspot.TOP -> left
+ Hotspot.TOP_RIGHT,
+ Hotspot.RIGHT,
+ Hotspot.BOTTOM_RIGHT -> right
+ }
+ val endTop =
+ when (destination) {
+ Hotspot.CENTER -> (top + bottom) / 2
+ Hotspot.LEFT,
+ Hotspot.TOP_LEFT,
+ Hotspot.TOP,
+ Hotspot.TOP_RIGHT,
+ Hotspot.RIGHT -> top
+ Hotspot.BOTTOM_RIGHT,
+ Hotspot.BOTTOM,
+ Hotspot.BOTTOM_LEFT -> bottom
+ }
+ val endRight =
+ when (destination) {
+ Hotspot.CENTER -> (left + right) / 2
+ Hotspot.TOP,
+ Hotspot.TOP_RIGHT,
+ Hotspot.RIGHT,
+ Hotspot.BOTTOM_RIGHT,
+ Hotspot.BOTTOM -> right
+ Hotspot.BOTTOM_LEFT,
+ Hotspot.LEFT,
+ Hotspot.TOP_LEFT -> left
+ }
+ val endBottom =
+ when (destination) {
+ Hotspot.CENTER -> (top + bottom) / 2
+ Hotspot.RIGHT,
+ Hotspot.BOTTOM_RIGHT,
+ Hotspot.BOTTOM,
+ Hotspot.BOTTOM_LEFT,
+ Hotspot.LEFT -> bottom
+ Hotspot.TOP_LEFT,
+ Hotspot.TOP,
+ Hotspot.TOP_RIGHT -> top
+ }
return mapOf(
Bound.LEFT to endLeft,
@@ -718,6 +766,7 @@ class ViewHierarchyAnimator {
* its center is at the [destination].
*
* Examples:
+ * ```
* 1) destination=TOP
* The child maintains its left and right positions, but is shifted up so that its
* center is on the parent's end top edge.
@@ -725,6 +774,7 @@ class ViewHierarchyAnimator {
* The child shifts so that its center is on the parent's end bottom left corner.
* 3) destination=CENTER
* The child shifts so that its own center is on the parent's end center.
+ * ```
*/
private fun processChildEndValuesForRemoval(
destination: Hotspot,
@@ -738,32 +788,54 @@ class ViewHierarchyAnimator {
val halfWidth = (right - left) / 2
val halfHeight = (bottom - top) / 2
- val endLeft = when (destination) {
- Hotspot.CENTER -> (parentWidth / 2) - halfWidth
- Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> -halfWidth
- Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> parentWidth - halfWidth
- Hotspot.TOP, Hotspot.BOTTOM -> left
- }
- val endTop = when (destination) {
- Hotspot.CENTER -> (parentHeight / 2) - halfHeight
- Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> -halfHeight
- Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT ->
- parentHeight - halfHeight
- Hotspot.LEFT, Hotspot.RIGHT -> top
- }
- val endRight = when (destination) {
- Hotspot.CENTER -> (parentWidth / 2) + halfWidth
- Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> parentWidth + halfWidth
- Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> halfWidth
- Hotspot.TOP, Hotspot.BOTTOM -> right
- }
- val endBottom = when (destination) {
- Hotspot.CENTER -> (parentHeight / 2) + halfHeight
- Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT ->
- parentHeight + halfHeight
- Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> halfHeight
- Hotspot.LEFT, Hotspot.RIGHT -> bottom
- }
+ val endLeft =
+ when (destination) {
+ Hotspot.CENTER -> (parentWidth / 2) - halfWidth
+ Hotspot.BOTTOM_LEFT,
+ Hotspot.LEFT,
+ Hotspot.TOP_LEFT -> -halfWidth
+ Hotspot.TOP_RIGHT,
+ Hotspot.RIGHT,
+ Hotspot.BOTTOM_RIGHT -> parentWidth - halfWidth
+ Hotspot.TOP,
+ Hotspot.BOTTOM -> left
+ }
+ val endTop =
+ when (destination) {
+ Hotspot.CENTER -> (parentHeight / 2) - halfHeight
+ Hotspot.TOP_LEFT,
+ Hotspot.TOP,
+ Hotspot.TOP_RIGHT -> -halfHeight
+ Hotspot.BOTTOM_RIGHT,
+ Hotspot.BOTTOM,
+ Hotspot.BOTTOM_LEFT -> parentHeight - halfHeight
+ Hotspot.LEFT,
+ Hotspot.RIGHT -> top
+ }
+ val endRight =
+ when (destination) {
+ Hotspot.CENTER -> (parentWidth / 2) + halfWidth
+ Hotspot.TOP_RIGHT,
+ Hotspot.RIGHT,
+ Hotspot.BOTTOM_RIGHT -> parentWidth + halfWidth
+ Hotspot.BOTTOM_LEFT,
+ Hotspot.LEFT,
+ Hotspot.TOP_LEFT -> halfWidth
+ Hotspot.TOP,
+ Hotspot.BOTTOM -> right
+ }
+ val endBottom =
+ when (destination) {
+ Hotspot.CENTER -> (parentHeight / 2) + halfHeight
+ Hotspot.BOTTOM_RIGHT,
+ Hotspot.BOTTOM,
+ Hotspot.BOTTOM_LEFT -> parentHeight + halfHeight
+ Hotspot.TOP_LEFT,
+ Hotspot.TOP,
+ Hotspot.TOP_RIGHT -> halfHeight
+ Hotspot.LEFT,
+ Hotspot.RIGHT -> bottom
+ }
return mapOf(
Bound.LEFT to endLeft,
@@ -833,44 +905,49 @@ class ViewHierarchyAnimator {
duration: Long,
ephemeral: Boolean
) {
- val propertyValuesHolders = buildList {
- bounds.forEach { bound ->
- add(
- PropertyValuesHolder.ofInt(
- PROPERTIES[bound],
- startValues.getValue(bound),
- endValues.getValue(bound)
- )
- )
- }
- }.toTypedArray()
+ val propertyValuesHolders =
+ buildList {
+ bounds.forEach { bound ->
+ add(
+ PropertyValuesHolder.ofInt(
+ PROPERTIES[bound],
+ startValues.getValue(bound),
+ endValues.getValue(bound)
+ )
+ )
+ }
+ }
+ .toTypedArray()
(view.getTag(R.id.tag_animator) as? ObjectAnimator)?.cancel()
val animator = ObjectAnimator.ofPropertyValuesHolder(view, *propertyValuesHolders)
animator.interpolator = interpolator
animator.duration = duration
- animator.addListener(object : AnimatorListenerAdapter() {
- var cancelled = false
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+ var cancelled = false
- override fun onAnimationEnd(animation: Animator) {
- view.setTag(R.id.tag_animator, null /* tag */)
- bounds.forEach { view.setTag(it.overrideTag, null /* tag */) }
-
- // When an animation is cancelled, a new one might be taking over. We shouldn't
- // unregister the listener yet.
- if (ephemeral && !cancelled) {
- // The duration is the same for the whole hierarchy, so it's safe to remove
- // the listener recursively. We do this because some descendant views might
- // not change bounds, and therefore not animate and leak the listener.
- recursivelyRemoveListener(view)
+ override fun onAnimationEnd(animation: Animator) {
+ view.setTag(R.id.tag_animator, null /* tag */)
+ bounds.forEach { view.setTag(it.overrideTag, null /* tag */) }
+
+ // When an animation is cancelled, a new one might be taking over. We
+ // shouldn't unregister the listener yet.
+ if (ephemeral && !cancelled) {
+ // The duration is the same for the whole hierarchy, so it's safe to
+ // remove the listener recursively. We do this because some descendant
+ // views might not change bounds, and therefore not animate and leak the
+ // listener.
+ recursivelyRemoveListener(view)
+ }
}
- }
- override fun onAnimationCancel(animation: Animator?) {
- cancelled = true
+ override fun onAnimationCancel(animation: Animator?) {
+ cancelled = true
+ }
}
- })
+ )
bounds.forEach { bound -> setBound(view, bound, startValues.getValue(bound)) }
@@ -902,7 +979,15 @@ class ViewHierarchyAnimator {
/** An enum used to determine the origin of addition animations. */
enum class Hotspot {
- CENTER, LEFT, TOP_LEFT, TOP, TOP_RIGHT, RIGHT, BOTTOM_RIGHT, BOTTOM, BOTTOM_LEFT
+ CENTER,
+ LEFT,
+ TOP_LEFT,
+ TOP,
+ TOP_RIGHT,
+ RIGHT,
+ BOTTOM_RIGHT,
+ BOTTOM,
+ BOTTOM_LEFT
}
private enum class Bound(val label: String, val overrideTag: Int) {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt
index 76de7b503451..77640f1992e1 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewRootSync.kt
@@ -11,37 +11,36 @@ object ViewRootSync {
/**
* Synchronize the next draw between the view roots of [view] and [otherView], then run [then].
*
- * Note that in some cases, the synchronization might not be possible (e.g. WM consumed the
- * next transactions) or disabled (temporarily, on low ram devices). In this case, [then] will
- * be called without synchronizing.
+ * Note that in some cases, the synchronization might not be possible (e.g. WM consumed the next
+ * transactions) or disabled (temporarily, on low ram devices). In this case, [then] will be
+ * called without synchronizing.
*/
- fun synchronizeNextDraw(
- view: View,
- otherView: View,
- then: () -> Unit
- ) {
- if (!view.isAttachedToWindow || view.viewRootImpl == null ||
- !otherView.isAttachedToWindow || otherView.viewRootImpl == null ||
- view.viewRootImpl == otherView.viewRootImpl) {
+ fun synchronizeNextDraw(view: View, otherView: View, then: () -> Unit) {
+ if (
+ !view.isAttachedToWindow ||
+ view.viewRootImpl == null ||
+ !otherView.isAttachedToWindow ||
+ otherView.viewRootImpl == null ||
+ view.viewRootImpl == otherView.viewRootImpl
+ ) {
// No need to synchronize if either the touch surface or dialog view is not attached
// to a window.
then()
return
}
- surfaceSyncer = SurfaceSyncer().apply {
- val syncId = setupSync(Runnable { then() })
- addToSync(syncId, view)
- addToSync(syncId, otherView)
- markSyncReady(syncId)
- }
+ surfaceSyncer =
+ SurfaceSyncer().apply {
+ val syncId = setupSync(Runnable { then() })
+ addToSync(syncId, view)
+ addToSync(syncId, otherView)
+ markSyncReady(syncId)
+ }
}
- /**
- * A Java-friendly API for [synchronizeNextDraw].
- */
+ /** A Java-friendly API for [synchronizeNextDraw]. */
@JvmStatic
fun synchronizeNextDraw(view: View, otherView: View, then: Runnable) {
synchronizeNextDraw(view, otherView, then::run)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/docs/camera.md b/packages/SystemUI/docs/camera.md
index cabc65c53caa..a1c845832049 100644
--- a/packages/SystemUI/docs/camera.md
+++ b/packages/SystemUI/docs/camera.md
@@ -1,34 +1,23 @@
# How double-click power launches the camera
-
-_as of august 2020_
-
+_Last update: July 2022_
## Sequence of events
-
-
-
-1. [PhoneWindowManager.java](/services/core/java/com/android/server/policy/PhoneWindowManager.java) is responsible for all power button presses (see `interceptPowerKeyDown`).
-2. Even though PWMgr has a lot of logic to detect all manner of power button multipresses and gestures, it also checks with GestureLauncherService, which is also [offered the chance](/services/core/java/com/android/server/policy/PhoneWindowManager.java#943) to [intercept](/services/core/java/com/android/server/GestureLauncherService.java#358) the power key.
-3. GLS is responsible for the camera timeout, and if it detects one, it [forwards it to the StatusBarManagerService](/services/core/java/com/android/server/GestureLauncherService.java#475) (which hands it off to SystemUI).
-4. Inside SystemUI, [onCameraLaunchDetected](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#3927) looks at the keyguard state and determines
+1. [PhoneWindowManager.java](/services/core/java/com/android/server/policy/PhoneWindowManager.java) is responsible for all power button presses (see `interceptPowerKeyDown`)
+2. Even though `PhoneWindowManager` has a lot of logic to detect all manner of power button multi-presses and gestures, it also checks with `GestureLauncherService`, which is also [offered the chance](/services/core/java/com/android/server/policy/PhoneWindowManager.java#943) to [intercept](/services/core/java/com/android/server/GestureLauncherService.java) the power key
+3. `GestureLauncherService` is responsible for the camera timeout, and if it detects one, it [forwards it to the StatusBarManagerService](/services/core/java/com/android/server/GestureLauncherService.java) (which hands it off to SystemUI)
+4. Inside SystemUI, `onCameraLaunchDetected` in [CentralSurfacesCommandQueueCallbacks.java](/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java) looks at the keyguard state and determines
1. whether the camera is even allowed
2. whether the screen is on; if not, we need to delay until that happens
3. whether the device is locked (defined as "keyguard is showing").
-5. If the device is unlocked (no keyguard), the camera is launched immediately. [Callsite in onCameraLaunchGestureDetected](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#4047).
-6. If the keyguard is up, however, [KeyguardBottomAreaView.launchCamera](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#477) takes over to handle the "secure camera" (a different intent, usually directing to the same app, but giving that app the cue to not allow access to the photo roll, etc).
-7. If the intent [would have to launch a resolver](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#480) (the user has multiple cameras installed and hasn’t chosen one to always launch for the `SECURE_CAMERA_INTENT`),
- 1. In order to show the resolver, the lockscreen "bouncer" (authentication method) [is first presented](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#523).
-8. Otherwise (just one secure camera), [it is launched](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#501) (with some window animation gymnastics).
-
-
-## Which intent launches?
-
-
-
-* If the keyguard is not showing (device is unlocked)
- * `CameraIntents.getInsecureCameraIntent()`, defined to be `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA`.
- * [Callsite](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#3950) in StatusBar.java.
-* If the keyguard is showing (device locked)
- * [KeyguardBottomAreaView.getCameraIntent()](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#366) is consulted, which allows the "keyguard right button" (which we don’t actually show) to control the camera intent. The [default implementation](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#831) returns one of `CameraIntents.getInsecureCameraIntent()` or `CameraIntents.getSecureCameraIntent()`, which are `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA` and `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE`, respectively.
- * [Callsite](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#523) in KeyguardBottomAreaView.java.
-* Note that starting in Android 12, as required by some OEMs, if the special string resource `config_cameraGesturePackage` is nonempty, this will be treated as a package name to be added to the insecure camera intent, constraining the invocation to that single app and typically preventing implicit intent resolution. This package must be on the device or the camera gesture will no longer work properly. \ No newline at end of file
+5. If the device is unlocked (no keyguard), the camera is launched immediately
+6. If the keyguard is up, however, [NotificationPanelViewController.launchCamera](/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java) takes over to handle the "secure camera" (a different intent, usually directing to the same app, but giving that app the cue to not allow access to the photo roll, etc).
+7. If the intent would have to launch a resolver (because the user has multiple camera apps installed and has not chosen one to always launch for the `SECURE_CAMERA_INTENT`, then - in order to show the resolver, the lockscreen "bouncer" (authentication method) is first presented
+8. Otherwise (just one secure camera), it is launched
+
+## Which intent launches the camera app?
+[CameraGestureHelper](/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt) encapsulate this logic. Roughly:
+* If the keyguard is not showing (device is unlocked)
+ * `CameraIntents.getInsecureCameraIntent()`, defined to be `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA`.
+* If the keyguard is showing (device is locked)
+ * one of `CameraIntents.getInsecureCameraIntent()` or `CameraIntents.getSecureCameraIntent()`, which are `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA` and `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE`, respectively
+* Note that starting in Android 12, as required by some OEMs, if the special string resource `config_cameraGesturePackage` is nonempty, this will be treated as a package name to be added to the insecure camera intent, constraining the invocation to that single app and typically preventing implicit intent resolution. This package must be on the device or the camera gesture will no longer work properly
diff --git a/packages/SystemUI/docs/device-entry/doze.md b/packages/SystemUI/docs/device-entry/doze.md
index 5ff8851b4c69..6b6dce5da169 100644
--- a/packages/SystemUI/docs/device-entry/doze.md
+++ b/packages/SystemUI/docs/device-entry/doze.md
@@ -17,6 +17,9 @@ Note: The default UI used in AOD shares views with the Lock Screen and does not
### DOZE
Device is asleep and listening for enabled pulsing and wake-up gesture triggers. In this state, no UI shows.
+### DOZE_SUSPEND_TRIGGERS
+Device is asleep and not listening for any triggers to wake up. This state is used only when CAR_MODE is active. In this state, no UI shows.
+
### DOZE_AOD
Device is asleep, showing UI, and listening for enabled pulsing and wake-up triggers. In this state, screen brightness is handled by [DozeScreenBrightness][5] which uses the brightness sensor specified by `doze_brightness_sensor_type` in the [SystemUI config][6]. To save power, this should be a low-powered sensor that shouldn't trigger as often as the light sensor used for on-screen adaptive brightness.
diff --git a/packages/SystemUI/docs/user-file-manager.md b/packages/SystemUI/docs/user-file-manager.md
new file mode 100644
index 000000000000..52fa2066fbe1
--- /dev/null
+++ b/packages/SystemUI/docs/user-file-manager.md
@@ -0,0 +1,33 @@
+# UserFileManager
+
+This class is used to generate file paths and SharedPreferences that is compatible for specific OS
+users in SystemUI. Due to constraints in SystemUI, we can only read/write files as the system user.
+Therefore, for secondary users, we want to store secondary user specific files into the system user
+directory.
+
+
+## Usages
+
+Inject UserFileManager into your class.
+
+### fun getFile(fileName: String, userId: Int): File
+Add a file name and user id. You can retrieve the current user id from UserTracker. This will
+return a java.io File object that contains the file path to write/read to.
+
+i.e. `fileManager.getFile("example.xml", userTracker.userId)`
+
+### fun getSharedPreferences(fileName: String, mode: Int, userId: Int): SharedPreferences
+Add a file name, user id, and PreferencesMode. You can retrieve the current user id from
+UserTracker. This returns SharedPreferences object that is tied to the specific user. Note that if
+the SharedPreferences file does not exist, one will be created automatically. See
+[SharedPreferences documentation](https://developer.android.com/reference/android/content/Context#getSharedPreferences(java.lang.String,%20int))
+for more details.
+
+i.e. `fileManager.getSharedPreferences("prefs.xml", userTracker.userId, 0)`
+
+## Handling User Removal
+
+This class will listen for Intent.ACTION_USER_REMOVED and remove directories that no longer
+corresponding to active users. Additionally, upon start up, the class will run the same query for
+deletion to ensure that there is no stale data.
+
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 3cf76456de91..eac765ab45b2 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -105,6 +105,11 @@ public interface BcSmartspaceDataPlugin extends Plugin {
void setDozeAmount(float amount);
/**
+ * Set the current keyguard bypass enabled status.
+ */
+ default void setKeyguardBypassEnabled(boolean enabled) {}
+
+ /**
* Overrides how Intents/PendingIntents gets launched. Mostly to support auth from
* the lockscreen.
*/
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
index 3058d9466121..bef61b867f7d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
@@ -23,7 +23,9 @@ import java.util.TimeZone;
/**
* Plugin used to replace main clock in keyguard.
+ * @deprecated Migrating to ClockProviderPlugin
*/
+@Deprecated
@ProvidesInterface(action = ClockPlugin.ACTION, version = ClockPlugin.VERSION)
public interface ClockPlugin extends Plugin {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 0f10589dfcf9..bd628ccb3c08 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -29,7 +29,7 @@ import java.lang.annotation.RetentionPolicy;
/**
* Interface that decides whether a touch on the phone was accidental. i.e. Pocket Dialing.
*
- * {@see com.android.systemui.classifier.FalsingManagerImpl}
+ * {@see com.android.systemui.classifier.BrightLineFalsingManager}
*/
@ProvidesInterface(version = FalsingManager.VERSION)
public interface FalsingManager {
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index e74b6c78ec80..5b9299c278a9 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -5,9 +5,9 @@
-keep class com.android.systemui.statusbar.car.CarStatusBar
-keep class com.android.systemui.statusbar.phone.CentralSurfaces
-keep class com.android.systemui.statusbar.tv.TvStatusBar
--keep class com.android.systemui.car.CarSystemUIFactory
--keep class com.android.systemui.SystemUIFactory
--keep class com.android.systemui.tv.TvSystemUIFactory
+-keep class ** extends com.android.systemui.SystemUIInitializer {
+ *;
+}
-keep class * extends com.android.systemui.CoreStartable
-keep class * implements com.android.systemui.CoreStartable$Injector
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_off.xml b/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_off.xml
new file mode 100644
index 000000000000..538f3284a301
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_off.xml
@@ -0,0 +1,725 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="28"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="7.062"
+ android:valueTo="8.578"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="221"
+ android:propertyName="translateX"
+ android:startOffset="28"
+ android:valueFrom="8.578"
+ android:valueTo="-1.789"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="418"
+ android:propertyName="translateX"
+ android:startOffset="248"
+ android:valueFrom="-1.789"
+ android:valueTo="-7"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="28"
+ android:propertyName="translateY"
+ android:startOffset="0"
+ android:valueFrom="3.312"
+ android:valueTo="2.016"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="221"
+ android:propertyName="translateY"
+ android:startOffset="28"
+ android:valueFrom="2.016"
+ android:valueTo="-8.789"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="418"
+ android:propertyName="translateY"
+ android:startOffset="248"
+ android:valueFrom="-8.789"
+ android:valueTo="-3.5"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="28"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="180"
+ android:valueTo="90"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="166"
+ android:propertyName="rotation"
+ android:startOffset="28"
+ android:valueFrom="90"
+ android:valueTo="90"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="55"
+ android:propertyName="rotation"
+ android:startOffset="193"
+ android:valueFrom="90"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_N_1_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="-135"
+ android:valueTo="-180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="28"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="-7"
+ android:valueTo="-8.859"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="221"
+ android:propertyName="translateX"
+ android:startOffset="28"
+ android:valueFrom="-8.859"
+ android:valueTo="1.69"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="418"
+ android:propertyName="translateX"
+ android:startOffset="248"
+ android:valueFrom="1.69"
+ android:valueTo="7"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="28"
+ android:propertyName="translateY"
+ android:startOffset="0"
+ android:valueFrom="-3.594"
+ android:valueTo="-1.922"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="221"
+ android:propertyName="translateY"
+ android:startOffset="28"
+ android:valueFrom="-1.922"
+ android:valueTo="8.627"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="418"
+ android:propertyName="translateY"
+ android:startOffset="248"
+ android:valueFrom="8.627"
+ android:valueTo="3.5"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="28"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="360"
+ android:valueTo="270"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="166"
+ android:propertyName="rotation"
+ android:startOffset="28"
+ android:valueFrom="270"
+ android:valueTo="270"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="55"
+ android:propertyName="rotation"
+ android:startOffset="193"
+ android:valueFrom="270"
+ android:valueTo="180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="418"
+ android:propertyName="rotation"
+ android:startOffset="248"
+ android:valueFrom="180"
+ android:valueTo="180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_N_1_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="-135"
+ android:valueTo="-180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="28"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.95 -7.53,0.95 C-7.53,0.95 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 7.14,2.14 7.14,2.14 C7.14,2.14 8.48,3.49 8.48,3.49 C8.48,3.49 8.5,3.51 8.5,3.51 C8.5,3.51 8.52,3.53 8.52,3.53c "
+ android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -6.08,2.36 -6.08,2.36 C-6.08,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 7.49,1.78 7.49,1.78 C7.49,1.78 8.87,0.41 8.87,0.41 C8.87,0.41 8.95,0.5 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="66"
+ android:propertyName="pathData"
+ android:startOffset="28"
+ android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -6.08,2.36 -6.08,2.36 C-6.08,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 7.49,1.78 7.49,1.78 C7.49,1.78 8.87,0.41 8.87,0.41 C8.87,0.41 8.95,0.5 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -2.78,5.89 -2.78,5.89 C-2.78,5.89 -1.29,4.47 -1.29,4.47 C-1.29,4.47 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="72"
+ android:propertyName="pathData"
+ android:startOffset="94"
+ android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -2.78,5.89 -2.78,5.89 C-2.78,5.89 -1.29,4.47 -1.29,4.47 C-1.29,4.47 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 0.36,8.83 0.36,8.83 C0.36,8.83 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="28"
+ android:propertyName="pathData"
+ android:startOffset="166"
+ android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 0.36,8.83 0.36,8.83 C0.36,8.83 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueTo="M8.82 3.18 C8.26,3.74 3.22,8.8 3.22,8.8 C3.22,8.8 3.21,8.79 3.21,8.79 C3.21,8.79 3.24,8.8 3.24,8.8 C3.24,8.8 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="55"
+ android:propertyName="pathData"
+ android:startOffset="193"
+ android:valueFrom="M8.82 3.18 C8.26,3.74 3.22,8.8 3.22,8.8 C3.22,8.8 3.21,8.79 3.21,8.79 C3.21,8.79 3.24,8.8 3.24,8.8 C3.24,8.8 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueTo="M8.82 3.18 C8.26,3.74 5.82,6.14 5.82,6.14 C5.82,6.14 5.81,6.14 5.81,6.14 C5.81,6.14 5.83,6.14 5.83,6.14 C5.83,6.14 4.39,4.78 4.39,4.78 C4.39,4.78 4.37,4.76 4.37,4.76 C4.37,4.76 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="41"
+ android:propertyName="pathData"
+ android:startOffset="248"
+ android:valueFrom="M8.82 3.18 C8.26,3.74 5.82,6.14 5.82,6.14 C5.82,6.14 5.81,6.14 5.81,6.14 C5.81,6.14 5.83,6.14 5.83,6.14 C5.83,6.14 4.39,4.78 4.39,4.78 C4.39,4.78 4.37,4.76 4.37,4.76 C4.37,4.76 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueTo="M8.91 3.09 C8.91,3.09 8.91,3.1 8.91,3.1 C8.91,3.1 8.9,3.09 8.9,3.09 C8.9,3.09 7.49,1.74 7.49,1.74 C7.49,1.74 7.5,1.75 7.5,1.75 C7.5,1.75 7.48,1.73 7.48,1.73 C7.48,1.73 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.79,2.2 8.91,3.09c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="28"
+ android:propertyName="pathData"
+ android:startOffset="290"
+ android:valueFrom="M8.91 3.09 C8.91,3.09 8.91,3.1 8.91,3.1 C8.91,3.1 8.9,3.09 8.9,3.09 C8.9,3.09 7.49,1.74 7.49,1.74 C7.49,1.74 7.5,1.75 7.5,1.75 C7.5,1.75 7.48,1.73 7.48,1.73 C7.48,1.73 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.79,2.2 8.91,3.09c "
+ android:valueTo="M9.05 0.57 C9.05,0.57 9.05,0.58 9.05,0.58 C9.05,0.58 9.04,0.57 9.04,0.57 C9.04,0.57 7.6,1.83 7.6,1.83 C7.6,1.83 7.61,1.85 7.61,1.85 C7.61,1.85 7.59,1.83 7.59,1.83 C7.59,1.83 7.61,1.86 7.61,1.86 C7.61,1.86 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 9.05,0.58 9.05,0.58 C9.05,0.58 9.05,0.57 9.05,0.57c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="pathData"
+ android:startOffset="317"
+ android:valueFrom="M9.05 0.57 C9.05,0.57 9.05,0.58 9.05,0.58 C9.05,0.58 9.04,0.57 9.04,0.57 C9.04,0.57 7.6,1.83 7.6,1.83 C7.6,1.83 7.61,1.85 7.61,1.85 C7.61,1.85 7.59,1.83 7.59,1.83 C7.59,1.83 7.61,1.86 7.61,1.86 C7.61,1.86 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 9.05,0.58 9.05,0.58 C9.05,0.58 9.05,0.57 9.05,0.57c "
+ android:valueTo="M5.94 -2.52 C5.94,-2.52 5.94,-2.51 5.94,-2.51 C5.94,-2.51 5.93,-2.52 5.93,-2.52 C5.93,-2.52 4.53,-1.12 4.53,-1.12 C4.53,-1.12 4.55,-1.11 4.55,-1.11 C4.55,-1.11 4.53,-1.13 4.53,-1.13 C4.53,-1.13 4.55,-1.09 4.55,-1.09 C4.55,-1.09 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 5.94,-2.52 5.94,-2.52 C5.94,-2.52 5.94,-2.52 5.94,-2.52c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="28"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.95 7.53,-0.95 C7.53,-0.95 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -7.14,-2.14 -7.14,-2.14 C-7.14,-2.14 -8.48,-3.49 -8.48,-3.49 C-8.48,-3.49 -8.5,-3.51 -8.5,-3.51 C-8.5,-3.51 -8.52,-3.53 -8.52,-3.53c "
+ android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 6.08,-2.36 6.08,-2.36 C6.08,-2.36 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -7.49,-1.78 -7.49,-1.78 C-7.49,-1.78 -8.87,-0.41 -8.87,-0.41 C-8.87,-0.41 -8.95,-0.5 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="66"
+ android:propertyName="pathData"
+ android:startOffset="28"
+ android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 6.08,-2.36 6.08,-2.36 C6.08,-2.36 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -7.49,-1.78 -7.49,-1.78 C-7.49,-1.78 -8.87,-0.41 -8.87,-0.41 C-8.87,-0.41 -8.95,-0.5 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 2.78,-5.89 2.78,-5.89 C2.78,-5.89 1.29,-4.47 1.29,-4.47 C1.29,-4.47 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="72"
+ android:propertyName="pathData"
+ android:startOffset="94"
+ android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 2.78,-5.89 2.78,-5.89 C2.78,-5.89 1.29,-4.47 1.29,-4.47 C1.29,-4.47 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 -0.36,-8.83 -0.36,-8.83 C-0.36,-8.83 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="28"
+ android:propertyName="pathData"
+ android:startOffset="166"
+ android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 -0.36,-8.83 -0.36,-8.83 C-0.36,-8.83 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -3.22,-8.8 -3.22,-8.8 C-3.22,-8.8 -3.21,-8.79 -3.21,-8.79 C-3.21,-8.79 -3.24,-8.8 -3.24,-8.8 C-3.24,-8.8 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="55"
+ android:propertyName="pathData"
+ android:startOffset="193"
+ android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -3.22,-8.8 -3.22,-8.8 C-3.22,-8.8 -3.21,-8.79 -3.21,-8.79 C-3.21,-8.79 -3.24,-8.8 -3.24,-8.8 C-3.24,-8.8 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -5.81,-6.14 -5.81,-6.14 C-5.81,-6.14 -5.81,-6.13 -5.81,-6.13 C-5.81,-6.13 -5.83,-6.14 -5.83,-6.14 C-5.83,-6.14 -4.39,-4.78 -4.39,-4.78 C-4.39,-4.78 -4.37,-4.76 -4.37,-4.76 C-4.37,-4.76 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="41"
+ android:propertyName="pathData"
+ android:startOffset="248"
+ android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -5.81,-6.14 -5.81,-6.14 C-5.81,-6.14 -5.81,-6.13 -5.81,-6.13 C-5.81,-6.13 -5.83,-6.14 -5.83,-6.14 C-5.83,-6.14 -4.39,-4.78 -4.39,-4.78 C-4.39,-4.78 -4.37,-4.76 -4.37,-4.76 C-4.37,-4.76 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueTo="M-8.91 -3.09 C-8.91,-3.09 -8.91,-3.09 -8.91,-3.09 C-8.91,-3.09 -8.9,-3.09 -8.9,-3.09 C-8.9,-3.09 -7.49,-1.74 -7.49,-1.74 C-7.49,-1.74 -7.5,-1.75 -7.5,-1.75 C-7.5,-1.75 -7.48,-1.73 -7.48,-1.73 C-7.48,-1.73 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.79,-2.2 -8.91,-3.09c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="28"
+ android:propertyName="pathData"
+ android:startOffset="290"
+ android:valueFrom="M-8.91 -3.09 C-8.91,-3.09 -8.91,-3.09 -8.91,-3.09 C-8.91,-3.09 -8.9,-3.09 -8.9,-3.09 C-8.9,-3.09 -7.49,-1.74 -7.49,-1.74 C-7.49,-1.74 -7.5,-1.75 -7.5,-1.75 C-7.5,-1.75 -7.48,-1.73 -7.48,-1.73 C-7.48,-1.73 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.79,-2.2 -8.91,-3.09c "
+ android:valueTo="M-9.05 -0.57 C-9.05,-0.57 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.04,-0.57 -9.04,-0.57 C-9.04,-0.57 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.85 -7.61,-1.85 C-7.61,-1.85 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.86 -7.61,-1.86 C-7.61,-1.86 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.05,-0.57 -9.05,-0.57c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="pathData"
+ android:startOffset="317"
+ android:valueFrom="M-9.05 -0.57 C-9.05,-0.57 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.04,-0.57 -9.04,-0.57 C-9.04,-0.57 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.85 -7.61,-1.85 C-7.61,-1.85 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.86 -7.61,-1.86 C-7.61,-1.86 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.05,-0.57 -9.05,-0.57c "
+ android:valueTo="M-5.94 2.52 C-5.94,2.52 -5.94,2.51 -5.94,2.51 C-5.94,2.51 -5.93,2.52 -5.93,2.52 C-5.93,2.52 -4.53,1.12 -4.53,1.12 C-4.53,1.12 -4.55,1.11 -4.55,1.11 C-4.55,1.11 -4.53,1.13 -4.53,1.13 C-4.53,1.13 -4.55,1.09 -4.55,1.09 C-4.55,1.09 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -5.94,2.52 -5.94,2.52 C-5.94,2.52 -5.94,2.52 -5.94,2.52c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="61"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c "
+ android:valueTo="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="28"
+ android:propertyName="pathData"
+ android:startOffset="61"
+ android:valueFrom="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c "
+ android:valueTo="M6.15 -2.35 C6.15,-2.35 6.15,-2.35 6.15,-2.35 C6.16,-2.34 6.15,-2.35 6.16,-2.35 C6.16,-2.35 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 4.72,-0.93 4.72,-0.93 C4.72,-0.93 4.7,-0.94 4.7,-0.94 C4.7,-0.94 6.15,-2.35 6.15,-2.35c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="143"
+ android:propertyName="pathData"
+ android:startOffset="88"
+ android:valueFrom="M6.15 -2.35 C6.15,-2.35 6.15,-2.35 6.15,-2.35 C6.16,-2.34 6.15,-2.35 6.16,-2.35 C6.16,-2.35 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 4.72,-0.93 4.72,-0.93 C4.72,-0.93 4.7,-0.94 4.7,-0.94 C4.7,-0.94 6.15,-2.35 6.15,-2.35c "
+ android:valueTo="M-0.65 -9.12 C-0.65,-9.12 -0.66,-9.13 -0.66,-9.13 C-0.65,-9.12 -0.66,-9.12 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -0.65,-9.12 -0.65,-9.12c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="232"
+ android:valueFrom="M-0.65 -9.12 C-0.65,-9.12 -0.66,-9.13 -0.66,-9.13 C-0.65,-9.12 -0.66,-9.12 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -0.65,-9.12 -0.65,-9.12c "
+ android:valueTo="M-3.21 -8.85 C-3.21,-8.85 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -3.21,-8.85 -3.21,-8.85c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="418"
+ android:propertyName="pathData"
+ android:startOffset="248"
+ android:valueFrom="M-3.21 -8.85 C-3.21,-8.85 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -3.21,-8.85 -3.21,-8.85c "
+ android:valueTo="M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -8.52,-3.53 -8.52,-3.53c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="61"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c "
+ android:valueTo="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="28"
+ android:propertyName="pathData"
+ android:startOffset="61"
+ android:valueFrom="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c "
+ android:valueTo="M-6.15 2.35 C-6.15,2.35 -6.15,2.35 -6.15,2.35 C-6.16,2.34 -6.15,2.35 -6.15,2.35 C-6.15,2.35 -6.14,2.36 -6.14,2.36 C-6.14,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -4.72,0.93 -4.72,0.93 C-4.72,0.93 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -6.15,2.35 -6.15,2.35c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="143"
+ android:propertyName="pathData"
+ android:startOffset="88"
+ android:valueFrom="M-6.15 2.35 C-6.15,2.35 -6.15,2.35 -6.15,2.35 C-6.16,2.34 -6.15,2.35 -6.15,2.35 C-6.15,2.35 -6.14,2.36 -6.14,2.36 C-6.14,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -4.72,0.93 -4.72,0.93 C-4.72,0.93 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -6.15,2.35 -6.15,2.35c "
+ android:valueTo="M0.65 9.12 C0.65,9.12 0.66,9.13 0.66,9.13 C0.65,9.12 0.66,9.13 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 0.65,9.12 0.65,9.12c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="232"
+ android:valueFrom="M0.65 9.12 C0.65,9.12 0.66,9.13 0.66,9.13 C0.65,9.12 0.66,9.13 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 0.65,9.12 0.65,9.12c "
+ android:valueTo="M3.21 8.85 C3.21,8.85 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 3.21,8.85 3.21,8.85c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="418"
+ android:propertyName="pathData"
+ android:startOffset="248"
+ android:valueFrom="M3.21 8.85 C3.21,8.85 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 3.21,8.85 3.21,8.85c "
+ android:valueTo="M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 8.52,3.53 8.52,3.53c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="-135"
+ android:valueTo="-180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="683"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_2_G_N_1_T_0"
+ android:rotation="-135"
+ android:translateX="12.008"
+ android:translateY="11.992">
+ <group
+ android:name="_R_G_L_2_G_T_1"
+ android:rotation="180"
+ android:translateX="7.062"
+ android:translateY="3.312">
+ <group
+ android:name="_R_G_L_2_G"
+ android:translateX="6.984"
+ android:translateY="3.547">
+ <path
+ android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-4.6 -1.06 C-4.6,-1.06 -9.55,-1.06 -9.55,-1.06 C-9.55,-1.06 -9.55,-6.01 -9.55,-6.01 C-9.55,-6.01 -7.55,-6 -7.55,-6 C-7.55,-6 -7.51,-3.04 -7.51,-3.04 C-7.51,-3.04 -4.6,-3.05 -4.6,-3.05 C-4.6,-3.05 -4.6,-1.06 -4.6,-1.06c " />
+ </group>
+ </group>
+ </group>
+ <group
+ android:name="_R_G_L_1_G_N_1_T_0"
+ android:rotation="-135"
+ android:translateX="12.008"
+ android:translateY="11.992">
+ <group
+ android:name="_R_G_L_1_G_T_1"
+ android:rotation="360"
+ android:translateX="-7"
+ android:translateY="-3.594">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="6.984"
+ android:translateY="3.547">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-4.6 -1.06 C-4.6,-1.06 -9.55,-1.06 -9.55,-1.06 C-9.55,-1.06 -9.55,-6.01 -9.55,-6.01 C-9.55,-6.01 -7.55,-6 -7.55,-6 C-7.55,-6 -7.51,-3.04 -7.51,-3.04 C-7.51,-3.04 -4.6,-3.05 -4.6,-3.05 C-4.6,-3.05 -4.6,-1.06 -4.6,-1.06c " />
+ </group>
+ </group>
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:rotation="-135"
+ android:translateX="12.008"
+ android:translateY="11.992">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.95 -7.53,0.95 C-7.53,0.95 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 7.14,2.14 7.14,2.14 C7.14,2.14 8.48,3.49 8.48,3.49 C8.48,3.49 8.5,3.51 8.5,3.51 C8.5,3.51 8.52,3.53 8.52,3.53c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.95 7.53,-0.95 C7.53,-0.95 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -7.14,-2.14 -7.14,-2.14 C-7.14,-2.14 -8.48,-3.49 -8.48,-3.49 C-8.48,-3.49 -8.5,-3.51 -8.5,-3.51 C-8.5,-3.51 -8.52,-3.53 -8.52,-3.53c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " />
+ <path
+ android:name="_R_G_L_0_G_D_3_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_on.xml b/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_on.xml
new file mode 100644
index 000000000000..bd67d9f8dbaa
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_on.xml
@@ -0,0 +1,781 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="367"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="fillAlpha"
+ android:startOffset="367"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.95 -7.53,0.95 C-7.53,0.95 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 7.14,2.14 7.14,2.14 C7.14,2.14 8.48,3.49 8.48,3.49 C8.48,3.49 8.5,3.51 8.5,3.51 C8.5,3.51 8.52,3.53 8.52,3.53c "
+ android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -6.08,2.36 -6.08,2.36 C-6.08,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 7.49,1.78 7.49,1.78 C7.49,1.78 8.87,0.41 8.87,0.41 C8.87,0.41 8.95,0.5 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="41"
+ android:propertyName="pathData"
+ android:startOffset="17"
+ android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -6.08,2.36 -6.08,2.36 C-6.08,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 7.49,1.78 7.49,1.78 C7.49,1.78 8.87,0.41 8.87,0.41 C8.87,0.41 8.95,0.5 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -2.78,5.89 -2.78,5.89 C-2.78,5.89 -1.29,4.47 -1.29,4.47 C-1.29,4.47 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="45"
+ android:propertyName="pathData"
+ android:startOffset="59"
+ android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -2.78,5.89 -2.78,5.89 C-2.78,5.89 -1.29,4.47 -1.29,4.47 C-1.29,4.47 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 0.36,8.83 0.36,8.83 C0.36,8.83 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="103"
+ android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 0.36,8.83 0.36,8.83 C0.36,8.83 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueTo="M8.82 3.18 C8.26,3.74 3.22,8.8 3.22,8.8 C3.22,8.8 3.21,8.79 3.21,8.79 C3.21,8.79 3.24,8.8 3.24,8.8 C3.24,8.8 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="34"
+ android:propertyName="pathData"
+ android:startOffset="121"
+ android:valueFrom="M8.82 3.18 C8.26,3.74 3.22,8.8 3.22,8.8 C3.22,8.8 3.21,8.79 3.21,8.79 C3.21,8.79 3.24,8.8 3.24,8.8 C3.24,8.8 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueTo="M8.82 3.18 C8.26,3.74 5.82,6.14 5.82,6.14 C5.82,6.14 5.81,6.14 5.81,6.14 C5.81,6.14 5.83,6.14 5.83,6.14 C5.83,6.14 4.39,4.78 4.39,4.78 C4.39,4.78 4.37,4.76 4.37,4.76 C4.37,4.76 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="26"
+ android:propertyName="pathData"
+ android:startOffset="155"
+ android:valueFrom="M8.82 3.18 C8.26,3.74 5.82,6.14 5.82,6.14 C5.82,6.14 5.81,6.14 5.81,6.14 C5.81,6.14 5.83,6.14 5.83,6.14 C5.83,6.14 4.39,4.78 4.39,4.78 C4.39,4.78 4.37,4.76 4.37,4.76 C4.37,4.76 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c "
+ android:valueTo="M8.91 3.09 C8.91,3.09 8.91,3.1 8.91,3.1 C8.91,3.1 8.9,3.09 8.9,3.09 C8.9,3.09 7.49,1.74 7.49,1.74 C7.49,1.74 7.5,1.75 7.5,1.75 C7.5,1.75 7.48,1.73 7.48,1.73 C7.48,1.73 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.79,2.2 8.91,3.09c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="181"
+ android:valueFrom="M8.91 3.09 C8.91,3.09 8.91,3.1 8.91,3.1 C8.91,3.1 8.9,3.09 8.9,3.09 C8.9,3.09 7.49,1.74 7.49,1.74 C7.49,1.74 7.5,1.75 7.5,1.75 C7.5,1.75 7.48,1.73 7.48,1.73 C7.48,1.73 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.79,2.2 8.91,3.09c "
+ android:valueTo="M9.05 0.57 C9.05,0.57 9.05,0.58 9.05,0.58 C9.05,0.58 9.04,0.57 9.04,0.57 C9.04,0.57 7.6,1.83 7.6,1.83 C7.6,1.83 7.61,1.85 7.61,1.85 C7.61,1.85 7.59,1.83 7.59,1.83 C7.59,1.83 7.61,1.86 7.61,1.86 C7.61,1.86 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 9.05,0.58 9.05,0.58 C9.05,0.58 9.05,0.57 9.05,0.57c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="52"
+ android:propertyName="pathData"
+ android:startOffset="198"
+ android:valueFrom="M9.05 0.57 C9.05,0.57 9.05,0.58 9.05,0.58 C9.05,0.58 9.04,0.57 9.04,0.57 C9.04,0.57 7.6,1.83 7.6,1.83 C7.6,1.83 7.61,1.85 7.61,1.85 C7.61,1.85 7.59,1.83 7.59,1.83 C7.59,1.83 7.61,1.86 7.61,1.86 C7.61,1.86 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 9.05,0.58 9.05,0.58 C9.05,0.58 9.05,0.57 9.05,0.57c "
+ android:valueTo="M5.94 -2.52 C5.94,-2.52 5.94,-2.51 5.94,-2.51 C5.94,-2.51 5.93,-2.52 5.93,-2.52 C5.93,-2.52 4.53,-1.12 4.53,-1.12 C4.53,-1.12 4.55,-1.11 4.55,-1.11 C4.55,-1.11 4.53,-1.13 4.53,-1.13 C4.53,-1.13 4.55,-1.09 4.55,-1.09 C4.55,-1.09 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 5.94,-2.52 5.94,-2.52 C5.94,-2.52 5.94,-2.52 5.94,-2.52c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="367"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="fillAlpha"
+ android:startOffset="367"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.95 7.53,-0.95 C7.53,-0.95 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -7.14,-2.14 -7.14,-2.14 C-7.14,-2.14 -8.48,-3.49 -8.48,-3.49 C-8.48,-3.49 -8.5,-3.51 -8.5,-3.51 C-8.5,-3.51 -8.52,-3.53 -8.52,-3.53c "
+ android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 6.08,-2.36 6.08,-2.36 C6.08,-2.36 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -7.49,-1.78 -7.49,-1.78 C-7.49,-1.78 -8.87,-0.41 -8.87,-0.41 C-8.87,-0.41 -8.95,-0.5 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="41"
+ android:propertyName="pathData"
+ android:startOffset="17"
+ android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 6.08,-2.36 6.08,-2.36 C6.08,-2.36 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -7.49,-1.78 -7.49,-1.78 C-7.49,-1.78 -8.87,-0.41 -8.87,-0.41 C-8.87,-0.41 -8.95,-0.5 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 2.78,-5.89 2.78,-5.89 C2.78,-5.89 1.29,-4.47 1.29,-4.47 C1.29,-4.47 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="45"
+ android:propertyName="pathData"
+ android:startOffset="59"
+ android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 2.78,-5.89 2.78,-5.89 C2.78,-5.89 1.29,-4.47 1.29,-4.47 C1.29,-4.47 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 -0.36,-8.83 -0.36,-8.83 C-0.36,-8.83 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="103"
+ android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 -0.36,-8.83 -0.36,-8.83 C-0.36,-8.83 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -3.22,-8.8 -3.22,-8.8 C-3.22,-8.8 -3.21,-8.79 -3.21,-8.79 C-3.21,-8.79 -3.24,-8.8 -3.24,-8.8 C-3.24,-8.8 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="34"
+ android:propertyName="pathData"
+ android:startOffset="121"
+ android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -3.22,-8.8 -3.22,-8.8 C-3.22,-8.8 -3.21,-8.79 -3.21,-8.79 C-3.21,-8.79 -3.24,-8.8 -3.24,-8.8 C-3.24,-8.8 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -5.81,-6.14 -5.81,-6.14 C-5.81,-6.14 -5.81,-6.13 -5.81,-6.13 C-5.81,-6.13 -5.83,-6.14 -5.83,-6.14 C-5.83,-6.14 -4.39,-4.78 -4.39,-4.78 C-4.39,-4.78 -4.37,-4.76 -4.37,-4.76 C-4.37,-4.76 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="26"
+ android:propertyName="pathData"
+ android:startOffset="155"
+ android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -5.81,-6.14 -5.81,-6.14 C-5.81,-6.14 -5.81,-6.13 -5.81,-6.13 C-5.81,-6.13 -5.83,-6.14 -5.83,-6.14 C-5.83,-6.14 -4.39,-4.78 -4.39,-4.78 C-4.39,-4.78 -4.37,-4.76 -4.37,-4.76 C-4.37,-4.76 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c "
+ android:valueTo="M-8.91 -3.09 C-8.91,-3.09 -8.91,-3.09 -8.91,-3.09 C-8.91,-3.09 -8.9,-3.09 -8.9,-3.09 C-8.9,-3.09 -7.49,-1.74 -7.49,-1.74 C-7.49,-1.74 -7.5,-1.75 -7.5,-1.75 C-7.5,-1.75 -7.48,-1.73 -7.48,-1.73 C-7.48,-1.73 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.79,-2.2 -8.91,-3.09c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="181"
+ android:valueFrom="M-8.91 -3.09 C-8.91,-3.09 -8.91,-3.09 -8.91,-3.09 C-8.91,-3.09 -8.9,-3.09 -8.9,-3.09 C-8.9,-3.09 -7.49,-1.74 -7.49,-1.74 C-7.49,-1.74 -7.5,-1.75 -7.5,-1.75 C-7.5,-1.75 -7.48,-1.73 -7.48,-1.73 C-7.48,-1.73 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.79,-2.2 -8.91,-3.09c "
+ android:valueTo="M-9.05 -0.57 C-9.05,-0.57 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.04,-0.57 -9.04,-0.57 C-9.04,-0.57 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.85 -7.61,-1.85 C-7.61,-1.85 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.86 -7.61,-1.86 C-7.61,-1.86 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.05,-0.57 -9.05,-0.57c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="52"
+ android:propertyName="pathData"
+ android:startOffset="198"
+ android:valueFrom="M-9.05 -0.57 C-9.05,-0.57 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.04,-0.57 -9.04,-0.57 C-9.04,-0.57 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.85 -7.61,-1.85 C-7.61,-1.85 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.86 -7.61,-1.86 C-7.61,-1.86 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.05,-0.57 -9.05,-0.57c "
+ android:valueTo="M-5.94 2.52 C-5.94,2.52 -5.94,2.51 -5.94,2.51 C-5.94,2.51 -5.93,2.52 -5.93,2.52 C-5.93,2.52 -4.53,1.12 -4.53,1.12 C-4.53,1.12 -4.55,1.11 -4.55,1.11 C-4.55,1.11 -4.53,1.13 -4.53,1.13 C-4.53,1.13 -4.55,1.09 -4.55,1.09 C-4.55,1.09 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -5.94,2.52 -5.94,2.52 C-5.94,2.52 -5.94,2.52 -5.94,2.52c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="38"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c "
+ android:valueTo="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="38"
+ android:valueFrom="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c "
+ android:valueTo="M6.15 -2.35 C6.15,-2.35 6.15,-2.35 6.15,-2.35 C6.16,-2.34 6.15,-2.35 6.16,-2.35 C6.16,-2.35 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 4.72,-0.93 4.72,-0.93 C4.72,-0.93 4.7,-0.94 4.7,-0.94 C4.7,-0.94 6.15,-2.35 6.15,-2.35c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="90"
+ android:propertyName="pathData"
+ android:startOffset="55"
+ android:valueFrom="M6.15 -2.35 C6.15,-2.35 6.15,-2.35 6.15,-2.35 C6.16,-2.34 6.15,-2.35 6.16,-2.35 C6.16,-2.35 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 4.72,-0.93 4.72,-0.93 C4.72,-0.93 4.7,-0.94 4.7,-0.94 C4.7,-0.94 6.15,-2.35 6.15,-2.35c "
+ android:valueTo="M-0.65 -9.12 C-0.65,-9.12 -0.66,-9.13 -0.66,-9.13 C-0.65,-9.12 -0.66,-9.12 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -0.65,-9.12 -0.65,-9.12c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="10"
+ android:propertyName="pathData"
+ android:startOffset="145"
+ android:valueFrom="M-0.65 -9.12 C-0.65,-9.12 -0.66,-9.13 -0.66,-9.13 C-0.65,-9.12 -0.66,-9.12 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -0.65,-9.12 -0.65,-9.12c "
+ android:valueTo="M-3.21 -8.85 C-3.21,-8.85 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -3.21,-8.85 -3.21,-8.85c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="261"
+ android:propertyName="pathData"
+ android:startOffset="155"
+ android:valueFrom="M-3.21 -8.85 C-3.21,-8.85 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -3.21,-8.85 -3.21,-8.85c "
+ android:valueTo="M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -8.52,-3.53 -8.52,-3.53c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_D_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="38"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c "
+ android:valueTo="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="38"
+ android:valueFrom="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c "
+ android:valueTo="M-6.15 2.35 C-6.15,2.35 -6.15,2.35 -6.15,2.35 C-6.16,2.34 -6.15,2.35 -6.15,2.35 C-6.15,2.35 -6.14,2.36 -6.14,2.36 C-6.14,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -4.72,0.93 -4.72,0.93 C-4.72,0.93 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -6.15,2.35 -6.15,2.35c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="90"
+ android:propertyName="pathData"
+ android:startOffset="55"
+ android:valueFrom="M-6.15 2.35 C-6.15,2.35 -6.15,2.35 -6.15,2.35 C-6.16,2.34 -6.15,2.35 -6.15,2.35 C-6.15,2.35 -6.14,2.36 -6.14,2.36 C-6.14,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -4.72,0.93 -4.72,0.93 C-4.72,0.93 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -6.15,2.35 -6.15,2.35c "
+ android:valueTo="M0.65 9.12 C0.65,9.12 0.66,9.13 0.66,9.13 C0.65,9.12 0.66,9.13 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 0.65,9.12 0.65,9.12c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="10"
+ android:propertyName="pathData"
+ android:startOffset="145"
+ android:valueFrom="M0.65 9.12 C0.65,9.12 0.66,9.13 0.66,9.13 C0.65,9.12 0.66,9.13 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 0.65,9.12 0.65,9.12c "
+ android:valueTo="M3.21 8.85 C3.21,8.85 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 3.21,8.85 3.21,8.85c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="261"
+ android:propertyName="pathData"
+ android:startOffset="155"
+ android:valueFrom="M3.21 8.85 C3.21,8.85 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 3.21,8.85 3.21,8.85c "
+ android:valueTo="M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 8.52,3.53 8.52,3.53c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="367"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="-135"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="-7"
+ android:valueTo="-8.859"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="138"
+ android:propertyName="translateX"
+ android:startOffset="17"
+ android:valueFrom="-8.859"
+ android:valueTo="1.734"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="261"
+ android:propertyName="translateX"
+ android:startOffset="155"
+ android:valueFrom="1.734"
+ android:valueTo="7"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="translateY"
+ android:startOffset="0"
+ android:valueFrom="-3.594"
+ android:valueTo="-1.922"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="138"
+ android:propertyName="translateY"
+ android:startOffset="17"
+ android:valueFrom="-1.922"
+ android:valueTo="8.671"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="261"
+ android:propertyName="translateY"
+ android:startOffset="155"
+ android:valueFrom="8.671"
+ android:valueTo="3.5"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="360"
+ android:valueTo="270"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="103"
+ android:propertyName="rotation"
+ android:startOffset="17"
+ android:valueFrom="270"
+ android:valueTo="270"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="34"
+ android:propertyName="rotation"
+ android:startOffset="121"
+ android:valueFrom="270"
+ android:valueTo="180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="261"
+ android:propertyName="rotation"
+ android:startOffset="155"
+ android:valueFrom="180"
+ android:valueTo="180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_N_3_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="367"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="-135"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="7.062"
+ android:valueTo="8.578"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="138"
+ android:propertyName="translateX"
+ android:startOffset="17"
+ android:valueFrom="8.578"
+ android:valueTo="-1.656"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="261"
+ android:propertyName="translateX"
+ android:startOffset="155"
+ android:valueFrom="-1.656"
+ android:valueTo="-7"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="translateY"
+ android:startOffset="0"
+ android:valueFrom="3.312"
+ android:valueTo="2.016"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="138"
+ android:propertyName="translateY"
+ android:startOffset="17"
+ android:valueFrom="2.016"
+ android:valueTo="-8.656"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="261"
+ android:propertyName="translateY"
+ android:startOffset="155"
+ android:valueFrom="-8.656"
+ android:valueTo="-3.5"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="180"
+ android:valueTo="90"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="103"
+ android:propertyName="rotation"
+ android:startOffset="17"
+ android:valueFrom="90"
+ android:valueTo="90"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="34"
+ android:propertyName="rotation"
+ android:startOffset="121"
+ android:valueFrom="90"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_N_3_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="367"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="-135"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="433"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_2_G"
+ android:rotation="0"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.95 -7.53,0.95 C-7.53,0.95 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 7.14,2.14 7.14,2.14 C7.14,2.14 8.48,3.49 8.48,3.49 C8.48,3.49 8.5,3.51 8.5,3.51 C8.5,3.51 8.52,3.53 8.52,3.53c " />
+ <path
+ android:name="_R_G_L_2_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.95 7.53,-0.95 C7.53,-0.95 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -7.14,-2.14 -7.14,-2.14 C-7.14,-2.14 -8.48,-3.49 -8.48,-3.49 C-8.48,-3.49 -8.5,-3.51 -8.5,-3.51 C-8.5,-3.51 -8.52,-3.53 -8.52,-3.53c " />
+ <path
+ android:name="_R_G_L_2_G_D_2_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " />
+ <path
+ android:name="_R_G_L_2_G_D_3_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " />
+ </group>
+ <group
+ android:name="_R_G_L_1_G_N_3_T_0"
+ android:rotation="0"
+ android:translateX="12"
+ android:translateY="12">
+ <group
+ android:name="_R_G_L_1_G_T_1"
+ android:rotation="360"
+ android:translateX="-7"
+ android:translateY="-3.594">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="6.984"
+ android:translateY="3.547">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-4.6 -1.06 C-4.6,-1.06 -9.55,-1.06 -9.55,-1.06 C-9.55,-1.06 -9.55,-6.01 -9.55,-6.01 C-9.55,-6.01 -7.55,-6 -7.55,-6 C-7.55,-6 -7.51,-3.04 -7.51,-3.04 C-7.51,-3.04 -4.6,-3.05 -4.6,-3.05 C-4.6,-3.05 -4.6,-1.06 -4.6,-1.06c " />
+ </group>
+ </group>
+ </group>
+ <group
+ android:name="_R_G_L_0_G_N_3_T_0"
+ android:rotation="0"
+ android:translateX="12"
+ android:translateY="12">
+ <group
+ android:name="_R_G_L_0_G_T_1"
+ android:rotation="180"
+ android:translateX="7.062"
+ android:translateY="3.312">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="6.984"
+ android:translateY="3.547">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-4.6 -1.06 C-4.6,-1.06 -9.55,-1.06 -9.55,-1.06 C-9.55,-1.06 -9.55,-6.01 -9.55,-6.01 C-9.55,-6.01 -7.55,-6 -7.55,-6 C-7.55,-6 -7.51,-3.04 -7.51,-3.04 C-7.51,-3.04 -4.6,-3.05 -4.6,-3.05 C-4.6,-3.05 -4.6,-1.06 -4.6,-1.06c " />
+ </group>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_off.xml b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_off.xml
new file mode 100644
index 000000000000..17a76116db25
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_off.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_2_G"
+ android:translateX="14.125"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-1 6.17 C-1,6.17 0.88,4.29 0.88,4.29 C0.88,4.29 -1,2.41 -1,2.41 C-1,2.41 -1,6.17 -1,6.17c M0.88 -4.29 C0.88,-4.29 -1,-6.17 -1,-6.17 C-1,-6.17 -1,-2.41 -1,-2.41 C-1,-2.41 0.88,-4.29 0.88,-4.29c M-2 -10 C-2,-10 3.71,-4.29 3.71,-4.29 C3.71,-4.29 -0.59,0 -0.59,0 C-0.59,0 3.71,4.29 3.71,4.29 C3.71,4.29 -2,10 -2,10 C-2,10 -3,10 -3,10 C-3,10 -3,2.41 -3,2.41 C-3,2.41 -7.59,7 -7.59,7 C-7.59,7 -9.01,5.59 -9.01,5.59 C-9.01,5.59 -3.41,0 -3.41,0 C-3.41,0 -9.01,-5.59 -9.01,-5.59 C-9.01,-5.59 -7.59,-7 -7.59,-7 C-7.59,-7 -3,-2.41 -3,-2.41 C-3,-2.41 -3,-10 -3,-10 C-3,-10 -2,-10 -2,-10c "/>
+ </group>
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="14.125"
+ android:translateY="12"
+ android:pivotX="-9.109"
+ android:scaleX="1"
+ android:scaleY="1">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-9.09 -1.5 C-8.26,-1.5 -7.59,-0.83 -7.59,0 C-7.59,0.83 -8.26,1.5 -9.09,1.5 C-9.91,1.5 -10.59,0.83 -10.59,0 C-10.59,-0.83 -9.91,-1.5 -9.09,-1.5c "/>
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="14.125"
+ android:translateY="12"
+ android:pivotX="4.875"
+ android:scaleX="1"
+ android:scaleY="1">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M4.92 -1.5 C5.75,-1.5 6.42,-0.83 6.42,0 C6.42,0.83 5.75,1.5 4.92,1.5 C4.09,1.5 3.42,0.83 3.42,0 C3.42,-0.83 4.09,-1.5 4.92,-1.5c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="scaleX"
+ android:duration="150"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:duration="150"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr
+ name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="scaleX"
+ android:duration="150"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:duration="150"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator
+ android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:propertyName="translateX"
+ android:duration="150"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_on.xml b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_on.xml
new file mode 100644
index 000000000000..2dba48cf155d
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_on.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="267"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_2_G"
+ android:translateX="14.125"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-1 6.17 C-1,6.17 0.88,4.29 0.88,4.29 C0.88,4.29 -1,2.41 -1,2.41 C-1,2.41 -1,6.17 -1,6.17c M0.88 -4.29 C0.88,-4.29 -1,-6.17 -1,-6.17 C-1,-6.17 -1,-2.41 -1,-2.41 C-1,-2.41 0.88,-4.29 0.88,-4.29c M-2 -10 C-2,-10 3.71,-4.29 3.71,-4.29 C3.71,-4.29 -0.59,0 -0.59,0 C-0.59,0 3.71,4.29 3.71,4.29 C3.71,4.29 -2,10 -2,10 C-2,10 -3,10 -3,10 C-3,10 -3,2.41 -3,2.41 C-3,2.41 -7.59,7 -7.59,7 C-7.59,7 -9.01,5.59 -9.01,5.59 C-9.01,5.59 -3.41,0 -3.41,0 C-3.41,0 -9.01,-5.59 -9.01,-5.59 C-9.01,-5.59 -7.59,-7 -7.59,-7 C-7.59,-7 -3,-2.41 -3,-2.41 C-3,-2.41 -3,-10 -3,-10 C-3,-10 -2,-10 -2,-10c " />
+ </group>
+ <group
+ android:name="_R_G_L_1_G"
+ android:pivotX="-9.109"
+ android:scaleX="0"
+ android:scaleY="0"
+ android:translateX="14.125"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-9.09 -1.5 C-8.26,-1.5 -7.59,-0.83 -7.59,0 C-7.59,0.83 -8.26,1.5 -9.09,1.5 C-9.91,1.5 -10.59,0.83 -10.59,0 C-10.59,-0.83 -9.91,-1.5 -9.09,-1.5c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:pivotX="4.875"
+ android:scaleX="0"
+ android:scaleY="0"
+ android:translateX="14.125"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M4.92 -1.5 C5.75,-1.5 6.42,-0.83 6.42,0 C6.42,0.83 5.75,1.5 4.92,1.5 C4.09,1.5 3.42,0.83 3.42,0 C3.42,-0.83 4.09,-1.5 4.92,-1.5c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_search.xml b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_search.xml
new file mode 100644
index 000000000000..3697769cfa34
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_search.xml
@@ -0,0 +1,268 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="167"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="333"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="167"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="333"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="500"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="167"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="333"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_4_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="167"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="333"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="500"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="1017"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="14.125"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-1 6.17 C-1,6.17 0.88,4.29 0.88,4.29 C0.88,4.29 -1,2.41 -1,2.41 C-1,2.41 -1,6.17 -1,6.17c M0.88 -4.29 C0.88,-4.29 -1,-6.17 -1,-6.17 C-1,-6.17 -1,-2.41 -1,-2.41 C-1,-2.41 0.88,-4.29 0.88,-4.29c M-2 -10 C-2,-10 3.71,-4.29 3.71,-4.29 C3.71,-4.29 -0.59,0 -0.59,0 C-0.59,0 3.71,4.29 3.71,4.29 C3.71,4.29 -2,10 -2,10 C-2,10 -3,10 -3,10 C-3,10 -3,2.41 -3,2.41 C-3,2.41 -7.59,7 -7.59,7 C-7.59,7 -9.01,5.59 -9.01,5.59 C-9.01,5.59 -3.41,0 -3.41,0 C-3.41,0 -9.01,-5.59 -9.01,-5.59 C-9.01,-5.59 -7.59,-7 -7.59,-7 C-7.59,-7 -3,-2.41 -3,-2.41 C-3,-2.41 -3,-10 -3,-10 C-3,-10 -2,-10 -2,-10c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="0"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M4.56 2.33 C4.56,2.33 2.24,0.01 2.24,0.01 C2.24,0.01 4.57,-2.31 4.57,-2.31 C4.84,-1.59 5,-0.82 5,0 C5,0.82 4.84,1.61 4.56,2.33c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="0"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M6.27 -4.03 C6.27,-4.03 7.53,-5.29 7.53,-5.29 C8.46,-3.77 9,-1.99 9.01,-0.1 C9.01,1.85 8.44,3.67 7.47,5.21 C7.47,5.21 6.27,4.01 6.27,4.01 C6.89,2.81 7.25,1.44 7.25,-0.01 C7.25,-1.46 6.9,-2.82 6.27,-4.03c " />
+ <path
+ android:name="_R_G_L_0_G_D_3_P_0"
+ android:fillAlpha="0"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-8.6 -2.33 C-8.6,-2.33 -6.28,-0.01 -6.28,-0.01 C-6.28,-0.01 -8.61,2.31 -8.61,2.31 C-8.88,1.59 -9.04,0.82 -9.04,0 C-9.04,-0.82 -8.88,-1.61 -8.6,-2.33c " />
+ <path
+ android:name="_R_G_L_0_G_D_4_P_0"
+ android:fillAlpha="0"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-10.31 4.03 C-10.31,4.03 -11.57,5.29 -11.57,5.29 C-12.5,3.77 -13.04,1.99 -13.05,0.1 C-13.05,-1.85 -12.48,-3.67 -11.51,-5.21 C-11.51,-5.21 -10.31,-4.01 -10.31,-4.01 C-10.93,-2.81 -11.29,-1.44 -11.29,0.01 C-11.29,1.46 -10.94,2.82 -10.31,4.03c " />
+ <path
+ android:name="_R_G_L_0_G_D_5_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M-9.09 0 C-9.09,0 -9.09,0 -9.09,0 C-9.09,0 -9.09,0 -9.09,0 C-9.09,0 -9.09,0 -9.09,0 C-9.09,0 -9.09,0 -9.09,0c " />
+ <path
+ android:name="_R_G_L_0_G_D_6_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M4.92 0 C4.92,0 4.92,0 4.92,0 C4.92,0 4.92,0 4.92,0 C4.92,0 4.92,0 4.92,0 C4.92,0 4.92,0 4.92,0c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
index 36035fc87e40..01e3de2315af 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
@@ -45,6 +45,7 @@
style="@style/Bouncer.UserSwitcher.Spinner.Header"
android:clickable="false"
android:id="@+id/user_switcher_header"
+ android:textDirection="locale"
android:layout_width="@dimen/bouncer_user_switcher_width"
android:layout_height="wrap_content" />
</LinearLayout>>
diff --git a/packages/SystemUI/res-keyguard/values-af/strings.xml b/packages/SystemUI/res-keyguard/values-af/strings.xml
index 5184c1cb02f5..169ccaaab250 100644
--- a/packages/SystemUI/res-keyguard/values-af/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-af/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Jy het jou wagwoord <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerd ingetik. \n\nProbeer weer oor <xliff:g id="NUMBER_1">%2$d</xliff:g> sekondes."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer verkeerd geteken. \n\nProbeer weer oor <xliff:g id="NUMBER_1">%2$d</xliff:g> sekondes."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Verkeerde SIM-PIN-kode. Jy sal nou jou diensverskaffer moet kontak om jou toestel te ontsluit."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Verkeerde SIM-PIN-kode. Jy het <xliff:g id="NUMBER_1">%d</xliff:g> pogings oor.</item>
- <item quantity="one">Verkeerde SIM-PIN-kode. Jy het <xliff:g id="NUMBER_0">%d</xliff:g> oorblywende poging voordat jy jou diensverskaffer sal moet kontak om jou toestel te ontsluit.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Verkeerde PIN-kode vir SIM. Jy het # poging oor voordat jy jou diensverskaffer moet kontak om jou toestel te ontsluit.}other{Verkeerde PIN-kode vir SIM. Jy het # pogings oor. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM is onbruikbaar. Kontak jou diensverskaffer."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Verkeerde SIM-PUK-kode. Jy het <xliff:g id="NUMBER_1">%d</xliff:g> pogings oor voordat SIM permanent onbruikbaar word.</item>
- <item quantity="one">Verkeerde SIM-PUK-kode. Jy het <xliff:g id="NUMBER_0">%d</xliff:g> poging oor voordat SIM permanent onbruikbaar word.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Verkeerde PUK-kode vir SIM. Jy het # poging oor voordat SIM permanent onbruikbaar word.}other{Verkeerde PUK-kode vir SIM. Jy het # pogings oor voordat SIM permanent onbruikbaar word.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM-PIN-bewerking het misluk!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM-PUK-bewerking het misluk!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Wissel invoermetode"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Toestel is handmatig gesluit"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nie herken nie"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Skakel kameratoegang aan om Gesigslot te gebruik"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Voer SIM-PIN in. Jy het <xliff:g id="NUMBER_1">%d</xliff:g> pogings oor.</item>
- <item quantity="one">Voer SIM-PIN in. Jy het <xliff:g id="NUMBER_0">%d</xliff:g> poging oor voordat jy jou diensverskaffer moet kontak om jou toestel te ontsluit.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM is nou gedeaktiveer. Voer PUK-kode in om voort te gaan. Jy het <xliff:g id="_NUMBER_1">%d</xliff:g> pogings oor voordat die SIM permanent onbruikbaar word. Kontak die diensverskaffer vir besonderhede.</item>
- <item quantity="one">SIM is nou gedeaktiveer. Voer PUK-kode in om voort te gaan. Jy het <xliff:g id="_NUMBER_0">%d</xliff:g> poging oor voordat die SIM permanent onbruikbaar word. Kontak die diensverskaffer vir besonderhede.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Voer SIM se PIN in. Jy het # poging oor voordat jy jou diensverskaffer moet kontak om jou toestel te ontsluit.}other{Voer SIM se PIN in. Jy het # pogings oor.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM is nou gedeaktiveer. Voer PUK-kode in om voort te gaan. Jy het # poging oor voordat die SIM permanent onbruikbaar word. Kontak die diensverskaffer vir besonderhede.}other{SIM is nou gedeaktiveer. Voer PUK-kode in om voort te gaan. Jy het # pogings oor voordat die SIM permanent onbruikbaar word. Kontak die diensverskaffer vir besonderhede.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Verstek"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Borrel"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analoog"</string>
diff --git a/packages/SystemUI/res-keyguard/values-am/strings.xml b/packages/SystemUI/res-keyguard/values-am/strings.xml
index 11a2be59d9b8..b528269a3100 100644
--- a/packages/SystemUI/res-keyguard/values-am/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-am/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"የይለፍ ቃልዎን <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ተይበዋል።\n\nበ<xliff:g id="NUMBER_1">%2$d</xliff:g> ሰኮንዶች ውስጥ እንደገና ይሞክሩ።"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"የመክፈቻ ስርዓተ ጥለቱን <xliff:g id="NUMBER_0">%1$d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ስለውታል።\n\nበ<xliff:g id="NUMBER_1">%2$d</xliff:g> ሰኮንዶች ውስጥ እንደገና ይሞክሩ።"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"ልክ ያልሆነ የሲም ፒን ኮድ። አሁን መሣሪያዎን ለማስከፈት አገልግሎት አቅራቢዎን ማነጋገር አለብዎት።"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">ልክ ያልሆነ የሲም ፒን ኮድ፣ <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
- <item quantity="other">ልክ ያልሆነ የሲም ፒን ኮድ፣ <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{የተሳሳተ የሲም ፒን ኮድ፣ መሣሪያዎን ለማስከፈት የአገልግሎት አቅራቢዎን ማነጋገር ግዴታ ከመሆኑ በፊት # ሙከራ ይቀርዎታል።}one{የተሳሳተ የሲም ፒን ኮድ፣ # ሙከራዎች ይቀሩዎታል። }other{የተሳሳተ የሲም ፒን ኮድ፣ # ሙከራዎች ይቀሩዎታል። }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"ሲሙ ጥቅም ላይ መዋል እይችልም። የአገልግሎት አቅራቢዎን ያነጋግሩ።"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">ልክ ያልሆነ የሲም ፒዩኬ ኮድ፣ ሲሙ እስከመጨረሻው የማይሰራ ከመሆኑ በፊት <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
- <item quantity="other">ልክ ያልሆነ የሲም ፒዩኬ ኮድ፣ ሲሙ እስከመጨረሻው የማይሰራ ከመሆኑ በፊት <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{የተሳሳተ የሲም PUK ኮድ፣ ሲም በቋሚነት መጠቀም የማይቻል ከመሆኑ በፊት # ሙከራ ይቀርዎታል።}one{የተሳሳተ የሲም PUK ኮድ፣ ሲም በቋሚነት መጠቀም የማይቻል ከመሆኑ በፊት # ሙከራ ይቀርዎታል።}other{የተሳሳተ የሲም PUK ኮድ፣ ሲም በቋሚነት መጠቀም የማይቻል ከመሆኑ በፊት # ሙከራዎች ይቀሩዎታል።}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"የሲም ፒን ክወና አልተሳካም!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"የሲም PUK ክወና አልተሳካም!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"የግቤት ስልት ቀይር"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"መሣሪያ በተጠቃሚው ራሱ ተቆልፏል"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"አልታወቀም"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"በመልክ መክፈትን ለመጠቀም በቅንብሮች ውስጥ የካሜራ መዳረሻን ያብሩ"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">የሲም ፒን ያስገቡ። <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
- <item quantity="other">የሲም ፒን ያስገቡ። <xliff:g id="NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል።</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">ሲም አሁን ተሰናክሏል። ለመቀጠል የPUK ኮድ ያስገቡ። ሲም እስከመጨረሻው መጠቀም የማይቻል ከመሆኑ በፊት <xliff:g id="_NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል። ዝርዝሮችን ለማግኘት የአገልግሎት አቅራቢን ያነጋግሩ።</item>
- <item quantity="other">ሲም አሁን ተሰናክሏል። ለመቀጠል የPUK ኮድ ያስገቡ። ሲም እስከመጨረሻው መጠቀም የማይቻል ከመሆኑ በፊት <xliff:g id="_NUMBER_1">%d</xliff:g> ሙከራዎች ይቀረዎታል። ዝርዝሮችን ለማግኘት የአገልግሎት አቅራቢን ያነጋግሩ።</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{የሲም ፒን ያስገቡ። መሣሪያዎን ለማስከፈት የአገልግሎት አቅራቢዎን ማነጋገር ግዴታ ከመሆኑ በፊት # ሙከራ ይቀርዎታል።}one{የሲም ፒን ያስገቡ። # ቀሪ ሙከራዎች አሉዎት።}other{የሲም ፒን ያስገቡ። # ቀሪ ሙከራዎች አሉዎት።}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{ሲም አሁን ተሰናክሏል። ለመቀጠል የPUK ኮድ ያስገቡ። ሲም በቋሚነት መጠቀም የማይቻል ከመሆኑ በፊት # ሙከራ ይቀርዎታል። ዝርዝሮችን ለማግኘት የአገልግሎት አቅራቢን ያነጋግሩ።}one{ሲም አሁን ተሰናክሏል። ለመቀጠል የPUK ኮድ ያስገቡ። ሲም በቋሚነት መጠቀም የማይቻል ከመሆኑ በፊት # ሙከራዎች ይቀሩዎታል። ዝርዝሮችን ለማግኘት የአገልግሎት አቅራቢን ያነጋግሩ።}other{ሲም አሁን ተሰናክሏል። ለመቀጠል የPUK ኮድ ያስገቡ። ሲም በቋሚነት መጠቀም የማይቻል ከመሆኑ በፊት # ሙከራዎች ይቀሩዎታል። ዝርዝሮችን ለማግኘት የአገልግሎት አቅራቢን ያነጋግሩ።}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"ነባሪ"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"አረፋ"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"አናሎግ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 5e3bc324568e..534dbaa60c5d 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -68,23 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"لقد كتبت كلمة المرور بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. \n\nأعد المحاولة خلال <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانية."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"لقد رسمت نقش فتح القفل بطريقة غير صحيحة <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. \n\nأعد المحاولة خلال <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانية."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"‏رمز \"رقم التعريف الشخصي\" لشريحة SIM غير صحيح، ويلزمك الاتصال الآن بمشغّل شبكة الجوّال لإلغاء قفل الجهاز."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="zero">‏رمز رقم التعريف الشخصي لشريحة SIM غير صحيح، ولم تتبق لديك أي محاولات (<xliff:g id="NUMBER_1">%d</xliff:g>).</item>
- <item quantity="two">‏رمز رقم التعريف الشخصي لشريحة SIM غير صحيح، ويتبقى لديك محاولتان (<xliff:g id="NUMBER_1">%d</xliff:g>).</item>
- <item quantity="few">‏رمز رقم التعريف الشخصي لشريحة SIM غير صحيح، ويتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولات.</item>
- <item quantity="many">‏رمز رقم التعريف الشخصي لشريحة SIM غير صحيح، ويتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة.</item>
- <item quantity="other">‏رمز رقم التعريف الشخصي لشريحة SIM غير صحيح، ويتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة.</item>
- <item quantity="one">‏رمز \"رقم التعريف الشخصي\" لشريحة SIM غير صحيح، ويتبقى لديك محاولة واحدة (<xliff:g id="NUMBER_0">%d</xliff:g>) يتعين عليك بعدها الاتصال بمشغّل شبكة الجوّال لإلغاء قفل الجهاز.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{‏رقم التعريف الشخصي لشريحة SIM غير صحيح. تتبقّى لديك محاولة واحدة يجب بعدها الاتصال بمشغّل شبكة الجوّال لفتح قفل الجهاز.}zero{‏رمز رقم التعريف الشخصي لشريحة SIM غير صحيح. تتبقّى لديك # محاولة. }two{‏رمز رقم التعريف الشخصي لشريحة SIM غير صحيح. تتبقّى لديك محاولتان. }few{‏رمز رقم التعريف الشخصي لشريحة SIM غير صحيح. تتبقّى لديك # محاولات. }many{‏رمز رقم التعريف الشخصي لشريحة SIM غير صحيح. تتبقّى لديك # محاولة. }other{‏رمز رقم التعريف الشخصي لشريحة SIM غير صحيح. تتبقّى لديك # محاولة. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"‏شريحة SIM غير صالحة للاستخدام. يُرجى الاتصال بمشغّل شبكة الجوّال."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="zero">‏رمز PUK لشريحة SIM غير صحيح، ولم تتبق لديك أي محاولات (<xliff:g id="NUMBER_1">%d</xliff:g>) تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
- <item quantity="two">‏رمز PUK لشريحة SIM غير صحيح، ويتبقى لديك محاولتان (<xliff:g id="NUMBER_1">%d</xliff:g>) تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
- <item quantity="few">‏رمز PUK لشريحة SIM غير صحيح، ويتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولات تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
- <item quantity="many">‏رمز PUK لشريحة SIM غير صحيح، ويتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
- <item quantity="other">‏رمز PUK لشريحة SIM غير صحيح، ويتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
- <item quantity="one">‏رمز PUK لشريحة SIM غير صالح، ويتبقى لديك محاولة واحدة (<xliff:g id="NUMBER_0">%d</xliff:g>)، تصبح بعدها شريحة SIM غير صالحة للاستخدام بشكل دائم.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{‏رمز PUK لشريحة SIM غير صحيح. تتبقّى لديك محاولة واحدة قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا.}zero{‏رمز PUK لشريحة SIM غير صحيح. تتبقّى لديك # محاولة قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا.}two{‏رمز PUK لشريحة SIM غير صحيح. تتبقّى لديك محاولتان قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا.}few{‏رمز PUK لشريحة SIM غير صحيح. تتبقّى لديك # محاولات قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا.}many{‏رمز PUK لشريحة SIM غير صحيح. تتبقّى لديك # محاولة قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا.}other{‏رمز PUK لشريحة SIM غير صحيح. تتبقّى لديك # محاولة قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"‏تعذّر إتمام عملية \"رقم التعريف الشخصي\" لشريحة SIM"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"‏تعذّر إتمام عملية PUK لشريحة SIM"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"تبديل أسلوب الإدخال"</string>
@@ -99,22 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"تم حظر الجهاز يدويًا"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"لم يتم التعرّف عليه."</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"يجب منح الكاميرا إذن الوصول في \"الإعدادات\"."</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="zero">‏أدخل رقم التعريف الشخصي لشريحة SIM. تتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة.</item>
- <item quantity="two">‏أدخل رقم التعريف الشخصي لشريحة SIM. تتبقى لديك محاولتان (<xliff:g id="NUMBER_1">%d</xliff:g>).</item>
- <item quantity="few">‏أدخل رقم التعريف الشخصي لشريحة SIM. تتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولات.</item>
- <item quantity="many">‏أدخل رقم التعريف الشخصي لشريحة SIM. تتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة.</item>
- <item quantity="other">‏أدخل رقم التعريف الشخصي لشريحة SIM. تتبقى لديك <xliff:g id="NUMBER_1">%d</xliff:g> محاولة.</item>
- <item quantity="one">‏أدخل رقم التعريف الشخصي لشريحة SIM. تتبقى لديك محاولة واحدة (<xliff:g id="NUMBER_0">%d</xliff:g>) يجب أن تتصل بعدها بمشغّل شبكة الجوّال لفتح الجهاز.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="zero">‏تم إيقاف شريحة SIM الآن. أدخل رمز PUK للمتابعة، وتتبقى لديك <xliff:g id="_NUMBER_1">%d</xliff:g> محاولة قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. ويمكنك الاتصال بمشغل شبكة الجوّال لمعرفة التفاصيل.</item>
- <item quantity="two">‏تم إيقاف شريحة SIM الآن. أدخل رمز PUK للمتابعة، وتتبقى لديك محاولتان (<xliff:g id="_NUMBER_1">%d</xliff:g>) قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. ويمكنك الاتصال بمشغل شبكة الجوّال لمعرفة التفاصيل.</item>
- <item quantity="few">‏تم إيقاف شريحة SIM الآن. أدخل رمز PUK للمتابعة، وتتبقى لديك <xliff:g id="_NUMBER_1">%d</xliff:g> محاولات قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. ويمكنك الاتصال بمشغل شبكة الجوّال لمعرفة التفاصيل.</item>
- <item quantity="many">‏تم إيقاف شريحة SIM الآن. أدخل رمز PUK للمتابعة، وتتبقى لديك <xliff:g id="_NUMBER_1">%d</xliff:g> محاولة قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. ويمكنك الاتصال بمشغل شبكة الجوّال لمعرفة التفاصيل.</item>
- <item quantity="other">‏تم إيقاف شريحة SIM الآن. أدخل رمز PUK للمتابعة، وتتبقى لديك <xliff:g id="_NUMBER_1">%d</xliff:g> محاولة قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. ويمكنك الاتصال بمشغل شبكة الجوّال لمعرفة التفاصيل.</item>
- <item quantity="one">‏تم إيقاف شريحة SIM الآن. أدخل رمز PUK للمتابعة، وتتبقى لديك محاولة واحدة (<xliff:g id="_NUMBER_0">%d</xliff:g>) قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. ويمكنك الاتصال بمشغل شبكة الجوّال لمعرفة التفاصيل.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{‏أدخِل رقم التعريف الشخصي لشريحة SIM. تتبقّى لديك محاولة واحدة ويجب بعدها التواصل مع مشغّل شبكة الجوّال لفتح قفل الجهاز.}zero{‏أدخِل رقم التعريف الشخصي لشريحة SIM. تتبقّى لديك # محاولة.}two{‏أدخِل رقم التعريف الشخصي لشريحة SIM. تتبقّى لديك محاولتان.}few{‏أدخِل رقم التعريف الشخصي لشريحة SIM. تتبقّى لديك # محاولات.}many{‏أدخِل رقم التعريف الشخصي لشريحة SIM. تتبقّى لديك # محاولةً.}other{‏أدخِل رقم التعريف الشخصي لشريحة SIM. تتبقّى لديك # محاولة.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{‏تم إيقاف شريحة SIM الآن. يجب إدخال رمز PUK للمتابعة. وتتبقّى لديك محاولة واحدة قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. يمكنك التواصل مع مشغِّل شبكة الجوّال لمعرفة التفاصيل.}zero{‏تم إيقاف شريحة SIM الآن. يجب إدخال رمز PUK للمتابعة. وتتبقّى لديك # محاولة قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. يمكنك التواصل مع مشغِّل شبكة الجوّال لمعرفة التفاصيل.}two{‏تم إيقاف شريحة SIM الآن. يجب إدخال رمز PUK للمتابعة. وتتبقّى لديك محاولتان قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. يمكنك التواصل مع مشغِّل شبكة الجوّال لمعرفة التفاصيل.}few{‏تم إيقاف شريحة SIM الآن. يجب إدخال رمز PUK للمتابعة. وتتبقّى لديك # محاولات قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. يمكنك التواصل مع مشغِّل شبكة الجوّال لمعرفة التفاصيل.}many{‏تم إيقاف شريحة SIM الآن. يجب إدخال رمز PUK للمتابعة. وتتبقّى لديك # محاولةً قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. يمكنك التواصل مع مشغِّل شبكة الجوّال لمعرفة التفاصيل.}other{‏تم إيقاف شريحة SIM الآن. يجب إدخال رمز PUK للمتابعة. وتتبقّى لديك # محاولة قبل أن تصبح شريحة SIM غير صالحة للاستخدام نهائيًا. يمكنك التواصل مع مشغِّل شبكة الجوّال لمعرفة التفاصيل.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"تلقائي"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"فقاعة"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"ساعة تقليدية"</string>
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index d60c564adcb4..537b5b8fe4a5 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"আপুনি আপোনাৰ পাছৱৰ্ড <xliff:g id="NUMBER_0">%1$d</xliff:g>বাৰ ভুলকৈ লিখিছে। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ছেকেণ্ডৰ পাছত আকৌ চেষ্টা কৰক।"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"আপুনি আপোনাৰ আনলক আৰ্হি <xliff:g id="NUMBER_0">%1$d</xliff:g> বাৰ ভুলকৈ আঁকিছে। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>ছেকেণ্ডৰ পিছত আকৌ চেষ্টা কৰক।"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"ছিমৰ ভুল পিন ক\'ড, আপোনাৰ ডিভাইচটো আনলক কৰিবলৈ আপুনি এতিয়া আপোনাৰ বাহকৰ সৈতে যোগাযোগ কৰিবই লাগিব।"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">ছিমৰ ভুল পিন ক’ড, আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g> বাৰ প্ৰয়াস কৰিব পাৰিব।</item>
- <item quantity="other">ছিমৰ ভুল পিন ক’ড, আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g> বাৰ প্ৰয়াস কৰিব পাৰিব।</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{ছিমৰ পিন ক’ড ভুল হৈছে, আপোনাৰ ডিভাইচ আনলক কৰিবলৈ আপোনাৰ বাহকৰ লগত যোগাযোগ কৰিবই লগা হোৱাৰ পূৰ্বে আপোনাৰ ওচৰত # টা প্ৰয়াস বাকী আছে।}one{ছিমৰ পিন ক’ড ভুল হৈছে, আপোনাৰ ওচৰত # টা প্ৰয়াস বাকী আছে। }other{ছিমৰ পিন ক’ড ভুল হৈছে, আপোনাৰ ওচৰত # টা প্ৰয়াস বাকী আছে। }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"ছিম ব্যৱহাৰযোগ্য নহয়। আপোনাৰ বাহকৰ সৈতে যোগাযোগ কৰক।"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">ছিমৰ ভুল PUK ক\'ড, আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g> বাৰ ভুল ক’ড দিলে আপোনাৰ ছিম চিৰকালৰ বাবে ব্যৱহাৰৰ অনুপযোগী হ’ব।</item>
- <item quantity="other">ছিমৰ ভুল PUK ক\'ড, আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g> বাৰ ভুল ক’ড দিলে আপোনাৰ ছিম চিৰকালৰ বাবে ব্যৱহাৰৰ অনুপযোগী হ’ব।</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{ছিমৰ PUK ক’ড ভুল হৈছে, ছিমখন স্থায়ীভাৱে ব্যৱহাৰৰ অনুপযোগী হোৱাৰ পূৰ্বে আপোনাৰ ওচৰত # টা প্ৰয়াস বাকী আছে।}one{ছিমৰ PUK ক’ড ভুল হৈছে, ছিমখন স্থায়ীভাৱে ব্যৱহাৰৰ অনুপযোগী হোৱাৰ পূৰ্বে আপোনাৰ ওচৰত # টা প্ৰয়াস বাকী আছে।}other{ছিমৰ PUK ক’ড ভুল হৈছে, ছিমখন স্থায়ীভাৱে ব্যৱহাৰৰ অনুপযোগী হোৱাৰ পূৰ্বে আপোনাৰ ওচৰত # টা প্ৰয়াস বাকী আছে।}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"ছিম পিনৰ জৰিয়তে আনলক কৰিব পৰা নগ\'ল!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"ছিম PUKৰ জৰিয়তে আনলক কৰিব পৰা নগ\'ল!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ইনপুট পদ্ধতি সলনি কৰক"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ডিভাইচটো মেনুৱেলভাৱে লক কৰা হৈছিল"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"চিনাক্ত কৰিব পৰা নাই"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"ফেচ আনলক ব্যৱহাৰ কৰিবলৈ ছেটিঙত কেমেৰাৰ এক্সেছ অন কৰক"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">ছিমৰ পিন দিয়ক। আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g>বাৰ প্ৰয়াস কৰিব পাৰে।</item>
- <item quantity="other">ছিমৰ পিন দিয়ক। আপুনি আৰু <xliff:g id="NUMBER_1">%d</xliff:g>বাৰ প্ৰয়াস কৰিব পাৰে।</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">ছিমখন অক্ষম হ’ল। অব্যাহত ৰাখিবলৈ PUK দিয়ক। ছিমখন স্থায়ীভাৱে ব্যৱহাৰৰ অনুপযোগী হোৱাৰ পূৰ্বে আপোনাৰ হাতত <xliff:g id="_NUMBER_1">%d</xliff:g>টা প্ৰয়াস বাকী আছে। সবিশেষ জানিবলৈ বাহকৰ সৈতে যোগাযোগ কৰক।</item>
- <item quantity="other">ছিমখন অক্ষম হ’ল। অব্যাহত ৰাখিবলৈ PUK দিয়ক। ছিমখন স্থায়ীভাৱে ব্যৱহাৰৰ অনুপযোগী হোৱাৰ পূৰ্বে আপোনাৰ হাতত <xliff:g id="_NUMBER_1">%d</xliff:g>টা প্ৰয়াস বাকী আছে। সবিশেষ জানিবলৈ বাহকৰ সৈতে যোগাযোগ কৰক।</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{ছিমৰ পিন দিয়ক। আপোনাৰ ডিভাইচ আনলক কৰিবলৈ আপোনাৰ বাহকৰ লগত যোগাযোগ কৰিবই লগা হোৱাৰ পূৰ্বে আপোনাৰ ওচৰত # টা প্ৰয়াস বাকী আছে।}one{ছিমৰ পিন দিয়ক। আপোনাৰ ওচৰত # টা প্ৰয়াস বাকী আছে।}other{ছিমৰ পিন দিয়ক। আপোনাৰ ওচৰত # টা প্ৰয়াস বাকী আছে।}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{ছিমখন এতিয়া অক্ষম কৰা হৈছে। অব্যাহত ৰাখিবলৈ PUK ক’ড দিয়ক। ছিমখন স্থায়ীভাৱে ব্যৱহাৰৰ অনুপযোগী হোৱাৰ পূৰ্বে আপোনাৰ ওচৰত # টা প্ৰয়াস বাকী আছে। সবিশেষৰ বাবে বাহকৰ সৈতে যোগাযোগ কৰক।}one{ছিমখন এতিয়া অক্ষম কৰা হৈছে। অব্যাহত ৰাখিবলৈ PUK ক’ড দিয়ক। ছিমখন স্থায়ীভাৱে ব্যৱহাৰৰ অনুপযোগী হোৱাৰ পূৰ্বে আপোনাৰ ওচৰত # টা প্ৰয়াস বাকী আছে। সবিশেষৰ বাবে বাহকৰ সৈতে যোগাযোগ কৰক।}other{ছিমখন এতিয়া অক্ষম কৰা হৈছে। অব্যাহত ৰাখিবলৈ PUK ক’ড দিয়ক। ছিমখন স্থায়ীভাৱে ব্যৱহাৰৰ অনুপযোগী হোৱাৰ পূৰ্বে আপোনাৰ ওচৰত # টা প্ৰয়াস বাকী আছে। সবিশেষৰ বাবে বাহকৰ সৈতে যোগাযোগ কৰক।}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"ডিফ’ল্ট"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"বাবল"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"এনাল’গ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml
index fe35bbc34bfa..3fd2f0405027 100644
--- a/packages/SystemUI/res-keyguard/values-az/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-az/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Parolu <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış daxil etdiniz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> saniyə sonra yenidən cəhd edin."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Kilid modelini <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış çəkdiniz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> saniyə sonra yenidən cəhd edin."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Yanlış SIM PIN kodu  cihazın açılması üçün operatorla indi əlaqə saxlamalısınız."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Yanlış SIM PIN kodu, <xliff:g id="NUMBER_1">%d</xliff:g> cəhdiniz qalır.</item>
- <item quantity="one">Yanlış SIM PIN kodu, <xliff:g id="NUMBER_0">%d</xliff:g> cəhddən sonra cihazı kiliddən çıxarmaq üçün operatorla əlaqə saxlamalısınız.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Yanlış SIM PIN kodu, # cəhdiniz qalıb, sonra cihazı kiliddən çıxarmaq üçün operatorla əlaqə saxlamalı olacaqsınız.}other{Yanlış SIM PIN kodu, # cəhdiniz qalıb. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM yararsızdır. Operatorla əlaqə saxlayın."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Yanlış SIM PUK kodu,<xliff:g id="NUMBER_1">%d</xliff:g> cəhddən sonra SIM kart həmişəlik yararsız olacaq.</item>
- <item quantity="one">Yanlış SIM PUK kodu, <xliff:g id="NUMBER_0">%d</xliff:g> cəhddən sonra SIM kart həmişəlik yararsız olacaq.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Yanlış SIM PUK kodu, # cəhdiniz qalıb, sonra SIM kart həmişəlik yararsız olacaq.}other{Yanlış SIM PUK kodu, # cəhdiniz qalıb, sonra SIM kart həmişəlik yararsız olacaq.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM PIN əməliyyatı alınmadı!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUK əməliyyatı alınmadı!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Daxiletmə metoduna keçin"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Cihaz əl ilə kilidləndi"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Tanınmır"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Üz ilə Kiliddən Açmanı istifadə etmək üçün Ayarlarda kameraya girişi aktiv edin"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">SIM PIN-ni daxil edin. <xliff:g id="NUMBER_1">%d</xliff:g> cəhdiniz qalır.</item>
- <item quantity="one">SIM PIN-ni daxil edin. Cihazınızı kiliddən çıxarmaq üçün operatorunuzla əlaqə saxlamadan öncə <xliff:g id="NUMBER_0">%d</xliff:g> cəhdiniz qalır.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM indi deaktivdir. Davam etmək üçün PUK kodunu daxil edin. SIM birdəfəlik yararsız olmadan öncə <xliff:g id="_NUMBER_1">%d</xliff:g> cəhdiniz qalır. Ətraflı məlumat üçün operatorla əlaqə saxlayın.</item>
- <item quantity="one">SIM indi deaktivdir. Davam etmək üçün PUK kodunu daxil edin. SIM birdəfəlik yararsız olmadan öncə <xliff:g id="_NUMBER_0">%d</xliff:g> cəhdiniz qalır. Ətraflı məlumat üçün operatorla əlaqə saxlayın.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{SIM PIN kodunu daxil edin. # cəhdiniz qalıb, sonra cihazınızı kiliddən çıxarmaq üçün operatorunuzla əlaqə saxlamalı olacaqsınız.}other{SIM PIN kodunu daxil edin. # cəhdiniz qalıb.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM indi deaktivdir. Davam etmək üçün PUK kodunu daxil edin. # cəhdiniz qalıb, sonra SIM birdəfəlik yararsız olacaq. Ətraflı məlumat üçün operatorla əlaqə saxlayın.}other{SIM indi deaktivdir. Davam etmək üçün PUK kodunu daxil edin. # cəhdiniz qalıb, sonra SIM birdəfəlik yararsız olacaq. Ətraflı məlumat üçün operatorla əlaqə saxlayın.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Defolt"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Qabarcıq"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analoq"</string>
diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
index 5e83a153ff76..b0059d8a8acd 100644
--- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
@@ -68,17 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Uneli ste pogrešnu lozinku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nProbajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Nacrtali ste netačan šablon za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nProbajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Netačan PIN kôd za SIM. Sada morate da kontaktirate mobilnog operatera da biste otključali uređaj."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Netačan PIN kôd za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
- <item quantity="few">Netačan PIN kôd za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
- <item quantity="other">Netačan PIN kôd za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Netačan PIN za SIM kôd. Imate još # pokušaj, a onda morate da se obratite mobilnom operateru da biste otključali uređaj.}one{Netačan PIN za SIM kôd. Imate još # pokušaj. }few{Netačan PIN za SIM kôd. Imate još # pokušaja. }other{Netačan PIN za SIM kôd. Imate još # pokušaja. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM kartica je neupotrebljiva. Kontaktirajte mobilnog operatera."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Netačan PUK kôd za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj pre nego što SIM kartica postane trajno neupotrebljiva.</item>
- <item quantity="few">Netačan PUK kôd za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja pre nego što SIM kartica postane trajno neupotrebljiva.</item>
- <item quantity="other">Netačan PUK kôd za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja pre nego što SIM kartica postane trajno neupotrebljiva.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Netačan SIM PUK kôd. Imate još # pokušaj pre nego što SIM kartica postane trajno neupotrebljiva.}one{Netačan PUK kôd za SIM. Imate još # pokušaj pre nego što SIM kartica postane trajno neupotrebljiva.}few{Netačan PUK kôd za SIM. Imate još # pokušaja pre nego što SIM kartica postane trajno neupotrebljiva.}other{Netačan PUK kôd za SIM. Imate još # pokušaja pre nego što SIM kartica postane trajno neupotrebljiva.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Radnja sa PIN kodom za SIM nije uspela!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Radnja sa PUK kodom za SIM nije uspela!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Promeni metod unosa"</string>
@@ -93,16 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Uređaj je ručno zaključan"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nije prepoznat"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Otključavanje licem traži pristup kameri u Podešavanjima"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
- <item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
- <item quantity="other">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaj pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.</item>
- <item quantity="few">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.</item>
- <item quantity="other">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Unesite PIN za SIM. Još # pokušaj i moraćete da se obratite mobilnom operateru da biste otključali uređaj.}one{Unesite PIN za SIM. Imate još # pokušaj.}few{Unesite PIN za SIM. Imate još # pokušaja.}other{Unesite PIN za SIM. Imate još # pokušaja.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još # pokušaj pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.}one{SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još # pokušaj pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.}few{SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još # pokušaja pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.}other{SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još # pokušaja pre nego što SIM postane trajno neupotrebljiv. Detaljne informacije potražite od mobilnog operatera.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Podrazumevani"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Mehurići"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogni"</string>
diff --git a/packages/SystemUI/res-keyguard/values-be/strings.xml b/packages/SystemUI/res-keyguard/values-be/strings.xml
index 0e6681ce8b3a..ebcca072ab90 100644
--- a/packages/SystemUI/res-keyguard/values-be/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-be/strings.xml
@@ -68,19 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Вы няправільна ўвялі пароль столькі разоў: <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\nПаўтарыце спробу праз <xliff:g id="NUMBER_1">%2$d</xliff:g> с."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Вы няправільна ўвялі ўзор разблакіроўкі столькі разоў: <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\nПаўтарыце спробу праз <xliff:g id="NUMBER_1">%2$d</xliff:g> с."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Няправільны PIN-код SIM-карты, цяпер вы павінны звязацца з аператарам для разблакіроўкі прылады."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Няправільны PIN-код SIM-карты, у вас засталася <xliff:g id="NUMBER_1">%d</xliff:g> спроба.</item>
- <item quantity="few">Няправільны PIN-код SIM-карты, у вас засталіся <xliff:g id="NUMBER_1">%d</xliff:g> спробы.</item>
- <item quantity="many">Няправільны PIN-код SIM-карты, у вас засталіся <xliff:g id="NUMBER_1">%d</xliff:g> спроб.</item>
- <item quantity="other">Няправільны PIN-код SIM-карты, у вас засталіся <xliff:g id="NUMBER_1">%d</xliff:g> спробы.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Няправільны PIN-код SIM-карты. У вас засталася # спроба, інакш вам прыйдзецца звязацца з аператарам для разблакіроўкі прылады.}one{Няправільны PIN-код SIM-карты, у вас засталіся # спроба. }few{Няправільны PIN-код SIM-карты, у вас засталіся # спробы. }many{Няправільны PIN-код SIM-карты, у вас засталіся # спроб. }other{Няправільны PIN-код SIM-карты, у вас засталіся # спробы. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM-карта не прыдатная для выкарыстання. Звяжыцеся з аператарам."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Няправільны PUK-код SIM-карты, у вас засталася <xliff:g id="NUMBER_1">%d</xliff:g> спроба перад тым, як SIM-карта перастане працаваць назаўжды.</item>
- <item quantity="few">Няправільны PUK-код SIM-карты, у вас засталіся <xliff:g id="NUMBER_1">%d</xliff:g> спробы перад тым, як SIM-карта перастане працаваць назаўжды.</item>
- <item quantity="many">Няправільны PUK-код SIM-карты, у вас засталіся <xliff:g id="NUMBER_1">%d</xliff:g> спроб перад тым, як SIM-карта перастане працаваць назаўжды.</item>
- <item quantity="other">Няправільны PUK-код SIM-карты, у вас засталіся <xliff:g id="NUMBER_1">%d</xliff:g> спробы перад тым, як SIM-карта перастане працаваць назаўжды.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Няправільны PUK-код SIM-карты, у вас засталася # спроба перад тым, як SIM-карта перастане працаваць назаўжды.}one{Няправільны PUK-код SIM-карты, у вас засталося # спроба перад тым, як SIM-карта перастане працаваць назаўжды.}few{Няправільны PUK-код SIM-карты, у вас засталося # спробы перад тым, як SIM-карта перастане працаваць назаўжды.}many{Няправільны PUK-код SIM-карты, у вас засталося # спроб перад тым, як SIM-карта перастане працаваць назаўжды.}other{Няправільны PUK-код SIM-карты, у вас засталося # спробы перад тым, як SIM-карта перастане працаваць назаўжды.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Разблакіраваць SIM-карту PIN-кодам не атрымалася!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Разблакіраваць SIM-карту PUK-кодам не атрымалася!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Пераключэнне рэжыму ўводу"</string>
@@ -95,18 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Прылада была заблакіравана ўручную"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Не распазнана"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Для распазнавання твару ўключыце доступ да камеры"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Увядзіце PIN-код SIM-карты. У вас засталася <xliff:g id="NUMBER_1">%d</xliff:g> спроба.</item>
- <item quantity="few">Увядзіце PIN-код SIM-карты. У вас засталося <xliff:g id="NUMBER_1">%d</xliff:g> спробы.</item>
- <item quantity="many">Увядзіце PIN-код SIM-карты. У вас засталося <xliff:g id="NUMBER_1">%d</xliff:g> спроб.</item>
- <item quantity="other">Увядзіце PIN-код SIM-карты. У вас засталося <xliff:g id="NUMBER_1">%d</xliff:g> спробы.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ <xliff:g id="_NUMBER_1">%d</xliff:g> спроба, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.</item>
- <item quantity="few">SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ <xliff:g id="_NUMBER_1">%d</xliff:g> спробы, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.</item>
- <item quantity="many">SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ <xliff:g id="_NUMBER_1">%d</xliff:g> спроб, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.</item>
- <item quantity="other">SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ <xliff:g id="_NUMBER_1">%d</xliff:g> спробы, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Увядзіце PIN-код SIM-карты. У вас засталася # спроба. Пасля гэтага вам давядзецца звярнуцца да свайго аператара, каб разблакіраваць прыладу.}one{Увядзіце PIN-код SIM-карты. У вас засталося # спроба.}few{Увядзіце PIN-код SIM-карты. У вас засталося # спробы.}many{Увядзіце PIN-код SIM-карты. У вас засталося # спроб.}other{Увядзіце PIN-код SIM-карты. У вас засталося # спробы.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ # спроба, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.}one{SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ # спроба, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.}few{SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ # спробы, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.}many{SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ # спроб, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.}other{SIM-карта заблакіравана. Каб працягнуць, увядзіце PUK-код. У вас ёсць яшчэ # спробы, пасля чаго SIM-карта будзе заблакіравана назаўсёды. Звярніцеся да аператара, каб даведацца больш.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Стандартны"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Бурбалкі"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Са стрэлкамі"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml
index a4e08f3621b1..a08409ec0027 100644
--- a/packages/SystemUI/res-keyguard/values-bg/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Въведохте неправилно паролата си <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. \n\nОпитайте отново след <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%1$d</xliff:g> пъти. \n\nОпитайте отново след <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Неправилен ПИН код за SIM картата – сега трябва да се свържете с оператора си, за да отключите устройството."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Неправилен ПИН код за SIM картата – остават ви <xliff:g id="NUMBER_1">%d</xliff:g> опита.</item>
- <item quantity="one">Неправилен ПИН код за SIM картата – остава ви <xliff:g id="NUMBER_0">%d</xliff:g> опит, преди да трябва да се свържете с оператора си, за да отключите устройството.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Неправилен ПИН код за SIM картата. Остава ви # опит, преди да трябва да се свържете с оператора си, за да отключите устройството.}other{Неправилен ПИН код за SIM картата. Остават ви # опита. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM картата е неизползваема. Свържете се с оператора си."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Неправилен PUK код за SIM картата – остават ви <xliff:g id="NUMBER_1">%d</xliff:g> опита, преди тя да стане неизползваема завинаги.</item>
- <item quantity="one">Неправилен PUK код за SIM картата – остава ви <xliff:g id="NUMBER_0">%d</xliff:g> опит, преди тя да стане неизползваема завинаги.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Неправилен PUK код за SIM картата. Остава ви # опит, преди тя да стане неизползваема завинаги.}other{Неправилен PUK код за SIM картата. Остават ви # опита, преди тя да стане неизползваема завинаги.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Операцията с ПИН кода за SIM картата не бе успешна!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Операцията с PUK кода за SIM картата не бе успешна!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Превключване на метода на въвеждане"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Устройството бе заключено ръчно"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Не е разпознато"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"За да ползвате „Отключване с лице“, вкл. достъпа до камерата от настройките"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Въведете ПИН кода за SIM картата – остават ви <xliff:g id="NUMBER_1">%d</xliff:g> опита.</item>
- <item quantity="one">Въведете ПИН кода за SIM картата – остава ви <xliff:g id="NUMBER_0">%d</xliff:g> опит, преди да се наложи да се свържете с оператора си, за да отключите устройството.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM картата вече е деактивирана. Въведете PUK кода, за да продължите. Остават ви <xliff:g id="_NUMBER_1">%d</xliff:g> опита, преди SIM картата да стане неизползваема завинаги. Свържете се с оператора за подробности.</item>
- <item quantity="one">SIM картата вече е деактивирана. Въведете PUK кода, за да продължите. Остава ви <xliff:g id="_NUMBER_0">%d</xliff:g> опит, преди SIM картата да стане неизползваема завинаги. Свържете се с оператора за подробности.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Въведете ПИН кода за SIM картата. Остава ви # опит, преди да трябва да се свържете с оператора си, за да отключите устройството.}other{Въведете ПИН кода за SIM картата. Остават ви # опита.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM картата вече е деактивирана. Въведете PUK кода, за да продължите. Остава ви # опит, преди SIM картата да стане неизползваема завинаги. Свържете се с оператора за подробности.}other{SIM картата вече е деактивирана. Въведете PUK кода, за да продължите. Остават ви # опита, преди SIM картата да стане неизползваема завинаги. Свържете се с оператора за подробности.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Стандартен"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Балонен"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Аналогов"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index bbc025fe08bb..cb9e9005a0cc 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"আপনি <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুলভাবে আপনার পাসওয়ার্ড লিখেছেন।\n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন।"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"আপনি <xliff:g id="NUMBER_0">%1$d</xliff:g> বার ভুলভাবে আপনার আনলকের প্যাটার্ন এঁকেছেন।\n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন।"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"ভুল সিম পিন কোড দিয়েছেন, আপনার ডিভাইসটি আনলক করতে এখন আপনাকে অবশ্যই আপনার পরিষেবা প্রদানকারীর সাথে যোগাযোগ করতে হবে।"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">সিমের পিন কোডটি ভুল, আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারেন।</item>
- <item quantity="other">সিমের পিন কোডটি ভুল, আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারেন।</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{সিম কার্ডের ভুল পিন কোড দিয়েছেন, আপনি আরও # বার চেষ্টা করতে পারবেন, তারপরে ডিভাইস আনলক করতে পরিষেবা প্রদানকারীর সাথে যোগাযোগ করতে হবে।}one{সিম কার্ডের ভুল পিন কোড দিয়েছেন, আপনি আরও # বার চেষ্টা করতে পারবেন। }other{সিম কার্ডের ভুল পিন কোড দিয়েছেন, আপনি আরও # বার চেষ্টা করতে পারবেন। }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"সিমটি ব্যবহার করা যাবে না। আপনার পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন।"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">সিমের PUK কোডটি ভুল, আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারেন, তারপর আপনার সিমটি স্থায়ীভাবে অব্যবহারযোগ্য হবে।</item>
- <item quantity="other">সিমের PUK কোডটি ভুল, আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারেন, তারপর আপনার সিমটি স্থায়ীভাবে অব্যবহারযোগ্য হবে।</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{সিম কার্ডের ভুল PUK কোড দিয়েছেন, আপনি আরও # বার চেষ্টা করতে পারবেন, তারপরে এই সিম কার্ডটি স্থায়ীভাবে কাজ করা বন্ধ করে দেবে।}one{সিম কার্ডের ভুল PUK কোড দিয়েছেন, আপনি আরও # বার চেষ্টা করতে পারবেন, তারপরে এই সিম কার্ডটি স্থায়ীভাবে কাজ করা বন্ধ করে দেবে।}other{সিম কার্ডের ভুল PUK কোড দিয়েছেন, আপনি আরও # বার চেষ্টা করতে পারবেন, তারপরে এই সিম কার্ডটি স্থায়ীভাবে কাজ করা বন্ধ করে দেবে।}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"সিম পিন দিয়ে আনলক করা যায়নি!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"সিম PUK দিয়ে আনলক করা যায়নি!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ইনপুট পদ্ধতি পরিবর্তন করুন"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ডিভাইসটিকে ম্যানুয়ালি লক করা হয়েছে"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"শনাক্ত করা যায়নি"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"\'ফেস আনলক\' ফিচার ব্যবহার করতে, \'সেটিংস\' থেকে ক্যামেরার অ্যাক্সেস চালু করুন"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">সিমের পিন লিখুন। আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন।</item>
- <item quantity="other">সিমের পিন লিখুন। আপনি আর <xliff:g id="NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন।</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">সিম অক্ষম করা হয়েছে। চালিয়ে যেতে PUK কোড লিখুন। আপনি আর <xliff:g id="_NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন, তারপরে এই সিমটি আর একেবারেই ব্যবহার করা যাবে না। বিশদে জানতে পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন।</item>
- <item quantity="other">সিম অক্ষম করা হয়েছে। চালিয়ে যেতে PUK কোড লিখুন। আপনি আর <xliff:g id="_NUMBER_1">%d</xliff:g> বার চেষ্টা করতে পারবেন, তারপরে এই সিমটি আর একেবারেই ব্যবহার করা যাবে না। বিশদে জানতে পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন।</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{সিম কার্ডের পিন লিখুন। আপনি আরও # বার চেষ্টা করতে পারবেন, তারপরে ডিভাইস আনলক করতে পরিষেবা প্রদানকারীর সাথে যোগাযোগ করতে হবে।}one{সিম কার্ডের পিন লিখুন। আপনি আরও # বার চেষ্টা করতে পারবেন।}other{সিম কার্ডের পিন লিখুন। আপনি আরও # বার চেষ্টা করতে পারবেন।}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{সিম কার্ডটি এখন বন্ধ করা হয়েছে। চালিয়ে যেতে PUK কোড লিখুন। আপনি আরও # বার চেষ্টা করতে পারবেন, তারপরে এই সিম কার্ডটি স্থায়ীভাবে কাজ করা বন্ধ করে দেবে। বিশদ বিবরণের জন্য পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন।}one{সিম কার্ডটি এখন বন্ধ করা হয়েছে। চালিয়ে যেতে PUK কোড লিখুন। আপনি আরও # বার চেষ্টা করতে পারবেন, তারপরে এই সিম কার্ডটি স্থায়ীভাবে কাজ করা বন্ধ করে দেবে। বিশদ বিবরণের জন্য পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন।}other{সিম কার্ডটি এখন বন্ধ করা হয়েছে। চালিয়ে যেতে PUK কোড লিখুন। আপনি আরও # বার চেষ্টা করতে পারবেন, তারপরে এই সিম কার্ডটি স্থায়ীভাবে কাজ করা বন্ধ করে দেবে। বিশদ বিবরণের জন্য পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন।}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"ডিফল্ট"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"বাবল"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"অ্যানালগ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index e0bec8463ed5..280d35409aa3 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -68,17 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Pogrešno ste unijeli lozinku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Pogrešno ste nacrtali svoj uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"PIN za SIM karticu je netačan. Za otključavanje uređaja sada se morate obratiti svom operateru."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">PIN za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
- <item quantity="few">PIN za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
- <item quantity="other">PIN za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{PIN kôd za SIM je netačan. Imate još # pokušaj prije nego što budete morali kontaktirati mobilnog operatera da vam otključa uređaj.}one{PIN kôd za SIM je netačan. Imate još # pokušaj. }few{PIN kôd za SIM je netačan. Imate još # pokušaja. }other{PIN kôd za SIM je netačan. Imate još # pokušaja. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM kartica je neupotrebljiva. Obratite se svom operateru."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">PUK kôd za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj prije nego što SIM kartica postane trajno neupotrebljiva.</item>
- <item quantity="few">PUK kôd za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva.</item>
- <item quantity="other">PUK kôd za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{PUK kôd za SIM karticu je netačan. Imate još # pokušaj prije nego što SIM postane trajno neupotrebljiv.}one{PUK kôd za SIM karticu je netačan. Imate još # pokušaj prije nego što SIM postane trajno neupotrebljiv.}few{PUK kôd za SIM karticu je netačan. Imate još # pokušaja prije nego što SIM postane trajno neupotrebljiv.}other{PUK kôd za SIM karticu je netačan. Imate još # pokušaja prije nego što SIM postane trajno neupotrebljiv.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Korištenje PIN-a za SIM karticu nije uspjelo!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Korištenje PUK-a za SIM nije uspjelo!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Promjena načina unosa"</string>
@@ -93,16 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Uređaj je ručno zaključan"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nije prepoznato"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Za otključavanje licem uključite pristup kameri"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
- <item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
- <item quantity="other">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">SIM kartica je onemogućena. Unesite PUK kôd da nastavite. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaj prije nego što SIM kartica postane trajno neupotrebljiva. Za više informacija kontaktirajte mobilnog operatera.</item>
- <item quantity="few">SIM kartica je onemogućena. Unesite PUK kôd da nastavite. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Za više informacija kontaktirajte mobilnog operatera.</item>
- <item quantity="other">SIM kartica je onemogućena. Unesite PUK kôd da nastavite. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Za više informacija kontaktirajte mobilnog operatera.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Unesite PIN za SIM. Imate još # pokušaj prije nego što budete morali kontaktirati mobilnog operatera da vam otključa uređaj.}one{Unesite PIN za SIM. Imate još # pokušaj.}few{Unesite PIN za SIM. Imate još # pokušaja.}other{Unesite PIN za SIM. Imate još # pokušaja.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM je sada onemogućen. Unesite PUK kôd da nastavite. Imate još # pokušaj prije nego što SIM postane trajno neupotrebljiv. Za više informacija kontaktirajte mobilnog operatera.}one{SIM je sada onemogućen. Unesite PUK kôd da nastavite. Imate još # pokušaja prije nego što SIM postane trajno neupotrebljiv. Za više informacija kontaktirajte mobilnog operatera.}few{SIM je sada onemogućen. Unesite PUK kôd da nastavite. Imate još # pokušaja prije nego što SIM postane trajno neupotrebljiv. Za više informacija kontaktirajte mobilnog operatera.}other{SIM je sada onemogućen. Unesite PUK kôd da nastavite. Imate još # pokušaja prije nego što SIM postane trajno neupotrebljiv. Za više informacija kontaktirajte mobilnog operatera.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Zadano"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Mjehurići"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogni"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index 1e30e788ed3a..b1f5a3f9206f 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Has escrit la contrasenya <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"El codi PIN de la SIM no és correcte. Contacta amb l\'operador de telefonia mòbil per desbloquejar el dispositiu."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">El codi PIN de la SIM no és correcte. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents.</item>
- <item quantity="one">El codi PIN de la SIM no és correcte. Et queda <xliff:g id="NUMBER_0">%d</xliff:g> intent; si no l\'encertes, contacta amb l\'operador de telefonia mòbil per desbloquejar el dispositiu.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{El codi PIN de la SIM no és correcte. Et queda # intent; si no l\'encertes, contacta amb l\'operador per desbloquejar el dispositiu.}other{El codi PIN de la SIM no és correcte. Et queden # intents. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"La SIM no es pot fer servir. Contacta amb l\'operador de telefonia mòbil."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">El codi PUK de la SIM no és correcte. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents; si els esgotes tots, la SIM no es podrà tornar a fer servir.</item>
- <item quantity="one">El codi PUK de la SIM no és correcte. Et queda <xliff:g id="NUMBER_0">%d</xliff:g> intent; si no l\'encertes, la SIM no es podrà tornar a fer servir.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{El codi PUK de la SIM no és correcte. Et queda # intent; si no l\'encertes, la SIM no es podrà tornar a fer servir.}other{El codi PUK de la SIM no és correcte. Et queden # intents; si no l\'encertes, la SIM no es podrà tornar a fer servir.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Ha fallat l\'operació del PIN de la SIM"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"No s\'ha pogut desbloquejar la SIM amb el codi PUK."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Canvia el mètode d\'introducció"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"El dispositiu s\'ha bloquejat manualment"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"No s\'ha reconegut"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Desbloqueig facial necessita accés a la càmera"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Introdueix el PIN de la SIM. Et queden <xliff:g id="NUMBER_1">%d</xliff:g> intents.</item>
- <item quantity="one">Introdueix el PIN de la SIM. Et queda <xliff:g id="NUMBER_0">%d</xliff:g> intent; si no l\'encertes, contacta amb l\'operador de telefonia mòbil per desbloquejar el dispositiu.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">La targeta SIM s\'ha desactivat. Introdueix el codi PUK per continuar. Et queden <xliff:g id="_NUMBER_1">%d</xliff:g> intents; si no l\'encertes, la SIM no es podrà tornar a fer servir. Contacta amb l\'operador de telefonia mòbil per obtenir-ne més informació.</item>
- <item quantity="one">La targeta SIM s\'ha desactivat. Introdueix el codi PUK per continuar. Et queda <xliff:g id="_NUMBER_0">%d</xliff:g> intent; si no l\'encertes, la SIM no es podrà tornar a fer servir. Contacta amb l\'operador de telefonia mòbil per obtenir-ne més informació.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Introdueix el PIN de la SIM. Et queda # intent; si no l\'encertes, contacta amb l\'operador per desbloquejar el dispositiu.}other{Introdueix el PIN de la SIM. Et queden # intents.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{La targeta SIM s\'ha desactivat. Introdueix el codi PUK per continuar. Et queda # intent; si no l\'encertes, la SIM no es podrà tornar a fer servir. Contacta amb l\'operador per obtenir informació.}other{La targeta SIM s\'ha desactivat. Introdueix el codi PUK per continuar. Et queden # intents; si no l\'encertes, la SIM no es podrà tornar a fer servir. Contacta amb l\'operador per obtenir informació.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Predeterminada"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bombolla"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analògica"</string>
diff --git a/packages/SystemUI/res-keyguard/values-cs/strings.xml b/packages/SystemUI/res-keyguard/values-cs/strings.xml
index 3c37bba3fe81..1b52635052e5 100644
--- a/packages/SystemUI/res-keyguard/values-cs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-cs/strings.xml
@@ -68,19 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Již <xliff:g id="NUMBER_0">%1$d</xliff:g>krát jste nesprávně zadali heslo. \n\nZkuste to znovu za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Již <xliff:g id="NUMBER_0">%1$d</xliff:g>krát jste zadali nesprávné bezpečnostní gesto. \n\nZkuste to znovu za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Zadali jste nesprávný kód PIN SIM karty. Nyní musíte za účelem odemknutí zařízení kontaktovat svého operátora."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="few">Zadali jste nesprávný kód PIN SIM karty. Máte ještě <xliff:g id="NUMBER_1">%d</xliff:g> pokusy.</item>
- <item quantity="many">Zadali jste nesprávný kód PIN SIM karty. Máte ještě <xliff:g id="NUMBER_1">%d</xliff:g> pokusu.</item>
- <item quantity="other">Zadali jste nesprávný kód PIN SIM karty. Máte ještě <xliff:g id="NUMBER_1">%d</xliff:g> pokusů.</item>
- <item quantity="one">Zadali jste nesprávný PIN SIM karty. Zbývá <xliff:g id="NUMBER_0">%d</xliff:g> pokus, poté bude muset zařízení odemknout operátor.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Zadali jste nesprávný PIN SIM karty. Zbývá vám # pokus, poté zařízení bude muset odemknout operátor.}few{Zadali jste nesprávný PIN SIM karty. Máte ještě # pokusy. }many{Zadali jste nesprávný PIN SIM karty. Máte ještě # pokusu. }other{Zadali jste nesprávný PIN SIM karty. Máte ještě # pokusů. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM kartu nelze použít. Kontaktujte operátora."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="few">Nesprávný kód PUK SIM karty. Máte ještě <xliff:g id="NUMBER_1">%d</xliff:g> pokusy, poté bude SIM karta natrvalo zablokována.</item>
- <item quantity="many">Nesprávný kód PUK SIM karty. Máte ještě <xliff:g id="NUMBER_1">%d</xliff:g> pokusu, poté bude SIM karta natrvalo zablokována.</item>
- <item quantity="other">Nesprávný kód PUK SIM karty. Máte ještě <xliff:g id="NUMBER_1">%d</xliff:g> pokusů, poté bude SIM karta natrvalo zablokována.</item>
- <item quantity="one">Nesprávný kód PUK SIM karty. Máte ještě <xliff:g id="NUMBER_0">%d</xliff:g> pokus, poté bude SIM karta natrvalo zablokována.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Nesprávný PUK SIM karty. Máte ještě # pokus, poté bude SIM karta natrvalo zablokována.}few{Nesprávný PUK SIM karty. Máte ještě # pokusy, poté bude SIM karta natrvalo zablokována.}many{Nesprávný PUK SIM karty. Máte ještě # pokusu, poté bude SIM karta natrvalo zablokována.}other{Nesprávný PUK SIM karty. Máte ještě # pokusů, poté bude SIM karta natrvalo zablokována.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Operace pomocí kódu PIN SIM karty se nezdařila."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Operace pomocí kódu PUK SIM karty se nezdařila."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Přepnout metodu zadávání"</string>
@@ -95,18 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Zařízení bylo ručně uzamčeno"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nerozpoznáno"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"V Nastavení zapněte přístup k fotoaparátu"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="few">Zadejte PIN SIM karty. Zbývají <xliff:g id="NUMBER_1">%d</xliff:g> pokusy.</item>
- <item quantity="many">Zadejte PIN SIM karty. Zbývá <xliff:g id="NUMBER_1">%d</xliff:g> pokusu.</item>
- <item quantity="other">Zadejte PIN SIM karty. Zbývá <xliff:g id="NUMBER_1">%d</xliff:g> pokusů.</item>
- <item quantity="one">Zadejte PIN SIM karty. Zbývá <xliff:g id="NUMBER_0">%d</xliff:g> pokus, poté bude muset zařízení odemknout operátor.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="few">SIM karta je teď zablokována. Chcete-li pokračovat, zadejte kód PUK. Máte ještě <xliff:g id="_NUMBER_1">%d</xliff:g> pokusy, poté bude SIM karta natrvalo zablokována. Podrobnosti vám poskytne operátor.</item>
- <item quantity="many">SIM karta je teď zablokována. Chcete-li pokračovat, zadejte kód PUK. Máte ještě <xliff:g id="_NUMBER_1">%d</xliff:g> pokusu, poté bude SIM karta natrvalo zablokována. Podrobnosti vám poskytne operátor.</item>
- <item quantity="other">SIM karta je teď zablokována. Chcete-li pokračovat, zadejte kód PUK. Máte ještě <xliff:g id="_NUMBER_1">%d</xliff:g> pokusů, poté bude SIM karta natrvalo zablokována. Podrobnosti vám poskytne operátor.</item>
- <item quantity="one">SIM karta je teď zablokována. Chcete-li pokračovat, zadejte kód PUK. Máte ještě <xliff:g id="_NUMBER_0">%d</xliff:g> pokus, poté bude SIM karta natrvalo zablokována. Podrobnosti vám poskytne operátor.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Zadejte PIN SIM karty. Zbývá # pokus, poté bude muset zařízení odemknout operátor.}few{Zadejte PIN SIM karty. Zbývají # pokusy.}many{Zadejte PIN SIM karty. Zbývá # pokusu.}other{Zadejte PIN SIM karty. Zbývá # pokusů.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM karta je nyní zablokována. Chcete-li pokračovat, zadejte PUK. Máte ještě # pokus, poté bude SIM karta natrvalo zablokována. Podrobnosti poskytne operátor.}few{SIM karta je nyní zablokována. Chcete-li pokračovat, zadejte PUK. Máte ještě # pokusy, poté bude SIM karta natrvalo zablokována. Podrobnosti poskytne operátor.}many{SIM karta je nyní zablokována. Chcete-li pokračovat, zadejte PUK. Máte ještě # pokusu, poté bude SIM karta natrvalo zablokována. Podrobnosti poskytne operátor.}other{SIM karta je nyní zablokována. Chcete-li pokračovat, zadejte PUK. Máte ještě # pokusů, poté bude SIM karta natrvalo zablokována. Podrobnosti poskytne operátor.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Výchozí"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bublina"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogové"</string>
diff --git a/packages/SystemUI/res-keyguard/values-da/strings.xml b/packages/SystemUI/res-keyguard/values-da/strings.xml
index 0774c520cb62..cb8ad8f7e375 100644
--- a/packages/SystemUI/res-keyguard/values-da/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-da/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Du har indtastet din adgangskode forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. \n\nPrøv igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%1$d</xliff:g> gange. \n\nPrøv igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Forkert pinkode til SIM-kort. Du er nu nødt til at kontakte dit mobilselskab for at låse din enhed op."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Forkert pinkode til SIM-kort. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage.</item>
- <item quantity="other">Forkert pinkode til SIM-kort. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Du har angivet en forkert pinkode til SIM-kortet. Du har # forsøg tilbage, før du skal kontakte dit mobilselskab for at få låst din enhed op.}one{Du har angivet en forkert pinkode til SIM-kortet. Du har # forsøg tilbage. }other{Du har angivet en forkert pinkode til SIM-kortet. Du har # forsøg tilbage. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM-kortet er ubrugeligt. Kontakt dit mobilselskab."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Forkert PUK-kode til SIM-kort. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage, før SIM-kortet bliver permanent ubrugeligt.</item>
- <item quantity="other">Forkert PUK-kode til SIM-kort. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage, før SIM-kortet bliver permanent ubrugeligt.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Du har angivet en forkert PUK-kode til SIM-kortet. Du har # forsøg tilbage, før SIM-kortet bliver permanent ubrugeligt.}one{Du har angivet en forkert PUK-kode til SIM-kortet. Du har # forsøg tilbage, før SIM-kortet bliver permanent ubrugeligt.}other{Du har angivet en forkert PUK-kode til SIM-kortet. Du har # forsøg tilbage, før SIM-kortet bliver permanent ubrugeligt.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Pinkoden til SIM-kortet blev afvist"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"PUK-koden til SIM-kortet blev afvist"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Skift indtastningsmetode"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Enheden blev låst manuelt"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Ikke genkendt"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Aktivér kameraadgang i Indstillinger for at bruge ansigtslås"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Angiv pinkoden til SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage.</item>
- <item quantity="other">Angiv pinkoden til SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøg tilbage.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">SIM-kortet er nu deaktiveret. Angiv PUK-koden for at fortsætte. Du har <xliff:g id="_NUMBER_1">%d</xliff:g> forsøg tilbage, før SIM-kortet bliver permanent ubrugeligt. Kontakt dit mobilselskab for at få flere oplysninger.</item>
- <item quantity="other">SIM-kortet er nu deaktiveret. Angiv PUK-koden for at fortsætte. Du har <xliff:g id="_NUMBER_1">%d</xliff:g> forsøg tilbage, før SIM-kortet bliver permanent ubrugeligt. Kontakt dit mobilselskab for at få flere oplysninger.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Angiv pinkoden til SIM-kortet. Du har # forsøg tilbage, før du skal kontakte dit mobilselskab for at låse din enhed op.}one{Angiv pinkoden til SIM-kortet. Du har # forsøg tilbage.}other{Angiv pinkoden til SIM-kortet. Du har # forsøg tilbage.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM-kortet er nu deaktiveret. Angiv PUK-koden for at fortsætte. Du har # forsøg tilbage, før SIM-kortet bliver permanent ubrugeligt. Kontakt dit mobilselskab for at få flere oplysninger.}one{SIM-kortet er nu deaktiveret. Angiv PUK-koden for at fortsætte. Du har # forsøg tilbage, før SIM-kortet bliver permanent ubrugeligt. Kontakt dit mobilselskab for at få flere oplysninger.}other{SIM-kortet er nu deaktiveret. Angiv PUK-koden for at fortsætte. Du har # forsøg tilbage, før SIM-kortet bliver permanent ubrugeligt. Kontakt dit mobilselskab for at få flere oplysninger.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Standard"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Boble"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index c46c08f0e7e9..579c5141ddde 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Du hast dein Passwort <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch eingegeben.\n\nBitte versuche es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Du hast dein Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. \n\nBitte versuche es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Falscher PIN-Code der SIM-Karte. Bitte wende dich an deinen Mobilfunkanbieter, damit er dein Gerät entsperrt."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Falscher PIN-Code der SIM-Karte. Du hast noch <xliff:g id="NUMBER_1">%d</xliff:g> Versuche.</item>
- <item quantity="one">Falscher PIN-Code der SIM-Karte. Du hast noch <xliff:g id="NUMBER_0">%d</xliff:g> Versuch, bevor du das Gerät von deinem Mobilfunkanbieter entsperren lassen musst.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Falscher PIN-Code für die SIM-Karte. Du hast noch # Versuch, bevor das Gerät nur noch vom Mobilfunkanbieter entsperrt werden kann.}other{Falscher PIN-Code für die SIM-Karte. Du hast noch # Versuche. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Die SIM-Karte kann nicht verwendet werden. Bitte wende dich an deinen Mobilfunkanbieter."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Falscher PUK-Code der SIM-Karte. Du hast noch <xliff:g id="NUMBER_1">%d</xliff:g> Versuche, bevor deine SIM-Karte endgültig gesperrt wird.</item>
- <item quantity="one">Falscher PUK-Code der SIM-Karte. Du hast noch <xliff:g id="NUMBER_0">%d</xliff:g> Versuch, bevor deine SIM-Karte endgültig gesperrt wird.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Falscher PUK-Code für die SIM-Karte. Du hast noch # Versuch, bevor die SIM-Karte endgültig gesperrt wird.}other{Falscher PUK-Code für die SIM-Karte. Du hast noch # Versuche, bevor die SIM-Karte endgültig gesperrt wird.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Fehler beim Entsperren der SIM-Karte mit der PIN."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Fehler beim Entsperren der SIM-Karte mithilfe des PUK-Codes."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Eingabemethode wechseln"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Gerät manuell gesperrt"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nicht erkannt"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Die Gesichtsentsperrung benötigt Kamerazugriff"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Gib die PIN für die SIM-Karte ein. Du hast noch <xliff:g id="NUMBER_1">%d</xliff:g> Versuche.</item>
- <item quantity="one">Gib die PIN für die SIM-Karte ein. Du hast noch <xliff:g id="NUMBER_0">%d</xliff:g> Versuch, bevor das Gerät nur noch vom Mobilfunkanbieter entsperrt werden kann.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">Die SIM-Karte ist jetzt deaktiviert. Gib den PUK-Code ein, um fortzufahren. Du hast noch <xliff:g id="_NUMBER_1">%d</xliff:g> Versuche, bevor die SIM-Karte endgültig gesperrt wird. Weitere Informationen erhältst du von deinem Mobilfunkanbieter.</item>
- <item quantity="one">Die SIM-Karte ist jetzt deaktiviert. Gib den PUK-Code ein, um fortzufahren. Du hast noch <xliff:g id="_NUMBER_0">%d</xliff:g> Versuch, bevor die SIM-Karte endgültig gesperrt wird. Weitere Informationen erhältst du von deinem Mobilfunkanbieter.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Gib die PIN für die SIM-Karte ein. Du hast noch # Versuch, bevor das Gerät nur noch vom Mobilfunkanbieter entsperrt werden kann.}other{Gib die PIN für die SIM-Karte ein. Du hast noch # Versuche.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Die SIM-Karte ist jetzt deaktiviert. Gib den PUK-Code ein, um fortzufahren. Du hast noch # Versuch, bevor die SIM-Karte endgültig gesperrt wird. Wende dich für weitere Informationen an deinen Mobilfunkanbieter.}other{Die SIM-Karte ist jetzt deaktiviert. Gib den PUK-Code ein, um fortzufahren. Du hast noch # Versuche, bevor die SIM-Karte endgültig gesperrt wird. Wende dich für weitere Informationen an deinen Mobilfunkanbieter.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Standard"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bubble"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml
index 73afafa7e0cb..8dd5d2087fa4 100644
--- a/packages/SystemUI/res-keyguard/values-el/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-el/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Έχετε πληκτρολογήσει τον κωδικό πρόσβασης εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος<xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Λανθασμένος κωδικός PIN κάρτας SIM. Θα πρέπει να επικοινωνήσετε με την εταιρεία κινητής τηλεφωνίας σας για να ξεκλειδώσετε τη συσκευή σας."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Λανθασμένος κωδικός PIN κάρτας SIM. Απομένουν άλλες <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες.</item>
- <item quantity="one">Λανθασμένος κωδικός PIN κάρτας SIM. Απομένει άλλη <xliff:g id="NUMBER_0">%d</xliff:g> προσπάθεια. Στη συνέχεια, θα πρέπει να επικοινωνήσετε με την εταιρεία κινητής τηλεφωνίας σας για να ξεκλειδώσετε τη συσκευή σας.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Εσφαλμένος κωδικός PIN κάρτας SIM. Απομένει άλλη # προσπάθεια. Στη συνέχεια, θα πρέπει να επικοινωνήσετε με τον πάροχο κινητής τηλεφωνίας σας για να ξεκλειδώσετε τη συσκευή σας.}other{Εσφαλμένος κωδικός PIN κάρτας SIM. Απομένουν # προσπάθειες. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Η κάρτα SIM δεν μπορεί να χρησιμοποιηθεί. Επικοινωνήστε με την εταιρεία κινητής τηλεφωνίας σας."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Λανθασμένος κωδικός PUK κάρτας SIM. Απομένουν άλλες <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες προτού να μην είναι πλέον δυνατή η χρήση της κάρτας SIM.</item>
- <item quantity="one">Λανθασμένος κωδικός PUK κάρτας SIM. Απομένει άλλη <xliff:g id="NUMBER_0">%d</xliff:g> προσπάθεια προτού να μην είναι πλέον δυνατή η χρήση της κάρτας SIM.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Λανθασμένος κωδικός PUK κάρτας SIM. Απομένει άλλη # προσπάθεια προτού να μην είναι πλέον δυνατή η χρήση της κάρτας SIM.}other{Εσφαλμένος κωδικός PUK κάρτας SIM. Απομένουν # προσπάθειες προτού να μην είναι πλέον δυνατή η χρήση της κάρτας SIM.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Αποτυχία λειτουργίας κωδικού PIN κάρτας SIM!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Αποτυχία λειτουργίας κωδικού PUK κάρτας SIM!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Εναλλαγή μεθόδου εισαγωγής"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Η συσκευή κλειδώθηκε με μη αυτόματο τρόπο"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Δεν αναγνωρίστηκε"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Ενεργοπ. πρόσβ. κάμ. στις Ρυθμ. για Ξεκλείδ. με το πρόσωπο."</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Εισαγάγετε τον αριθμό PIN της κάρτας SIM. Απομένουν άλλες <xliff:g id="NUMBER_1">%d</xliff:g> προσπάθειες.</item>
- <item quantity="one">Εισαγάγετε τον αριθμό PIN της κάρτας SIM. Απομένει άλλη <xliff:g id="NUMBER_0">%d</xliff:g> προσπάθεια. Στη συνέχεια, θα πρέπει να επικοινωνήσετε με τον πάροχο κινητής τηλεφωνίας, για να ξεκλειδώσετε τη συσκευή.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">Η κάρτα SIM απενεργοποιήθηκε. Καταχωρίστε τον κωδικό PUK, για να συνεχίσετε. Απομένουν <xliff:g id="_NUMBER_1">%d</xliff:g> ακόμη προσπάθειες προτού να μην είναι πλέον δυνατή η χρήση της κάρτας SIM. Επικοινωνήστε με την εταιρεία κινητής τηλεφωνίας για λεπτομέρειες.</item>
- <item quantity="one">Η κάρτα SIM απενεργοποιήθηκε. Καταχωρίστε τον κωδικό PUK, για να συνεχίσετε. Απομένει <xliff:g id="_NUMBER_0">%d</xliff:g> ακόμη προσπάθεια προτού να μην είναι πλέον δυνατή η χρήση της κάρτας SIM. Επικοινωνήστε με την εταιρεία κινητής τηλεφωνίας για λεπτομέρειες.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Εισαγάγετε τον κωδικό PIN της κάρτας SIM. Απομένει άλλη # προσπάθεια. Στη συνέχεια, θα πρέπει να επικοινωνήσετε με τον πάροχο κινητής τηλεφωνίας, για να ξεκλειδώσετε τη συσκευή.}other{Εισαγάγετε τον κωδικό PIN της κάρτας SIM. Απομένουν # προσπάθειες.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Η κάρτα SIM απενεργοποιήθηκε. Εισαγάγετε τον κωδικό PUK, για να συνεχίσετε. Απομένει # ακόμη προσπάθεια προτού να μην είναι πλέον δυνατή η χρήση της κάρτας SIM. Επικοινωνήστε με την εταιρεία κινητής τηλεφωνίας για λεπτομέρειες.}other{Η κάρτα SIM απενεργοποιήθηκε. Εισαγάγετε τον κωδικό PUK, για να συνεχίσετε. Απομένουν # ακόμη προσπάθειες προτού να μην είναι πλέον δυνατή η χρήση της κάρτας SIM. Επικοινωνήστε με την εταιρεία κινητής τηλεφωνίας για λεπτομέρειες.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Προεπιλογή"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Συννεφάκι"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Αναλογικό"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
index 0d08c428307c..865ebab28d9e 100644
--- a/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rAU/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Incorrect SIM PIN code; you must now contact your operator to unlock your device."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Incorrect SIM PIN code. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
- <item quantity="one">Incorrect SIM PIN code. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your provider to unlock your device.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Incorrect SIM PIN code; you have # remaining attempt before you must contact your operator to unlock your device.}other{Incorrect SIM PIN code; you have # remaining attempts. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM is unusable. Contact your operator."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Incorrect SIM PUK code. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable.</item>
- <item quantity="one">Incorrect SIM PUK code. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Incorrect SIM PUK code; you have # remaining attempt before SIM becomes permanently unusable.}other{Incorrect SIM PUK code, you have # remaining attempts before SIM becomes permanently unusable.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM PIN operation failed!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUK operation failed!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Switch input method"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"To use Face Unlock, turn on camera access in Settings"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
- <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact operator for details.</item>
- <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact operator for details.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Enter SIM PIN. You have # remaining attempt before you must contact your operator to unlock your device.}other{Enter SIM PIN. You have # remaining attempts.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM is now disabled. Enter PUK code to continue. You have # remaining attempt before SIM becomes permanently unusable. Contact operator for details.}other{SIM is now disabled. Enter PUK code to continue. You have # remaining attempts before SIM becomes permanently unusable. Contact operator for details.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Default"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bubble"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogue"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
index 3356ec98ad2c..9b4df35f1754 100644
--- a/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rCA/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Incorrect SIM PIN code you must now contact your carrier to unlock your device."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Incorrect SIM PIN code, you have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
- <item quantity="one">Incorrect SIM PIN code, you have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your carrier to unlock your device.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Incorrect SIM PIN code, you have # remaining attempt before you must contact your carrier to unlock your device.}other{Incorrect SIM PIN code, you have # remaining attempts. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM is unusable. Contact your carrier."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Incorrect SIM PUK code. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable.</item>
- <item quantity="one">Incorrect SIM PUK code. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Incorrect SIM PUK code; you have # remaining attempt before SIM becomes permanently unusable.}other{Incorrect SIM PUK code, you have # remaining attempts before SIM becomes permanently unusable.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM PIN operation failed!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUK operation failed!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Switch input method"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"To use Face Unlock, turn on camera access in Settings"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
- <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your carrier to unlock your device.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item>
- <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact carrier for details.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Enter SIM PIN. You have # remaining attempt before you must contact your carrier to unlock your device.}other{Enter SIM PIN. You have # remaining attempts.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM is now disabled. Enter PUK code to continue. You have # remaining attempt before SIM becomes permanently unusable. Contact carrier for details.}other{SIM is now disabled. Enter PUK code to continue. You have # remaining attempts before SIM becomes permanently unusable. Contact carrier for details.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Default"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bubble"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogue"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
index 0d08c428307c..865ebab28d9e 100644
--- a/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rGB/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Incorrect SIM PIN code; you must now contact your operator to unlock your device."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Incorrect SIM PIN code. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
- <item quantity="one">Incorrect SIM PIN code. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your provider to unlock your device.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Incorrect SIM PIN code; you have # remaining attempt before you must contact your operator to unlock your device.}other{Incorrect SIM PIN code; you have # remaining attempts. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM is unusable. Contact your operator."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Incorrect SIM PUK code. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable.</item>
- <item quantity="one">Incorrect SIM PUK code. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Incorrect SIM PUK code; you have # remaining attempt before SIM becomes permanently unusable.}other{Incorrect SIM PUK code, you have # remaining attempts before SIM becomes permanently unusable.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM PIN operation failed!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUK operation failed!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Switch input method"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"To use Face Unlock, turn on camera access in Settings"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
- <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact operator for details.</item>
- <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact operator for details.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Enter SIM PIN. You have # remaining attempt before you must contact your operator to unlock your device.}other{Enter SIM PIN. You have # remaining attempts.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM is now disabled. Enter PUK code to continue. You have # remaining attempt before SIM becomes permanently unusable. Contact operator for details.}other{SIM is now disabled. Enter PUK code to continue. You have # remaining attempts before SIM becomes permanently unusable. Contact operator for details.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Default"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bubble"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogue"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
index 0d08c428307c..865ebab28d9e 100644
--- a/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rIN/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%1$d</xliff:g> times. \n\nTry again in <xliff:g id="NUMBER_1">%2$d</xliff:g> seconds."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Incorrect SIM PIN code; you must now contact your operator to unlock your device."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Incorrect SIM PIN code. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
- <item quantity="one">Incorrect SIM PIN code. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your provider to unlock your device.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Incorrect SIM PIN code; you have # remaining attempt before you must contact your operator to unlock your device.}other{Incorrect SIM PIN code; you have # remaining attempts. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM is unusable. Contact your operator."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Incorrect SIM PUK code. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable.</item>
- <item quantity="one">Incorrect SIM PUK code. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Incorrect SIM PUK code; you have # remaining attempt before SIM becomes permanently unusable.}other{Incorrect SIM PUK code, you have # remaining attempts before SIM becomes permanently unusable.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM PIN operation failed!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUK operation failed!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Switch input method"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Device was locked manually"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Not recognised"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"To use Face Unlock, turn on camera access in Settings"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
- <item quantity="one">Enter SIM PIN. You have <xliff:g id="NUMBER_0">%d</xliff:g> remaining attempt before you must contact your operator to unlock your device.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_1">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact operator for details.</item>
- <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="_NUMBER_0">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact operator for details.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Enter SIM PIN. You have # remaining attempt before you must contact your operator to unlock your device.}other{Enter SIM PIN. You have # remaining attempts.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM is now disabled. Enter PUK code to continue. You have # remaining attempt before SIM becomes permanently unusable. Contact operator for details.}other{SIM is now disabled. Enter PUK code to continue. You have # remaining attempts before SIM becomes permanently unusable. Contact operator for details.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Default"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bubble"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogue"</string>
diff --git a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
index 3c3194283be4..afb3d655cb30 100644
--- a/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-en-rXC/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‎‏‎‏‎‏‎‏‏‎‎You have incorrectly typed your password ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%1$d</xliff:g>‎‏‎‎‏‏‏‎ times. ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Try again in ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%2$d</xliff:g>‎‏‎‎‏‏‏‎ seconds.‎‏‎‎‏‎"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎‎‎‎You have incorrectly drawn your unlock pattern ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%1$d</xliff:g>‎‏‎‎‏‏‏‎ times. ‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Try again in ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%2$d</xliff:g>‎‏‎‎‏‏‏‎ seconds.‎‏‎‎‏‎"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‎‎‏‎‏‎‎‎‏‎‏‎Incorrect SIM PIN code you must now contact your carrier to unlock your device.‎‏‎‎‏‎"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‎‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‎‏‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‎‎Incorrect SIM PIN code, you have ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%d</xliff:g>‎‏‎‎‏‏‏‎ remaining attempts.‎‏‎‎‏‎</item>
- <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‎‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‎‏‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‎‎Incorrect SIM PIN code, you have ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%d</xliff:g>‎‏‎‎‏‏‏‎ remaining attempt before you must contact your carrier to unlock your device.‎‏‎‎‏‎</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‎‎‎‎‏‎‏‎Incorrect SIM PIN code, you have # remaining attempt before you must contact your carrier to unlock your device.‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‎‎‎‎‏‎‏‎Incorrect SIM PIN code, you have # remaining attempts. ‎‏‎‎‏‎}}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‎‏‎‏‏‏‏‎‎‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‏‎SIM is unusable. Contact your carrier.‎‏‎‎‏‎"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‏‎‎‏‏‎‎Incorrect SIM PUK code, you have ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%d</xliff:g>‎‏‎‎‏‏‏‎ remaining attempts before SIM becomes permanently unusable.‎‏‎‎‏‎</item>
- <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‏‎‎‏‏‎‎Incorrect SIM PUK code, you have ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%d</xliff:g>‎‏‎‎‏‏‏‎ remaining attempt before SIM becomes permanently unusable.‎‏‎‎‏‎</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‎‎‎‎‏‎‏‎‎‏‏‎Incorrect SIM PUK code, you have # remaining attempt before SIM becomes permanently unusable.‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‎‎‎‎‏‎‏‎‎‏‏‎Incorrect SIM PUK code, you have # remaining attempts before SIM becomes permanently unusable.‎‏‎‎‏‎}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‎‎‎‎‏‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‏‎‎‏‎‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‎‎‏‎SIM PIN operation failed!‎‏‎‎‏‎"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‎‏‎‏‎‎‏‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‎‎SIM PUK operation failed!‎‏‎‎‏‎"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‎‏‏‎‎‎‎‏‏‎‏‎‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‎‎‎‏‎‎‎‏‎Switch input method‎‏‎‎‏‎"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎‎‎‎‎Device was locked manually‎‏‎‎‏‎"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎Not recognized‎‏‎‎‏‎"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‎‎‎‎‎‏‏‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‎‎To use Face Unlock, turn on camera access in Settings‎‏‎‎‏‎"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‎‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‎Enter SIM PIN. You have ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%d</xliff:g>‎‏‎‎‏‏‏‎ remaining attempts.‎‏‎‎‏‎</item>
- <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‎‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‎Enter SIM PIN. You have ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%d</xliff:g>‎‏‎‎‏‏‏‎ remaining attempt before you must contact your carrier to unlock your device.‎‏‎‎‏‎</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‎‏‏‏‎SIM is now disabled. Enter PUK code to continue. You have ‎‏‎‎‏‏‎<xliff:g id="_NUMBER_1">%d</xliff:g>‎‏‎‎‏‏‏‎ remaining attempts before SIM becomes permanently unusable. Contact carrier for details.‎‏‎‎‏‎</item>
- <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‎‏‏‏‎SIM is now disabled. Enter PUK code to continue. You have ‎‏‎‎‏‏‎<xliff:g id="_NUMBER_0">%d</xliff:g>‎‏‎‎‏‏‏‎ remaining attempt before SIM becomes permanently unusable. Contact carrier for details.‎‏‎‎‏‎</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‎‏‎Enter SIM PIN. You have # remaining attempt before you must contact your carrier to unlock your device.‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‎‏‎Enter SIM PIN. You have # remaining attempts.‎‏‎‎‏‎}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‎‎SIM is now disabled. Enter PUK code to continue. You have # remaining attempt before SIM becomes permanently unusable. Contact carrier for details.‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‎‎SIM is now disabled. Enter PUK code to continue. You have # remaining attempts before SIM becomes permanently unusable. Contact carrier for details.‎‏‎‎‏‎}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‎‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‎‎‎‎‎‎‏‎‎‏‏‎‎‎‎Default‎‏‎‎‏‎"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‏‎‏‎Bubble‎‏‎‎‏‎"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‎‎‎‎‎‏‏‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‏‎Analog‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
index 708e6330ed53..ea3b8a749621 100644
--- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Escribiste tu contraseña <xliff:g id="NUMBER_0">%1$d</xliff:g> veces de manera incorrecta. \n\nVuelve a intentarlo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Dibujaste tu patrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces de manera incorrecta. \n\nVuelve a intentarlo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"El código PIN de la tarjeta SIM es incorrecto. Debes comunicarte con tu proveedor para desbloquear el dispositivo."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">El código PIN de la tarjeta SIM es incorrecto. Te quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos más.</item>
- <item quantity="one">El código PIN de la tarjeta SIM es incorrecto. Te queda <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que debas comunicarte con tu proveedor para desbloquear el dispositivo.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{El código PIN de la tarjeta SIM es incorrecto. Tienes # intento restante antes de que debas comunicarte con tu operador para desbloquear el dispositivo.}other{El código PIN de la tarjeta SIM es incorrecto. Tienes # intentos restantes. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"La tarjeta SIM no se puede usar. Comunícate con tu proveedor."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">El código PUK de la tarjeta SIM es incorrecto. Tienes <xliff:g id="NUMBER_1">%d</xliff:g> intentos más antes de que la tarjeta SIM quede inutilizable de manera permanente.</item>
- <item quantity="one">El código PUK de la tarjeta SIM es incorrecto. Tienes <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que la tarjeta SIM quede inutilizable de manera permanente.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{El código PUK de la tarjeta SIM es incorrecto. Tienes # intento restante antes de que la tarjeta SIM quede inutilizable permanentemente.}other{El código PUK de la tarjeta SIM es incorrecto. Tienes # intentos restantes antes de que la tarjeta SIM quede inutilizable permanentemente.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Se produjo un error al desbloquear la tarjeta SIM con el PIN."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Se produjo un error al desbloquear la tarjeta SIM con el PUK."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Cambiar método de entrada"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"El dispositivo se bloqueó de forma manual"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"No se reconoció"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Activa acceso a cámara en Config. y usa Desb. facial"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Ingresa el PIN de la SIM. Quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos más.</item>
- <item quantity="one">Ingresa el PIN de la SIM. Queda <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que debas comunicarte con tu proveedor para desbloquear el dispositivo.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">Se inhabilitó la SIM. Para continuar, ingresa el código PUK. Te quedan <xliff:g id="_NUMBER_1">%d</xliff:g> intentos más antes de que la SIM quede inutilizable permanentemente. Comunícate con tu proveedor para obtener más detalles.</item>
- <item quantity="one">Se inhabilitó la SIM. Para continuar, ingresa el código PUK. Te queda <xliff:g id="_NUMBER_0">%d</xliff:g> intento más antes de que la SIM quede inutilizable permanentemente. Comunícate con tu proveedor para obtener más detalles.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Ingresa el PIN de la tarjeta SIM. Tienes # intento restante antes de que debas comunicarte con tu operador para desbloquear el dispositivo.}other{Ingresa el PIN de la tarjeta SIM. Tienes # intentos restantes.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Se inhabilitó la tarjeta SIM. Para continuar, ingresa el código PUK. Tienes # intento restante antes de que la SIM quede inutilizable permanentemente. Comunícate con tu operador para conocer más detalles.}other{Se inhabilitó la tarjeta SIM. Para continuar, ingresa el código PUK. Tienes # intentos restantes antes de que la SIM quede inutilizable permanentemente. Comunícate con tu operador para conocer más detalles.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Predeterminado"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Burbuja"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analógico"</string>
diff --git a/packages/SystemUI/res-keyguard/values-es/strings.xml b/packages/SystemUI/res-keyguard/values-es/strings.xml
index b498c0ea8c8f..0c267aafd8fa 100644
--- a/packages/SystemUI/res-keyguard/values-es/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-es/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Has fallado <xliff:g id="NUMBER_0">%1$d</xliff:g> veces al introducir la contraseña. \n\nVuelve a intentarlo dentro de <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Has fallado <xliff:g id="NUMBER_0">%1$d</xliff:g> veces al dibujar el patrón de desbloqueo. \n\nVuelve a intentarlo dentro de <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"El código PIN de la tarjeta SIM es incorrecto. Debes ponerte en contacto con tu operador para desbloquear el dispositivo."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">El código PIN de la tarjeta SIM es incorrecto. Quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos.</item>
- <item quantity="one">El código PIN de la tarjeta SIM es incorrecto. Queda <xliff:g id="NUMBER_0">%d</xliff:g> intento para tener que ponerte en contacto con tu operador para desbloquear el dispositivo.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Código PIN de la SIM incorrecto. Te queda # intento antes de tener que ponerte en contacto con tu operador para desbloquear el dispositivo.}other{Código PIN de la SIM incorrecto. Te quedan # intentos. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"La tarjeta SIM no se puede utilizar. Ponte en contacto con tu operador."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">El código PUK de la tarjeta SIM es incorrecto. Quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos paraa que la tarjeta SIM no se pueda utilizar de forma permanente.</item>
- <item quantity="one">El código PUK de la tarjeta SIM es incorrecto. Queda <xliff:g id="NUMBER_0">%d</xliff:g> intento para que la tarjeta SIM no se pueda utilizar de forma permanente.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Código PUK de la SIM incorrecto. Te queda # intento antes de que la SIM quede inservible permanentemente.}other{Código PUK de la SIM incorrecto. Te quedan # intentos antes de que la SIM quede inservible permanentemente.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"No se ha podido desbloquear la tarjeta SIM con el código PIN."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"No se ha podido desbloquear la tarjeta SIM con el código PUK."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Cambiar método de introducción"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"El dispositivo se ha bloqueado manualmente"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"No se reconoce"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Desbloqueo facial: activa el acceso a la cámara"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Introduce el PIN de la tarjeta SIM. Te quedan <xliff:g id="NUMBER_1">%d</xliff:g> intentos.</item>
- <item quantity="one">Introduce el PIN de la tarjeta SIM. Te queda <xliff:g id="NUMBER_0">%d</xliff:g> intento para tener que ponerte en contacto con tu operador para desbloquear el dispositivo.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">La tarjeta SIM está inhabilitada. Introduce el código PUK para continuar. Te quedan <xliff:g id="_NUMBER_1">%d</xliff:g> intentos para que la tarjeta SIM quede inservible de forma permanente. Ponte en contacto con tu operador para obtener más información.</item>
- <item quantity="one">La tarjeta SIM está inhabilitada. Introduce el código PUK para continuar. Te queda <xliff:g id="_NUMBER_0">%d</xliff:g> intento para que la tarjeta SIM quede inservible de forma permanente. Ponte en contacto con tu operador para obtener más información.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Introduce el PIN de la SIM. Te queda # intento antes de tener que ponerte en contacto con tu operador para desbloquear el dispositivo.}other{Introduce el PIN de la SIM. Te quedan # intentos.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{La SIM se ha inhabilitado. Introduce el código PUK para continuar. Te queda # intento antes de que la SIM quede inservible permanentemente. Ponte en contacto con tu operador para obtener más información.}other{La SIM se ha inhabilitado. Introduce el código PUK para continuar. Te quedan # intentos antes de que la SIM quede inservible permanentemente. Ponte en contacto con tu operador para obtener más información.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Predeterminado"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Burbuja"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analógico"</string>
diff --git a/packages/SystemUI/res-keyguard/values-et/strings.xml b/packages/SystemUI/res-keyguard/values-et/strings.xml
index 74ce06c3a00a..f4c99c41f504 100644
--- a/packages/SystemUI/res-keyguard/values-et/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-et/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Olete parooli <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti sisestanud. \n\nProovige <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundi pärast uuesti."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Olete oma avamismustrit <xliff:g id="NUMBER_0">%1$d</xliff:g> korda valesti joonistanud. \n\nProovige <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundi pärast uuesti."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM-kaardi vale PIN-kood. Seadme avamiseks peate nüüd ühendust võtma oma operaatoriga."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">SIM-kaardi vale PIN-kood. Teil on jäänud veel <xliff:g id="NUMBER_1">%d</xliff:g> katset.</item>
- <item quantity="one">SIM-kaardi vale PIN-kood. Teil on jäänud veel <xliff:g id="NUMBER_0">%d</xliff:g> katse enne, kui peate seadme avamiseks operaatoriga ühendust võtma.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM-i vale PIN-kood. Teil on veel # katse, enne kui peate seadme avamiseks operaatoriga ühendust võtma.}other{SIM-i vale PIN-kood. Teil on veel # katset. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM-kaart on kasutamiskõlbmatu. Võtke ühendust operaatoriga."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">SIM-kaardi vale PUK-kood. Teil on jäänud veel <xliff:g id="NUMBER_1">%d</xliff:g> katset enne, kui SIM-kaart jäädavalt lukustatakse.</item>
- <item quantity="one">SIM-kaardi vale PUK-kood. Teil on jäänud veel <xliff:g id="NUMBER_0">%d</xliff:g> katse enne, kui SIM-kaart jäädavalt lukustatakse.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{SIM-i vale PUK-kood. Teil on veel # katse, enne kui SIM püsivalt lukustatakse.}other{SIM-i vale PUK-kood. Teil on veel # katset, enne kui SIM püsivalt lukustatakse.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM-kaardi PIN-koodi toiming ebaõnnestus."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM-kaardi PUK-koodi toiming ebaõnnestus."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Vaheta sisestusmeetodit"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Seade lukustati käsitsi"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Ei tuvastatud"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Näoga avamise kasutamiseks andke seadetes juurdepääs kaamerale"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Sisestage SIM-kaardi PIN-kood. Jäänud on <xliff:g id="NUMBER_1">%d</xliff:g> katset.</item>
- <item quantity="one">Sisestage SIM-kaardi PIN-kood. Jäänud on <xliff:g id="NUMBER_0">%d</xliff:g> katse enne, kui peate seadme avamiseks ühendust võtma operaatoriga.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM-kaart on nüüd keelatud. Jätkamiseks sisestage PUK-kood. Teil on jäänud veel <xliff:g id="_NUMBER_1">%d</xliff:g> katset enne, kui SIM-kaart püsivalt lukustatakse. Lisateavet küsige operaatorilt.</item>
- <item quantity="one">SIM-kaart on nüüd keelatud. Jätkamiseks sisestage PUK-kood. Teil on jäänud veel <xliff:g id="_NUMBER_0">%d</xliff:g> katse enne, kui SIM-kaart püsivalt lukustatakse. Lisateavet küsige operaatorilt.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Sisestage SIM-i PIN-kood. Teil on veel # katse, enne kui peate seadme avamiseks operaatoriga ühendust võtma.}other{Sisestage SIM-i PIN-kood. Teil on veel # katset.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM on nüüd keelatud. Jätkamiseks sisestage PUK-kood. Teil on veel # katse enne, kui SIM püsivalt lukustatakse. Lisateavet küsige operaatorilt.}other{SIM on nüüd keelatud. Jätkamiseks sisestage PUK-kood. Teil on veel # katset enne, kui SIM püsivalt lukustatakse. Lisateavet küsige operaatorilt.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Vaikenumbrilaud"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Mull"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analoog"</string>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 19fc68a40ca0..bf9491572cf8 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"<xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz idatzi duzu pasahitza, baina huts egin duzu denetan. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%2$d</xliff:g> segundo barru."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"<xliff:g id="NUMBER_0">%1$d</xliff:g> aldiz marraztu duzu desblokeatzeko eredua, baina huts egin duzu denetan. \n\nSaiatu berriro <xliff:g id="NUMBER_1">%2$d</xliff:g> segundo barru."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIMaren PIN kodea ez da zuzena. Gailua desblokeatzeko, operadorearekin jarri beharko duzu harremanetan."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Ez da zuzena SIM txartelaren PIN kodea. <xliff:g id="NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu gailua desblokeatzeko.</item>
- <item quantity="one">Ez da zuzena SIM txartelaren PIN kodea. <xliff:g id="NUMBER_0">%d</xliff:g> saiakera geratzen zaizu gailua desblokeatzeko.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Oker idatzi duzu SIMaren PIN kodea. # saiakera geratzen zaizu gailua desblokeatzeko operadorearekin harremanetan jarri behar izan aurretik.}other{Oker idatzi duzu SIMaren PIN kodea. # saiakera geratzen zaizkizu gailua desblokeatzeko operadorearekin harremanetan jarri behar izan aurretik. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM txartela erabilgaitza da. Jarri operadorearekin harremanetan."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Ez da zuzena SIM txartelaren PUK kodea. <xliff:g id="NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu SIM txartela betiko erabilgaitz geratu aurretik.</item>
- <item quantity="one">Ez da zuzena SIM txartelaren PUK kodea. <xliff:g id="NUMBER_0">%d</xliff:g> saiakera geratzen zaizu SIM txartela betiko erabilgaitz geratu aurretik.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Oker idatzi duzu SIMaren PUK kodea. # saiakera geratzen zaizu SIMa betiko ez-erabilgarri geratu aurretik.}other{Oker idatzi duzu SIMaren PUK kodea. # saiakera geratzen zaizkizu SIMa betiko ez-erabilgarri geratu aurretik.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Huts egin du SIM txartelaren PIN kodearen eragiketak!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Huts egin du SIM txartelaren PUK kodearen eragiketak!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Aldatu idazketa-metodoa"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Eskuz blokeatu da gailua"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Ez da ezagutu"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Aurpegi bidezko desblokeoak kamera atzitzeko baimena behar du"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Idatzi SIMaren PINa. <xliff:g id="NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu.</item>
- <item quantity="one">Idatzi SIMaren PINa. <xliff:g id="NUMBER_0">%d</xliff:g> saiakera geratzen zaizu; oker idatziz gero, operadoreari eskatu beharko diozu gailua desblokeatzeko.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">Desgaitu egin da SIM txartela. Aurrera egiteko, idatzi PUK kodea. <xliff:g id="_NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu SIM txartela betiko erabilgaitz geratu aurretik. Xehetasunak lortzeko, jarri operadorearekin harremanetan.</item>
- <item quantity="one">Desgaitu egin da SIM txartela. Aurrera egiteko, idatzi PUK kodea. <xliff:g id="_NUMBER_0">%d</xliff:g> saiakera geratzen zaizu SIM txartela betiko erabilgaitz geratu aurretik. Xehetasunak lortzeko, jarri operadorearekin harremanetan.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Idatzi SIMaren PINa. # saiakera geratzen zaizu gailua desblokeatzeko operadorearekin harremanetan jarri behar izan aurretik.}other{Idatzi SIMaren PINa. # saiakera gelditzen zaizkizu.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Orain, SIMa desgaituta dago. Aurrera egiteko, idatzi PUK kodea. # saiakera geratzen zaizu SIMa betiko ez-erabilgarri geratu aurretik. Xehetasunak lortzeko, jarri operadorearekin harremanetan.}other{Orain, SIMa desgaituta dago. Aurrera egiteko, idatzi PUK kodea. # saiakera geratzen zaizkizu SIMa betiko ez-erabilgarri geratu aurretik. Xehetasunak lortzeko, jarri operadorearekin harremanetan.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Lehenetsia"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Puxikak"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogikoa"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index 6b90e8936199..ca222275aabd 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"گذرواژه خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه تایپ کردید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"الگوی بازگشایی قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیده‌اید. \n\nلطفاً پس‌از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"کد پین سیم‌کارت اشتباه است، اکنون برای باز کردن قفل دستگاهتان باید با شرکت مخابراتی تماس بگیرید."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">کد پین سیم‌کارت اشتباه است، <xliff:g id="NUMBER_1">%d</xliff:g> بار دیگر می‌توانید تلاش کنید.</item>
- <item quantity="other">کد پین سیم‌کارت اشتباه است، <xliff:g id="NUMBER_1">%d</xliff:g> بار دیگر می‌توانید تلاش کنید.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{کد پین سیم‌کارت اشتباه است. # فرصت دیگر باقی مانده است و پس‌از آن برای باز کردن قفل دستگاه باید با شرکت مخابراتی‌تان تماس بگیرید.}one{کد پین سیم‌کارت اشتباه است، # فرصت دیگر دارید. }other{کد پین سیم‌کارت اشتباه است، # فرصت دیگر دارید. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"سیم‌کارت غیرقابل استفاده است. با شرکت مخابراتی‌تان تماس بگیرید."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">‏کد PUK سیم‌کارت اشتباه است، <xliff:g id="NUMBER_1">%d</xliff:g> بار دیگر می‌توانید تلاش کنید و پس از آن سیم‌کارت برای همیشه غیرقابل استفاده می‌شود.</item>
- <item quantity="other">‏کد PUK سیم‌کارت اشتباه است، <xliff:g id="NUMBER_1">%d</xliff:g> بار دیگر می‌توانید تلاش کنید و پس از آن سیم‌کارت برای همیشه غیرقابل استفاده می‌شود.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{‏کد PUK سیم‌کارت اشتباه است، # فرصت دیگر باقی مانده است و پس‌از آن سیم‌کارت برای همیشه غیرقابل‌استفاده می‌شود.}one{‏کد PUK سیم‌کارت اشتباه است، # فرصت دیگر باقی مانده است و پس‌از آن سیم‌کارت برای همیشه غیرقابل‌استفاده می‌شود.}other{‏کد PUK سیم‌کارت اشتباه است، # فرصت دیگر باقی مانده است و پس‌از آن سیم‌کارت برای همیشه غیرقابل‌استفاده می‌شود.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"عملیات پین سیم‌کارت ناموفق بود!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"‏عملیات PUK سیم‌کارت ناموفق بود!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"تغییر روش ورودی"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"دستگاه به‌صورت دستی قفل شده است"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"شناسایی نشد"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"برای استفاده از قفل‌گشایی با چهره، دسترسی دوربین را در تنظیمات روشن کنید"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">پین سیم‌کارت را وارد کنید. <xliff:g id="NUMBER_1">%d</xliff:g> تلاش دیگری باقی مانده است.</item>
- <item quantity="other">پین سیم‌کارت را وارد کنید. <xliff:g id="NUMBER_1">%d</xliff:g> تلاش دیگری باقی مانده است.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">‏سیم‌کارت اکنون غیرفعال است. برای ادامه دادن کد PUK را وارد کنید. <xliff:g id="_NUMBER_1">%d</xliff:g> تلاش دیگر باقی مانده است و پس از آن سیم‌کارت برای همیشه غیرقابل‌استفاده می‌شود. برای اطلاع از جزئیات با شرکت مخابراتی تماس بگیرید.</item>
- <item quantity="other">‏سیم‌کارت اکنون غیرفعال است. برای ادامه دادن کد PUK را وارد کنید. <xliff:g id="_NUMBER_1">%d</xliff:g> تلاش دیگر باقی مانده است و پس از آن سیم‌کارت برای همیشه غیرقابل‌استفاده می‌شود. برای اطلاع از جزئیات با شرکت مخابراتی تماس بگیرید.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{پین سیم‌کارت را وارد کنید. # فرصت دیگر باقی مانده است و پس‌از آن برای باز کردن قفل دستگاه باید با شرکت مخابراتی‌تان تماس بگیرید.}one{پین سیم‌کارت را وارد کنید. # فرصت دیگر باقی مانده است.}other{پین سیم‌کارت را وارد کنید. # فرصت دیگر باقی مانده است.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{‏سیم‌کارت اکنون غیرفعال است. برای ادامه دادن، کد PUK را وارد کنید. # فرصت دیگر باقی مانده است و پس‌از آن سیم‌کارت برای همیشه غیرقابل‌استفاده می‌شود. برای اطلاع از جزئیات، با شرکت مخابراتی تماس بگیرید.}one{‏سیم‌کارت اکنون غیرفعال است. برای ادامه دادن، کد PUK را وارد کنید. # فرصت دیگر باقی مانده است و پس‌از آن سیم‌کارت برای همیشه غیرقابل‌استفاده می‌شود. برای اطلاع از جزئیات، با شرکت مخابراتی تماس بگیرید.}other{‏سیم‌کارت اکنون غیرفعال است. برای ادامه دادن، کد PUK را وارد کنید. # فرصت دیگر باقی مانده است و پس‌از آن سیم‌کارت برای همیشه غیرقابل‌استفاده می‌شود. برای اطلاع از جزئیات، با شرکت مخابراتی تماس بگیرید.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"پیش‌فرض"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"حباب"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"آنالوگ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fi/strings.xml b/packages/SystemUI/res-keyguard/values-fi/strings.xml
index f8aa5de1f268..da74b9a14e51 100644
--- a/packages/SystemUI/res-keyguard/values-fi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fi/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Olet kirjoittanut salasanan väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. \n\nYritä uudelleen <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunnin kuluttua."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Olet piirtänyt lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%1$d</xliff:g> kertaa. \n\nYritä uudelleen <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunnin kuluttua."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Virheellinen SIM-kortin PIN-koodi. Sinun on nyt otettava yhteys operaattoriin laitteen lukituksen avaamiseksi."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Virheellinen SIM-kortin PIN-koodi. Sinulla on <xliff:g id="NUMBER_1">%d</xliff:g> yritystä jäljellä.</item>
- <item quantity="one">Virheellinen SIM-kortin PIN-koodi. Sinulla on <xliff:g id="NUMBER_0">%d</xliff:g> yritys jäljellä, ennen kuin sinun on otettava yhteys operaattoriin laitteen lukituksen avaamiseksi.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Virheellinen SIM-kortin PIN-koodi. Sinulla on # yritys jäljellä, ennen kuin sinun on otettava yhteys operaattoriin laitteen lukituksen avaamiseksi.}other{Virheellinen SIM-kortin PIN-koodi. Sinulla on # yritystä jäljellä. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM-korttia ei voi käyttää. Ota yhteys operaattoriin."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Virheellinen SIM-kortin PUK-koodi. Sinulla on <xliff:g id="NUMBER_1">%d</xliff:g> yritystä jäljellä, ennen kuin SIM-kortista tulee pysyvästi käyttökelvoton.</item>
- <item quantity="one">Virheellinen SIM-kortin PUK-koodi. Sinulla on <xliff:g id="NUMBER_0">%d</xliff:g> yritys jäljellä, ennen kuin SIM-kortista tulee pysyvästi käyttökelvoton.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Virheellinen SIM-kortin PUK-koodi. Sinulla on # yritys jäljellä, ennen kuin SIM-kortti poistuu käytöstä pysyvästi.}other{Virheellinen SIM-kortin PUK-koodi. Sinulla on # yritystä jäljellä, ennen kuin SIM-kortti poistuu käytöstä pysyvästi.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM-kortin PIN-toiminto epäonnistui."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM-kortin PUK-toiminto epäonnistui."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Vaihda syöttötapaa."</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Laite lukittiin manuaalisesti"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Ei tunnistettu"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Kasvojentunnistusavaus: Asetukset &gt; pääsy kameraan"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Anna SIM-kortin PIN-koodi. Sinulla on <xliff:g id="NUMBER_1">%d</xliff:g> yritystä jäljellä.</item>
- <item quantity="one">Anna SIM-kortin PIN-koodi. <xliff:g id="NUMBER_0">%d</xliff:g> yrityksen jälkeen laite lukittuu, ja vain operaattori voi avata sen.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM-kortti on nyt lukittu. Anna PUK-koodi, niin voit jatkaa. Sinulla on <xliff:g id="_NUMBER_1">%d</xliff:g> yritystä jäljellä, ennen kuin SIM-kortti poistuu pysyvästi käytöstä. Pyydä lisätietoja operaattoriltasi.</item>
- <item quantity="one">SIM-kortti on nyt lukittu. Anna PUK-koodi, niin voit jatkaa. Sinulla on <xliff:g id="_NUMBER_0">%d</xliff:g> yritys jäljellä, ennen kuin SIM-kortti poistuu pysyvästi käytöstä. Pyydä lisätietoja operaattoriltasi.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Lisää SIM-kortin PIN-koodi. Sinulla on # yritys jäljellä, ennen kuin sinun on otettava yhteys operaattoriin laitteen lukituksen avaamiseksi.}other{Lisää SIM-kortin PIN-koodi. # yritystä jäljellä.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM-kortti on nyt lukittu. Lisää PUK-koodi, niin voit jatkaa. Sinulla on # yritys jäljellä, ennen kuin SIM-kortti poistuu käytöstä pysyvästi. Pyydä lisätietoa operaattoriltasi.}other{SIM-kortti on nyt lukittu. Lisää PUK-koodi, niin voit jatkaa. Sinulla on # yritystä jäljellä, ennen kuin SIM-kortti poistuu käytöstä pysyvästi. Pyydä lisätietoa operaattoriltasi.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Oletus"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Kupla"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analoginen"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index 0e9800707cc8..b812503213fc 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Vous avez entré un mot de passe incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises.\n\nVeuillez réessayer dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"NIP de carte SIM incorrect. Vous devez maintenant communiquer avec votre fournisseur de services pour déverrouiller votre appareil."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Le NIP de la carte SIM incorrect. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative.</item>
- <item quantity="other">Le NIP de la carte SIM incorrect. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{NIP de la carte SIM incorrect. Il vous reste # tentative. Après cela, vous devrez communiquer avec votre fournisseur de services pour déverrouiller votre appareil.}one{NIP de la carte SIM incorrect. Il vous reste # tentative. }other{NIP de la carte SIM incorrect. Il vous reste # tentatives. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"La carte SIM est inutilisable. Communiquez avec votre fournisseur de services."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Le code PUK de la carte SIM est incorrect. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM devienne définitivement inutilisable.</item>
- <item quantity="other">Le code PUK de la carte SIM est incorrect. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM devienne définitivement inutilisable.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Code PUK de la carte SIM incorrect. Il vous reste # tentative avant que votre carte SIM devienne définitivement inutilisable.}one{Code PUK de la carte SIM incorrect. Il vous reste # tentative avant que votre carte SIM devienne définitivement inutilisable.}other{Code PUK de la carte SIM incorrect. Il vous reste # tentatives avant que votre carte SIM devienne définitivement inutilisable.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Le déverrouillage par NIP de la carte SIM a échoué."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Le déverrouillage de la carte SIM par code PUK a échoué."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Changer de méthode d\'entrée"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"L\'appareil a été verrouillé manuellement"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Doigt non reconnu"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Déverr. rec. faciale : activez accès app. photo dans Param."</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Entrez le NIP de votre carte SIM. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative.</item>
- <item quantity="other">Entrez le NIP de votre carte SIM. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">La carte SIM est maintenant désactivée. Entrez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM devienne définitivement inutilisable. Pour obtenir plus de détails, communiquez avec votre fournisseur de services.</item>
- <item quantity="other">La carte SIM est maintenant désactivée. Entrez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM devienne définitivement inutilisable. Pour obtenir plus de détails, communiquez avec votre fournisseur de services.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Entrez le NIP de la carte SIM. Il vous reste # tentative. Après cela, vous devrez communiquer avec votre fournisseur de services pour déverrouiller votre appareil.}one{Entrez le NIP de la carte SIM. Il vous reste # tentative.}other{Entrez le NIP de la carte SIM. Il vous reste # tentatives.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{La carte SIM est maintenant désactivée. Entrez le code PUK pour continuer. Il vous reste # tentative avant que votre carte SIM devienne définitivement inutilisable. Communiquez avec votre fournisseur de services pour en savoir plus.}one{La carte SIM est maintenant désactivée. Entrez le code PUK pour continuer. Il vous reste # tentative avant que votre carte SIM devienne définitivement inutilisable. Communiquez avec votre fournisseur de services pour en savoir plus.}other{La carte SIM est maintenant désactivée. Entrez le code PUK pour continuer. Il vous reste # tentatives avant que votre carte SIM devienne définitivement inutilisable. Communiquez avec votre fournisseur de services pour en savoir plus.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Par défaut"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bulle"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogique"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr/strings.xml b/packages/SystemUI/res-keyguard/values-fr/strings.xml
index cf79eab5155c..0b21a406fc8a 100644
--- a/packages/SystemUI/res-keyguard/values-fr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Vous avez saisi un mot de passe incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises.\n\nRéessayez dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%1$d</xliff:g> reprises.\n\nRéessayez dans <xliff:g id="NUMBER_1">%2$d</xliff:g> secondes."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Code PIN de la carte SIM incorrect. Vous devez désormais contacter votre opérateur pour déverrouiller votre appareil."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Code PIN de la carte SIM incorrect. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative.</item>
- <item quantity="other">Code PIN de la carte SIM incorrect. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Code PIN SIM incorrect. Il vous reste # tentative avant de devoir contacter votre opérateur pour déverrouiller l\'appareil.}one{Code PIN SIM incorrect. Il vous reste # tentative. }other{Code PIN SIM incorrect. Il vous reste # tentatives. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"La carte SIM est inutilisable. Contactez votre opérateur."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Clé PUK de la carte SIM incorrecte. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM ne devienne définitivement inutilisable.</item>
- <item quantity="other">Clé PUK de la carte SIM incorrecte. Il vous reste <xliff:g id="NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM ne devienne définitivement inutilisable.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Clé PUK de la SIM incorrecte. Il vous reste # tentative avant que la SIM ne devienne définitivement inutilisable.}one{Clé PUK de la SIM incorrecte. Il vous reste # tentative avant que la carte SIM ne devienne définitivement inutilisable.}other{Clé PUK de la SIM incorrecte. Il vous reste # tentatives avant que la carte SIM ne devienne définitivement inutilisable.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Échec du déverrouillage à l\'aide du code PIN de la carte SIM."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Échec du déverrouillage à l\'aide de la clé PUK de la carte SIM."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Changer le mode de saisie"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Appareil verrouillé manuellement"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Non reconnu"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Pour le déverrouillage par reconnaissance faciale, activez l\'accès à l\'app. photo dans Paramètres"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Saisissez le code de la carte SIM. <xliff:g id="NUMBER_1">%d</xliff:g> tentative restante.</item>
- <item quantity="other">Saisissez le code de la carte SIM. <xliff:g id="NUMBER_1">%d</xliff:g> tentatives restantes.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">La carte SIM est maintenant désactivée. Saisissez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentative avant que votre carte SIM ne devienne définitivement inutilisable. Pour de plus amples informations, veuillez contacter votre opérateur.</item>
- <item quantity="other">La carte SIM est maintenant désactivée. Saisissez le code PUK pour continuer. Il vous reste <xliff:g id="_NUMBER_1">%d</xliff:g> tentatives avant que votre carte SIM ne devienne définitivement inutilisable. Pour de plus amples informations, veuillez contacter votre opérateur.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Saisissez le code PIN SIM. Il vous reste # tentative avant de devoir contacter votre opérateur pour déverrouiller l\'appareil.}one{Saisissez le code PIN SIM. Il vous reste # tentative.}other{Saisissez le code PIN SIM. Il vous reste # tentatives.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{La SIM est maintenant désactivée. Saisissez la clé PUK pour continuer. Il vous reste # tentative avant que la SIM ne devienne définitivement inutilisable. Pour plus d\'infos, contactez votre opérateur.}one{La SIM est maintenant désactivée. Saisissez la clé PUK pour continuer. Il vous reste # tentative avant que la SIM ne devienne définitivement inutilisable. Pour plus d\'infos, contactez votre opérateur.}other{La SIM est maintenant désactivée. Saisissez la clé PUK pour continuer. Il vous reste # tentatives avant que la SIM ne devienne définitivement inutilisable. Pour plus d\'infos, contactez votre opérateur.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Par défaut"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bulle"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogique"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index 819e6327b4ce..18b0fd57ab3c 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Introduciches o contrasinal incorrectamente <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Debuxaches incorrectamente o padrón de desbloqueo <xliff:g id="NUMBER_0">%1$d</xliff:g> veces. \n\nTéntao de novo en <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"O código PIN da SIM non é correcto. Agora debes contactar co operador para desbloquear o dispositivo."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">O código PIN da SIM é incorrecto. Quédanche <xliff:g id="NUMBER_1">%d</xliff:g> intentos.</item>
- <item quantity="one">O código PIN da SIM é incorrecto. Quédache <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que teñas que contactar co operador para desbloquear o dispositivo.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{O código PIN da SIM é incorrecto. Quédache # intento antes de que teñas que contactar co operador para desbloquear o dispositivo.}other{O código PIN da SIM é incorrecto. Quédanche # intentos. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"A SIM está inutilizable. Contacta co operador."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">O código PUK da SIM é incorrecto. Quédanche <xliff:g id="NUMBER_1">%d</xliff:g> intentos antes de que a SIM quede inutilizable para sempre.</item>
- <item quantity="one">O código PUK da SIM non é correcto. Quédache <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que a SIM quede inutilizable para sempre.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{O código PUK da SIM é incorrecto. Quédache # intento antes de que a SIM quede inutilizable para sempre.}other{O código PUK da SIM é incorrecto. Quédanche # intentos antes de que a SIM quede inutilizable para sempre.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Produciuse un erro no funcionamento do PIN da SIM"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Produciuse un erro ao tentar desbloquear a tarxeta SIM co código PUK."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Cambia o método de introdución"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"O dispositivo bloqueouse manualmente"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Non se recoñeceu"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Desbloqueo facial: acceso á cámara en Configuración"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Introduce o código PIN da SIM. Quédanche <xliff:g id="NUMBER_1">%d</xliff:g> intentos.</item>
- <item quantity="one">Introduce o código PIN da SIM. Quédache <xliff:g id="NUMBER_0">%d</xliff:g> intento antes de que teñas que contactar co operador para desbloquear o dispositivo.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">A SIM está desactivada. Introduce o código PUK para continuar. Quédanche <xliff:g id="_NUMBER_1">%d</xliff:g> intentos antes de que a SIM quede inutilizable para sempre. Contacta co operador para obter información.</item>
- <item quantity="one">A SIM está desactivada. Introduce o código PUK para continuar. Quédache <xliff:g id="_NUMBER_0">%d</xliff:g> intento antes de que a SIM quede inutilizable para sempre. Contacta co operador para obter información.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Mete o PIN da SIM. Quédache # intento antes de que teñas que contactar co operador para desbloquear o dispositivo.}other{Mete o PIN da SIM. Quédanche # intentos.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{A SIM está desactivada. Mete o código PUK para continuar. Quédache # intento antes de que a SIM quede inutilizable para sempre. Contacta co operador para obter información.}other{A SIM está desactivada. Mete o código PUK para continuar. Quédanche # intentos antes de que a SIM quede inutilizable para sempre. Contacta co operador para obter información.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Predeterminado"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Burbulla"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analóxico"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index 6142aa15049e..4dd994c45af4 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"તમારો પાસવર્ડ તમે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે લખ્યો છે. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> સેકંડમાં ફરીથી પ્રયાસ કરો."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"તમારી અનલૉક પૅટર્ન તમે <xliff:g id="NUMBER_0">%1$d</xliff:g> વખત ખોટી રીતે દોરી છે. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> સેકન્ડમાં ફરીથી પ્રયાસ કરો."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"ખોટો સિમ પિન કોડ, તમારે હવે તમારું ઉપકરણ અનલૉક કરવા માટે તમારા કૅરીઅરનો સંપર્ક કરવો આવશ્યક છે."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">ખોટો સિમ પિન કોડ, તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસ બાકી છે.</item>
- <item quantity="other">ખોટો સિમ પિન કોડ, તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસ બાકી છે.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{સિમ કાર્ડનો ખોટો પિન કોડ, તમને તમારું ડિવાઇસ અનલૉક કરવા માટે તમારા મોબાઇલ ઑપરેટરનો સંપર્ક કરવાની જરૂર પડે, તે પહેલાં તમારી પાસે # પ્રયાસ બાકી છે.}one{સિમ કાર્ડનો ખોટો પિન કોડ, તમારી પાસે # પ્રયાસ બાકી છે. }other{સિમ કાર્ડનો ખોટો પિન કોડ, તમારી પાસે # પ્રયાસ બાકી છે. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"સિમ અનુપયોગી છે. તમારા કૅરિઅરનો સંપર્ક કરો."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">ખોટો સિમ PUK કોડ, સિમ કાયમી રૂપે અનુપયોગી બની જાય તે પહેલા તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસ બાકી છે.</item>
- <item quantity="other">ખોટો સિમ PUK કોડ, સિમ કાયમી રૂપે અનુપયોગી બની જાય તે પહેલા તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસ બાકી છે.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{સિમ કાર્ડનો ખોટો PUK કોડ, સિમ કાર્ડ કાયમ માટે બિનઉપયોગી બની જાય, એ પહેલાં તમારી પાસે # પ્રયાસ બાકી છે.}one{સિમ કાર્ડનો ખોટો PUK કોડ, સિમ કાર્ડ કાયમ માટે બિનઉપયોગી બની જાય, એ પહેલાં તમારી પાસે # પ્રયાસ બાકી છે.}other{સિમ કાર્ડનો ખોટો PUK કોડ, સિમ કાર્ડ કાયમ માટે બિનઉપયોગી બની જાય, એ પહેલાં તમારી પાસે # પ્રયાસ બાકી છે.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"સિમ પિન ઑપરેશન નિષ્ફળ થયું!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"સિમ PUK ઓપરેશન નિષ્ફળ થયું!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ઇનપુટ પદ્ધતિ સ્વિચ કરો"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ઉપકરણ મેન્યુઅલી લૉક કર્યું હતું"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"ઓળખાયેલ નથી"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"ફેસ અનલૉક વાપરવા સેટિંગમાં કૅમેરા ઍક્સેસ ચાલુ કરો"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">સિમનો પિન દાખલ કરો, તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસ બાકી છે.</item>
- <item quantity="other">સિમનો પિન દાખલ કરો, તમારી પાસે <xliff:g id="NUMBER_1">%d</xliff:g> પ્રયાસો બાકી છે.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">સિમ હવે બંધ કરેલ છે. ચાલુ રાખવા માટે PUK કોડ દાખલ કરો. સિમ કાયમીરૂપે બિનઉપયોગી બની જાય એ પહેલાં તમારી પાસે <xliff:g id="_NUMBER_1">%d</xliff:g> પ્રયાસ બાકી છે. વિગતો માટે કૅરિઅરનો સંપર્ક કરો.</item>
- <item quantity="other">સિમ હવે બંધ કરેલ છે. ચાલુ રાખવા માટે PUK કોડ દાખલ કરો. સિમ કાયમીરૂપે બિનઉપયોગી બની જાય એ પહેલાં તમારી પાસે <xliff:g id="_NUMBER_1">%d</xliff:g> પ્રયાસો બાકી છે. વિગતો માટે કૅરિઅરનો સંપર્ક કરો.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{સિમ કાર્ડનો પિન દાખલ કરો. તમને તમારું ડિવાઇસ અનલૉક કરવા માટે તમારા મોબાઇલ ઑપરેટરનો સંપર્ક કરવાની જરૂર પડે, તે પહેલાં તમારી પાસે # પ્રયાસ બાકી છે.}one{સિમ કાર્ડનો પિન દાખલ કરો. તમારી પાસે # પ્રયાસ બાકી છે.}other{સિમ કાર્ડનો પિન દાખલ કરો. તમારી પાસે # પ્રયાસ બાકી છે.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{સિમ કાર્ડ બંધ કરેલું છે. ચાલુ રાખવા માટે PUK કોડ દાખલ કરો. સિમ કાર્ડ કાયમ માટે બિનઉપયોગી બની જાય, એ પહેલાં તમારી પાસે # પ્રયાસ બાકી છે. વિગતો માટે મોબાઇલ ઑપરેટરનો સંપર્ક કરો.}one{સિમ કાર્ડ બંધ કરેલું છે. ચાલુ રાખવા માટે PUK કોડ દાખલ કરો. સિમ કાર્ડ કાયમ માટે બિનઉપયોગી બની જાય, એ પહેલાં તમારી પાસે # પ્રયાસ બાકી છે. વિગતો માટે મોબાઇલ ઑપરેટરનો સંપર્ક કરો.}other{સિમ કાર્ડ બંધ કરેલું છે. ચાલુ રાખવા માટે PUK કોડ દાખલ કરો. સિમ કાર્ડ કાયમ માટે બિનઉપયોગી બની જાય, એ પહેલાં તમારી પાસે # પ્રયાસ બાકી છે. વિગતો માટે મોબાઇલ ઑપરેટરનો સંપર્ક કરો.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"ડિફૉલ્ટ"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"બબલ"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"એનાલોગ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml
index 742493aa1887..44a9c0e4993e 100644
--- a/packages/SystemUI/res-keyguard/values-hi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"आप अपना पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से लिख चुके हैं. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में फिर से कोशिश करें."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"आपने अपने लॉक खोलने के पैटर्न को <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से ड्रॉ किया है. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में फिर से कोशिश करें."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"गलत SIM पिन कोड, अपने डिवाइस को अनलॉक करने के लिए अब आपको अपनी मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करना होगा."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">गलत सिम पिन कोड, आप <xliff:g id="NUMBER_1">%d</xliff:g> बार और कोशिश कर सकते हैं.</item>
- <item quantity="other">गलत सिम पिन कोड, आप <xliff:g id="NUMBER_1">%d</xliff:g> बार और कोशिश कर सकते हैं.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{आपने सिम का गलत पिन कोड डाला है. आपके पास # मौका बचा है. अगर फिर भी डिवाइस अनलॉक नहीं होता है, तो आपको मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करना होगा.}one{आपने सिम का गलत पिन कोड डाला है. आपके पास # मौका बचा है. }other{आपने सिम का गलत पिन कोड डाला है. आपके पास # मौके बचे हैं. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM बेकार हो गया है. अपनी मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करें."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">SIM PUK कोड गलत है, SIM के हमेशा के लिए बेकार हो जाने से पहले आप <xliff:g id="NUMBER_1">%d</xliff:g> बार और कोशिश कर सकते हैं.</item>
- <item quantity="other">SIM PUK कोड गलत है, SIM के हमेशा के लिए बेकार हो जाने से पहले आप <xliff:g id="NUMBER_1">%d</xliff:g> बार और कोशिश कर सकते हैं.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{आपने सिम का गलत PUK कोड डाला है. आपके पास # मौका बचा है. इसके बाद, सिम हमेशा के लिए बंद हो जाएगा.}one{आपने सिम का गलत PUK कोड डाला है. आपके पास # मौका बचा है. इसके बाद, सिम हमेशा के लिए बंद हो जाएगा.}other{आपने सिम का गलत PUK कोड डाला है. आपके पास # मौके बचे हैं. इसके बाद, सिम हमेशा के लिए बंद हो जाएगा.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM पिन की कार्यवाही विफल रही!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUK की कार्यवाही विफल रही!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"इनपुट का तरीका बदलें"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"डिवाइस को मैन्युअल रूप से लॉक किया गया था"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"पहचान नहीं हो पाई"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"\'फ़ेस अनलॉक\' इस्तेमाल करने के लिए, सेटिंग में जाकर कैमरे का ऐक्सेस चालू करें"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">सिम का पिन डालें. आपके पास <xliff:g id="NUMBER_1">%d</xliff:g> मौके बचे हैं.</item>
- <item quantity="other">सिम का पिन डालें. आपके पास <xliff:g id="NUMBER_1">%d</xliff:g> मौके बचे हैं.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">सिम बंद कर दिया गया है. जारी रखने के लिए PUK कोड डालें. आपके पास <xliff:g id="_NUMBER_1">%d</xliff:g> मौके बचे हैं, उसके बाद, सिम हमेशा के लिए काम करना बंद कर देगा. जानकारी के लिए, मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करें.</item>
- <item quantity="other">सिम बंद कर दिया गया है. जारी रखने के लिए PUK कोड डालें. आपके पास <xliff:g id="_NUMBER_1">%d</xliff:g> मौके बचे हैं, उसके बाद, सिम हमेशा के लिए काम करना बंद कर देगा. जानकारी के लिए, मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करें.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{सिम का पिन डालें. आपके पास # मौका बचा है. अगर फिर भी डिवाइस अनलॉक नहीं होता है, तो आपको मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करना होगा.}one{सिम का पिन डालें. आपके पास # मौका बचा है.}other{सिम का पिन डालें. आपके पास # मौके बचे हैं.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{सिम बंद कर दिया गया है. जारी रखने के लिए PUK कोड डालें. आपके पास # मौका बचा है. इसके बाद, सिम हमेशा के लिए बंद हो जाएगा. ज़्यादा जानकारी के लिए, मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करें.}one{सिम बंद कर दिया गया है. जारी रखने के लिए PUK कोड डालें. आपके पास # मौका बचा है. इसके बाद, सिम हमेशा के लिए बंद हो जाएगा. ज़्यादा जानकारी के लिए, मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करें.}other{सिम बंद कर दिया गया है. जारी रखने के लिए PUK कोड डालें. आपके पास # मौके बचे हैं. इसके बाद, सिम हमेशा के लिए बंद हो जाएगा. ज़्यादा जानकारी के लिए, मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करें.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"डिफ़ॉल्ट"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"बबल"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"एनालॉग"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml
index 292bfd18637b..299d8115a339 100644
--- a/packages/SystemUI/res-keyguard/values-hr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml
@@ -68,17 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Netočno ste unijeli zaporku <xliff:g id="NUMBER_0">%1$d</xliff:g> put/a. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Netočno ste iscrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> put/a. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Netočan PIN kôd SIM kartice; sada morate kontaktirati svog mobilnog operatera da bi otključao vaš uređaj."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">PIN kôd SIM-a nije točan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
- <item quantity="few">PIN kôd SIM-a nije točan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
- <item quantity="other">PIN kôd SIM-a nije točan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{PIN kôd SIM-a nije točan. Imate još # pokušaj, a zatim ćete morati kontaktirati mobilnog operatera da bi otključao uređaj.}one{PIN kôd SIM-a nije točan. Imate još # pokušaj. }few{PIN kôd SIM-a nije točan. Imate još # pokušaja. }other{PIN kôd SIM-a nije točan. Imate još # pokušaja. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM kartica nije upotrebljiva. Kontaktirajte svog mobilnog operatera."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">PUK kôd SIM-a nije točan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj, a ako ne uspijete, SIM će postati trajno neupotrebljiv.</item>
- <item quantity="few">PUK kôd SIM-a nije točan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja, a ako ne uspijete, SIM će postati trajno neupotrebljiv.</item>
- <item quantity="other">PUK kôd SIM-a nije točan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja, a ako ne uspijete, SIM će postati trajno neupotrebljiv.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{PUK kôd SIM-a nije točan. Imate još # pokušaj prije nego što SIM kartica postane trajno neupotrebljiva.}one{PUK kôd SIM-a nije točan. Imate još # pokušaj prije nego što SIM kartica postane trajno neupotrebljiva.}few{PUK kôd SIM-a nije točan. Imate još # pokušaja prije nego što SIM kartica postane trajno neupotrebljiva.}other{PUK kôd SIM-a nije točan. Imate još # pokušaja prije nego što SIM kartica postane trajno neupotrebljiva.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Operacija PIN-a SIM kartice nije uspjela!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Operacija PUK-a SIM kartice nije uspjela!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Promjena načina unosa"</string>
@@ -93,16 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Uređaj je ručno zaključan"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nije prepoznato"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Za otključavanje licem uključite pristup kameri"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
- <item quantity="few">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
- <item quantity="other">Unesite PIN za SIM. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaj prije nego što SIM kartica postane trajno neupotrebljiva. Više informacija zatražite od mobilnog operatera.</item>
- <item quantity="few">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Više informacija zatražite od mobilnog operatera.</item>
- <item quantity="other">SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još <xliff:g id="_NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Više informacija zatražite od mobilnog operatera.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Unesite PIN za SIM. Imate još # pokušaj, a zatim ćete morati kontaktirati mobilnog operatera da bi otključao uređaj.}one{Unesite PIN za SIM. Imate još # pokušaj.}few{Unesite PIN za SIM. Imate još # pokušaja.}other{Unesite PIN za SIM. Imate još # pokušaja.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još # pokušaj prije nego što SIM kartica postane trajno neupotrebljiva. Više informacija zatražite od mobilnog operatera.}one{SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još # pokušaj prije nego što SIM kartica postane trajno neupotrebljiva. Više informacija zatražite od mobilnog operatera.}few{SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još # pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Više informacija zatražite od mobilnog operatera.}other{SIM je sada onemogućen. Unesite PUK kôd da biste nastavili. Imate još # pokušaja prije nego što SIM kartica postane trajno neupotrebljiva. Više informacija zatražite od mobilnog operatera.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Zadano"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Mjehurić"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogni"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hu/strings.xml b/packages/SystemUI/res-keyguard/values-hu/strings.xml
index b647f168ea0a..6201e95c03ec 100644
--- a/packages/SystemUI/res-keyguard/values-hu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hu/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"<xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal helytelenül adta meg a jelszót.\n\nPróbálja újra <xliff:g id="NUMBER_1">%2$d</xliff:g> másodperc múlva."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"<xliff:g id="NUMBER_0">%1$d</xliff:g> alkalommal rosszul rajzolta le a feloldási mintát.\n\nPróbálja újra <xliff:g id="NUMBER_1">%2$d</xliff:g> másodperc múlva."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Helytelen PIN-kód a SIM-kártyához. Az eszköz feloldása érdekében, kérjük, vegye fel a kapcsolatot szolgáltatójával."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Helytelen PIN-kód a SIM-kártyához. Még <xliff:g id="NUMBER_1">%d</xliff:g> próbálkozása maradt.</item>
- <item quantity="one">Helytelen PIN-kód a SIM-kártyához. Még <xliff:g id="NUMBER_0">%d</xliff:g> próbálkozása maradt, az eszköz feloldásához azt követően fel kell vennie a kapcsolatot szolgáltatójával.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Helytelen PIN-kód a SIM-kártyához; még # próbálkozása van, mielőtt fel kell vennie a kapcsolatot szolgáltatójával az eszköz feloldásához.}other{Helytelen PIN-kód a SIM-kártyához; még # próbálkozása van. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"A SIM-kártya használhatatlan. Vegye fel a kapcsolatot szolgáltatójával."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Helytelen PUK-kód a SIM-kártyához. Még <xliff:g id="NUMBER_1">%d</xliff:g> próbálkozása maradt, mielőtt a SIM-kártya végleg használhatatlanná válik.</item>
- <item quantity="one">Helytelen PUK-kód a SIM-kártyához. Még <xliff:g id="NUMBER_0">%d</xliff:g> próbálkozása maradt, mielőtt a SIM-kártya végleg használhatatlanná válik.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Helytelen PUK-kód a SIM-kártyához. Még # próbálkozása maradt, mielőtt a SIM-kártya végleg használhatatlanná válik.}other{Helytelen PUK-kód a SIM-kártyához; még # próbálkozása van, mielőtt a SIM-kártya végleg használhatatlan lesz.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"A SIM-kártya PIN-művelete sikertelen!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"A SIM-kártya PUK-művelete sikertelen!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Beviteli módszer váltása"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Az eszközt manuálisan lezárták"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nem ismerhető fel"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Arcalapú feloldáshoz Hozzáférés a kamerához szükséges"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Adja meg a SIM-kártya PIN-kódját. <xliff:g id="NUMBER_1">%d</xliff:g> próbálkozása maradt.</item>
- <item quantity="one">Adja meg a SIM-kártya PIN-kódját. <xliff:g id="NUMBER_0">%d</xliff:g> próbálkozása maradt. Ha elfogynak a próbálkozási lehetőségek, az eszköz feloldásához fel kell vennie a kapcsolatot szolgáltatójával.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">A SIM-kártya le van tiltva. A folytatáshoz adja meg a PUK-kódot. Még <xliff:g id="_NUMBER_1">%d</xliff:g> próbálkozása van, mielőtt végleg használhatatlanná válik a SIM-kártya. További információért forduljon a szolgáltatóhoz.</item>
- <item quantity="one">A SIM-kártya le van tiltva. A folytatáshoz adja meg a PUK-kódot. Még <xliff:g id="_NUMBER_0">%d</xliff:g> próbálkozása van, mielőtt végleg használhatatlanná válik a SIM-kártya. További információért forduljon a szolgáltatóhoz.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Adja meg a SIM-kártya PIN-kódját. # próbálkozása maradt. Ha elfogynak a próbálkozási lehetőségek, az eszköz feloldásához fel kell vennie a kapcsolatot szolgáltatójával.}other{Adja meg a SIM-kártya PIN-kódját. # próbálkozási lehetősége maradt.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{A SIM-kártya le van tiltva. A folytatáshoz adja meg a PUK-kódot. Még # próbálkozása van, mielőtt végleg használhatatlanná válik a SIM-kártya. További információért forduljon a szolgáltatóhoz.}other{A SIM-kártya le van tiltva. A folytatáshoz adja meg a PUK-kódot. Még # próbálkozása van, mielőtt végleg használhatatlanná válik a SIM-kártya. További információért forduljon a szolgáltatóhoz.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Alapértelmezett"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Buborék"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analóg"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml
index 2af6e7bbf619..5d65da0e0429 100644
--- a/packages/SystemUI/res-keyguard/values-hy/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Դուք սխալ եք մուտքագրել ձեր գաղտնաբառը <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ: \n\nՓորձեք կրկին <xliff:g id="NUMBER_1">%2$d</xliff:g> վայրկյանից:"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Դուք սխալ եք մուտքագրել ձեր ապակողպման նախշը <xliff:g id="NUMBER_0">%1$d</xliff:g> անգամ: \n\nՓորձեք կրկին <xliff:g id="NUMBER_1">%2$d</xliff:g> վայրկյանից։"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM PIN կոդը սխալ է։ Այժմ պետք է դիմեք ձեր օպերատորին՝ սարքն արգելահանելու համար:"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">SIM PIN կոդը սխալ է: Մնաց <xliff:g id="NUMBER_1">%d</xliff:g> փորձ, որից հետո պետք է դիմեք ձեր օպերատորին՝ սարքն արգելահանելու համար:</item>
- <item quantity="other">SIM PIN կոդը սխալ է: Մնաց <xliff:g id="NUMBER_1">%d</xliff:g> փորձ:</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM քարտի PIN կոդը սխալ է։ Մնաց # փորձ, որից հետո պետք է դիմեք ձեր օպերատորին՝ սարքն ապակողպելու համար։}one{SIM քարտի PIN կոդը սխալ է։ Մնաց # փորձ։ }other{SIM քարտի PIN կոդը սխալ է։ Մնաց # փորձ։ }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM-ը հնարավոր չէ օգտագործել: Դիմեք ձեր օպերատորին:"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">SIM PUK կոդը սխալ է: Մնաց <xliff:g id="NUMBER_1">%d</xliff:g> փորձ, որից հետո SIM քարտն այլևս հնարավոր չի լինի օգտագործել:</item>
- <item quantity="other">SIM PUK կոդը սխալ է: Մնաց <xliff:g id="NUMBER_1">%d</xliff:g> փորձ, որից հետո SIM քարտն այլևս հնարավոր չի լինի օգտագործել:</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{SIM քարտի PUK կոդը սխալ է։ Մնաց # փորձ, որից հետո SIM քարտն այլևս հնարավոր չի լինի օգտագործել։}one{SIM քարտի PUK կոդը սխալ է։ Մնաց # փորձ, որից հետո SIM քարտն այլևս հնարավոր չի լինի օգտագործել։}other{SIM քարտի PUK կոդը սխալ է։ Մնաց # փորձ, որից հետո SIM քարտն այլևս հնարավոր չի լինի օգտագործել։}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM PIN կոդի գործողությունը ձախողվեց:"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUK կոդի գործողությունը ձախողվեց:"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Փոխել ներածման եղանակը"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Սարքը կողպվել է ձեռքով"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Չհաջողվեց ճանաչել"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Դեմքով ապակողպման համար թույլատրեք տեսախցիկի օգտագործումը"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Մուտքագրեք SIM քարտի PIN կոդը: Մնացել է <xliff:g id="NUMBER_1">%d</xliff:g> փորձ:</item>
- <item quantity="other">Մուտքագրեք SIM քարտի PIN կոդը: Մնացել է <xliff:g id="NUMBER_1">%d</xliff:g> փորձ:</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">SIM քարտն անջատված է: Շարունակելու համար մուտքագրեք PUK կոդը: Մնացել է <xliff:g id="_NUMBER_1">%d</xliff:g> փորձ, որից հետո SIM քարտն այլևս հնարավոր չի լինի օգտագործել: Մանրամասների համար դիմեք օպերատորին:</item>
- <item quantity="other">SIM քարտն անջատված է: Շարունակելու համար մուտքագրեք PUK կոդը: Մնացել է <xliff:g id="_NUMBER_1">%d</xliff:g> փորձ, որից հետո SIM քարտն այլևս հնարավոր չի լինի օգտագործել: Մանրամասների համար դիմեք օպերատորին:</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Մուտքագրեք SIM քարտի PIN կոդը։ Մնացել է # փորձ, որից հետո պետք է դիմեք ձեր օպերատորին՝ սարքն ապակողպելու համար։}one{Մուտքագրեք SIM քարտի PIN կոդը։ Մնացել է # փորձ։}other{Մուտքագրեք SIM քարտի PIN կոդը։ Մնացել է # փորձ։}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM քարտն այժմ անջատված է։ Շարունակելու համար մուտքագրեք PUK կոդը։ Մնացել է # փորձ, որից հետո SIM քարտն այլևս հնարավոր չի լինի օգտագործել։ Մանրամասների համար դիմեք օպերատորին։}one{SIM քարտն այժմ անջատված է։ Շարունակելու համար մուտքագրեք PUK կոդը։ Մնացել է # փորձ, որից հետո SIM քարտն այլևս հնարավոր չի լինի օգտագործել։ Մանրամասների համար դիմեք օպերատորին։}other{SIM քարտն այժմ անջատված է։ Շարունակելու համար մուտքագրեք PUK կոդը։ Մնացել է # փորձ, որից հետո SIM քարտն այլևս հնարավոր չի լինի օգտագործել։ Մանրամասների համար դիմեք օպերատորին։}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Կանխադրված"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Պղպջակ"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Անալոգային"</string>
diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml
index 1bd0db26d27c..c42f5eb69306 100644
--- a/packages/SystemUI/res-keyguard/values-in/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-in/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah mengetik sandi. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> detik."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> detik."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Kode PIN SIM salah. Hubungi operator untuk membuka kunci perangkat."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Kode PIN SIM salah, sisa, sisa <xliff:g id="NUMBER_1">%d</xliff:g> percobaan.</item>
- <item quantity="one">Kode PIN SIM salah, sisa <xliff:g id="NUMBER_0">%d</xliff:g> percobaan sebelum Anda harus menghubungi operator untuk membuka kunci perangkat.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Kode PIN SIM salah. Tersisa # percobaan lagi sebelum Anda harus menghubungi operator untuk membuka kunci perangkat.}other{Kode PIN SIM salah, tersisa # percobaan lagi. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM tidak dapat digunakan. Hubungi operator Anda."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Kode PUK SIM salah, sisa <xliff:g id="NUMBER_1">%d</xliff:g> percobaan sebelum SIM tidak dapat digunakan selamanya.</item>
- <item quantity="one">Kode PUK SIM salah, sisa <xliff:g id="NUMBER_0">%d</xliff:g> percobaan sebelum SIM tidak dapat digunakan selamanya.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Kode PUK SIM salah, tersisa # percobaan lagi sebelum SIM tidak dapat digunakan secara permanen.}other{Kode PUK SIM salah, tersisa # percobaan lagi sebelum SIM tidak dapat digunakan secara permanen.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Operasi PIN SIM gagal!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Operasi PUK SIM gagal!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Beralih metode input"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Perangkat dikunci secara manual"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Tidak dikenali"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Untuk pakai Face Unlock, beri akses kamera di Setelan"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Masukkan PIN SIM. Tersisa <xliff:g id="NUMBER_1">%d</xliff:g> percobaan.</item>
- <item quantity="one">Masukkan PIN SIM. Tersisa <xliff:g id="NUMBER_0">%d</xliff:g> percobaan sebelum Anda harus menghubungi operator untuk membuka kunci perangkat.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM kini dinonaktifkan. Masukkan kode PUK untuk melanjutkan. Tersisa <xliff:g id="_NUMBER_1">%d</xliff:g> percobaan sebelum SIM tidak dapat digunakan secara permanen. Hubungi operator untuk mengetahui detailnya.</item>
- <item quantity="one">SIM kini dinonaktifkan. Masukkan kode PUK untuk melanjutkan. Tersisa <xliff:g id="_NUMBER_0">%d</xliff:g> percobaan sebelum SIM tidak dapat digunakan secara permanen. Hubungi operator untuk mengetahui detailnya.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Masukkan PIN SIM. Tersisa # percobaan lagi sebelum Anda harus menghubungi operator untuk membuka kunci perangkat.}other{Masukkan PIN SIM. Tersisa # percobaan lagi.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM kini dinonaktifkan. Masukkan kode PUK untuk melanjutkan. Tersisa # percobaan lagi sebelum SIM tidak dapat digunakan secara permanen. Hubungi operator untuk mengetahui detailnya.}other{SIM kini dinonaktifkan. Masukkan kode PUK untuk melanjutkan. Tersisa # percobaan lagi sebelum SIM tidak dapat digunakan secara permanen. Hubungi operator untuk mengetahui detailnya.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Default"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Balon"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index fc3af8403558..ea1a8ee3789c 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Þú hefur slegið inn rangt aðgangsorð <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. \n\nReyndu aftur eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> sekúndur."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Þú hefur teiknað rangt opnunarmynstur <xliff:g id="NUMBER_0">%1$d</xliff:g> sinnum. \n\nReyndu aftur eftir <xliff:g id="NUMBER_1">%2$d</xliff:g> sekúndur."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Rangt PIN-númer SIM-korts. Nú þarftu að hafa samband við símafyrirtækið til að opna fyrir tækið."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Rangt PIN-númer SIM-korts. Þú átt <xliff:g id="NUMBER_1">%d</xliff:g> tilraun eftir.</item>
- <item quantity="other">Rangt PIN-númer SIM-korts. Þú átt <xliff:g id="NUMBER_1">%d</xliff:g> tilraunir eftir.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Rangt PIN-númer SIM-korts. Þú átt # tilraun eftir áður en þú þarft að hafa samband við símafyrirtækið þitt til að taka tækið úr lás.}one{Rangt PIN-númer SIM-korts. Þú átt # tilraun eftir. }other{Rangt PIN-númer SIM-korts. Þú átt # tilraunir eftir. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM-kortið er ónothæft. Hafðu samband við símafyrirtækið þitt."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Rangt PUK-númer SIM-korts. Þú átt <xliff:g id="NUMBER_1">%d</xliff:g> tilraun eftir áður en SIM-kortið verður ónothæft til frambúðar.</item>
- <item quantity="other">Rangt PUK-númer SIM-korts. Þú átt <xliff:g id="NUMBER_1">%d</xliff:g> tilraunir eftir áður en SIM-kortið verður ónothæft til frambúðar.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Rangt PUK-númer SIM-korts. Þú átt # tilraun eftir áður en SIM-kortið verður ónothæft til frambúðar.}one{Rangt PUK-númer SIM-korts. Þú átt # tilraun eftir áður en SIM-kortið verður ónothæft til frambúðar.}other{Rangt PUK-númer SIM-korts. Þú átt # tilraunir eftir áður en SIM-kortið verður ónothæft til frambúðar.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"PIN-aðgerð SIM-korts mistókst!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"PUK-aðgerð SIM-korts mistókst!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Skipta um innsláttaraðferð"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Tækinu var læst handvirkt"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Þekktist ekki"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Kveiktu á myndavélaaðgangi í stillingum til að nota andlitskenni"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Sláðu inn PIN-númer SIM-korts. Þú átt <xliff:g id="NUMBER_1">%d</xliff:g> tilraun eftir.</item>
- <item quantity="other">Sláðu inn PIN-númer SIM-korts. Þú átt <xliff:g id="NUMBER_1">%d</xliff:g> tilraunir eftir.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">SIM-kortið er nú óvirkt. Sláðu inn PUK-númer til að halda áfram. Það er <xliff:g id="_NUMBER_1">%d</xliff:g> tilraun eftir þar til SIM-kortið verður ónothæft til frambúðar. Hafðu samband við símafyrirtækið til að fá upplýsingar.</item>
- <item quantity="other">SIM-kortið er nú óvirkt. Sláðu inn PUK-númer til að halda áfram. Það eru <xliff:g id="_NUMBER_1">%d</xliff:g> tilraunir eftir þar til SIM-kortið verður ónothæft til frambúðar. Hafðu samband við símafyrirtækið til að fá upplýsingar.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Sláðu inn PIN-númer SIM-korts. Þú átt # tilraun eftir áður en þú þarft að hafa samband við símafyrirtækið þitt til að taka tækið úr lás.}one{Sláðu inn PIN-númer SIM-kortsins. Þú átt # tilraun eftir.}other{Sláðu inn PIN-númer SIM-kortsins. Þú átt # tilraunir eftir.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM-kortið er nú óvirkt. Sláðu inn PUK-númer til að halda áfram. Þú átt # tilraun eftir þar til SIM-kortið verður ónothæft til frambúðar. Hafðu samband við símafyrirtækið til að fá upplýsingar.}one{SIM-kortið er nú óvirkt. Sláðu inn PUK-númer til að halda áfram. Þú átt # tilraun eftir þar til SIM-kortið verður ónothæft til frambúðar. Hafðu samband við símafyrirtækið til að fá upplýsingar.}other{SIM-kortið er nú óvirkt. Sláðu inn PUK-númer til að halda áfram. Þú átt # tilraunir eftir þar til SIM-kortið verður ónothæft til frambúðar. Hafðu samband við símafyrirtækið til að fá upplýsingar.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Sjálfgefið"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Blaðra"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Með vísum"</string>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index 81138f18a01c..f1583b159344 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Hai digitato la tua password <xliff:g id="NUMBER_0">%1$d</xliff:g> volte in modo errato. \n\nRiprova tra <xliff:g id="NUMBER_1">%2$d</xliff:g> secondi."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"<xliff:g id="NUMBER_0">%1$d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. \n\nRiprova tra <xliff:g id="NUMBER_1">%2$d</xliff:g> secondi."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Codice PIN della SIM errato. Devi contattare l\'operatore per sbloccare il dispositivo."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Codice PIN della SIM errato. Hai ancora <xliff:g id="NUMBER_1">%d</xliff:g> tentativi a disposizione.</item>
- <item quantity="one">Codice PIN della SIM errato. Hai ancora <xliff:g id="NUMBER_0">%d</xliff:g> tentativo a disposizione, dopodiché dovrai contattare l\'operatore per sbloccare il dispositivo.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Codice PIN della SIM errato. Hai ancora # tentativo a disposizione, dopodiché dovrai contattare l\'operatore per sbloccare il dispositivo.}other{Codice PIN della SIM errato. Hai ancora # tentativi a disposizione. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM inutilizzabile. Contatta il tuo operatore."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Codice PUK della SIM errato. Hai ancora <xliff:g id="NUMBER_1">%d</xliff:g> tentativi a disposizione prima che la SIM diventi definitivamente inutilizzabile.</item>
- <item quantity="one">Codice PUK della SIM errato. Hai ancora <xliff:g id="NUMBER_0">%d</xliff:g> tentativo a disposizione prima che la SIM diventi definitivamente inutilizzabile.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Codice PUK della SIM errato. Hai ancora # tentativo a disposizione prima che la SIM diventi definitivamente inutilizzabile.}other{Codice PUK della SIM errato. Hai ancora # tentativi a disposizione prima che la SIM diventi definitivamente inutilizzabile.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Operazione con PIN della SIM non riuscita."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Operazione con PUK della SIM non riuscita."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Cambia metodo di immissione"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Il dispositivo è stato bloccato manualmente"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Non riconosciuto"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Sblocco con volto richiede l\'accesso alla fotocamera"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Inserisci il codice PIN della SIM. Hai ancora <xliff:g id="NUMBER_1">%d</xliff:g> tentativi a disposizione.</item>
- <item quantity="one">Inserisci il codice PIN della SIM. Hai ancora <xliff:g id="NUMBER_0">%d</xliff:g> tentativo a disposizione, dopodiché dovrai contattare l\'operatore per sbloccare il dispositivo.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora <xliff:g id="_NUMBER_1">%d</xliff:g> tentativi a disposizione prima che la SIM diventi definitivamente inutilizzabile. Per informazioni dettagliate, contatta l\'operatore.</item>
- <item quantity="one">La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora <xliff:g id="_NUMBER_0">%d</xliff:g> tentativo a disposizione prima che la SIM diventi definitivamente inutilizzabile. Per informazioni dettagliate, contatta l\'operatore.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Inserisci il codice PIN della SIM. Hai ancora # tentativo a disposizione, dopodiché dovrai contattare l\'operatore per sbloccare il dispositivo.}other{Inserisci il PIN della SIM. Hai a disposizione ancora # tentativi.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora # tentativo a disposizione prima che la SIM diventi definitivamente inutilizzabile. Per informazioni dettagliate, contatta l\'operatore.}other{La scheda SIM è ora disattivata. Inserisci il codice PUK per continuare. Hai ancora # tentativi a disposizione prima che la SIM diventi definitivamente inutilizzabile. Per informazioni dettagliate, contatta l\'operatore.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Predefinito"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bolla"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogico"</string>
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index 90b48289d802..470dd726887a 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -68,19 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"הקלדת סיסמה שגויה <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. \n\nאפשר לנסות שוב בעוד <xliff:g id="NUMBER_1">%2$d</xliff:g> שניות."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"שרטטת קו ביטול נעילה שגוי <xliff:g id="NUMBER_0">%1$d</xliff:g> פעמים. \n\nאפשר לנסות שוב בעוד <xliff:g id="NUMBER_1">%2$d</xliff:g> שניות."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"‏קוד האימות של כרטיס ה-SIM שגוי. יש ליצור קשר עם הספק כדי לבטל את נעילת המכשיר."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="two">‏קוד האימות של כרטיס ה-SIM שגוי. נותרו לך עוד <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות.</item>
- <item quantity="many">‏קוד האימות של כרטיס ה-SIM שגוי. נותרו לך עוד <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות.</item>
- <item quantity="other">‏קוד האימות של כרטיס ה-SIM שגוי. נותרו לך עוד <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות.</item>
- <item quantity="one">‏קוד האימות של כרטיס ה-SIM שגוי. נותר לך עוד ניסיון <xliff:g id="NUMBER_0">%d</xliff:g> לפני שיהיה עליך ליצור קשר עם הספק כדי לבטל את נעילת המכשיר.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{‏קוד האימות של כרטיס ה-SIM שגוי. נשאר לך עוד ניסיון אחד (#) לפני שיהיה צורך ליצור קשר עם הספק כדי לבטל את נעילת המכשיר.}two{‏קוד האימות של כרטיס ה-SIM שגוי. נשארו לך עוד # ניסיונות. }many{‏קוד האימות של כרטיס ה-SIM שגוי. נשארו לך עוד # ניסיונות. }other{‏קוד האימות של כרטיס ה-SIM שגוי. נשארו לך עוד # ניסיונות. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"‏לא ניתן להשתמש בכרטיס ה-SIM. יש ליצור קשר עם הספק."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="two">‏קוד ה-PUK של כרטיס ה-SIM שגוי. נותרו לך עוד <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות לפני שכרטיס ה-SIM יינעל לצמיתות.</item>
- <item quantity="many">‏קוד ה-PUK של כרטיס ה-SIM שגוי. נותרו לך עוד <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות לפני שכרטיס ה-SIM יינעל לצמיתות.</item>
- <item quantity="other">‏קוד ה-PUK של כרטיס ה-SIM שגוי. נותרו לך עוד <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות לפני שכרטיס ה-SIM יינעל לצמיתות.</item>
- <item quantity="one">‏קוד ה-PUK של כרטיס ה-SIM שגוי. נותר לך ניסיון <xliff:g id="NUMBER_0">%d</xliff:g> נוסף לפני שכרטיס ה-SIM יינעל לצמיתות.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{‏קוד ה-PUK של כרטיס ה-SIM שגוי. נשאר לך עוד ניסיון אחד (#) לפני שכרטיס ה-SIM יינעל לתמיד.}two{‏קוד ה-PUK של כרטיס ה-SIM שגוי. נשארו לך עוד # ניסיונות לפני שכרטיס ה-SIM יינעל לתמיד.}many{‏קוד ה-PUK של כרטיס ה-SIM שגוי. נשארו לך עוד # ניסיונות לפני שכרטיס ה-SIM יינעל לתמיד.}other{‏קוד ה-PUK של כרטיס ה-SIM שגוי. נשארו לך עוד # ניסיונות לפני שכרטיס ה-SIM יינעל לתמיד.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"‏נכשלה פעולת קוד הגישה של כרטיס ה-SIM"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"‏הניסיון לביטול הנעילה של כרטיס ה-SIM באמצעות קוד PUK נכשל!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"החלפת שיטת קלט"</string>
@@ -95,18 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"המכשיר ננעל באופן ידני"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"לא זוהתה"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"לזיהוי הפנים יש להפעיל את הגישה למצלמה בהגדרות"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="two">‏יש להזין קוד אימות של כרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות נוספים.</item>
- <item quantity="many">‏יש להזין קוד אימות של כרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות נוספים.</item>
- <item quantity="other">‏יש להזין קוד אימות של כרטיס SIM. נותרו לך <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות נוספים.</item>
- <item quantity="one">‏יש להזין קוד אימות של כרטיס SIM. נותר לך ניסיון נוסף (<xliff:g id="NUMBER_0">%d</xliff:g>) לפני שיהיה צורך ליצור קשר עם הספק כדי לבטל את נעילת המכשיר.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="two">‏כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נותרו לך <xliff:g id="_NUMBER_1">%d</xliff:g> ניסיונות נוספים לפני שכרטיס ה-SIM יינעל באופן סופי. למידע נוסף, ניתן לפנות לספק שלך.</item>
- <item quantity="many">‏כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נותרו לך <xliff:g id="_NUMBER_1">%d</xliff:g> ניסיונות נוספים לפני שכרטיס ה-SIM יינעל באופן סופי. למידע נוסף, ניתן לפנות לספק שלך.</item>
- <item quantity="other">‏כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נותרו לך <xliff:g id="_NUMBER_1">%d</xliff:g> ניסיונות נוספים לפני שכרטיס ה-SIM יינעל באופן סופי. למידע נוסף, ניתן לפנות לספק שלך.</item>
- <item quantity="one">‏כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נותר לך ניסיון אחד (<xliff:g id="_NUMBER_0">%d</xliff:g>) נוסף לפני שכרטיס ה-SIM יינעל באופן סופי. למידע נוסף, ניתן לפנות לספק שלך.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{‏יש להזין את קוד האימות של כרטיס ה-SIM. נשאר לך עוד ניסיון אחד (#) לפני שיהיה צורך ליצור קשר עם הספק כדי לבטל את נעילת המכשיר.}two{‏יש להזין את קוד האימות של כרטיס ה-SIM. נשארו לך עוד # ניסיונות.}many{‏יש להזין את קוד האימות של כרטיס ה-SIM. נשארו לך עוד # ניסיונות.}other{‏יש להזין את קוד האימות של כרטיס ה-SIM. נשארו לך עוד # ניסיונות.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{‏כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נשאר לך עוד ניסיון אחד (#) לפני שכרטיס ה-SIM יינעל לתמיד. למידע נוסף, ניתן לפנות לספק.}two{‏כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נשארו לך עוד # ניסיונות לפני שכרטיס ה-SIM יינעל לתמיד. למידע נוסף, ניתן לפנות לספק.}many{‏כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נשארו לך עוד # ניסיונות לפני שכרטיס ה-SIM יינעל לתמיד. למידע נוסף, ניתן לפנות לספק.}other{‏כרטיס ה-SIM מושבת כעת. יש להזין קוד PUK כדי להמשיך. נשארו לך עוד # ניסיונות לפני שכרטיס ה-SIM יינעל לתמיד. למידע נוסף, ניתן לפנות לספק.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"ברירת מחדל"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"בועה"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"אנלוגי"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml
index 471bc12a2568..efe13ece180a 100644
--- a/packages/SystemUI/res-keyguard/values-ja/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"パスワードの入力を <xliff:g id="NUMBER_0">%1$d</xliff:g> 回間違えました。\n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後にもう一度お試しください。"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"ロック解除パターンの入力を <xliff:g id="NUMBER_0">%1$d</xliff:g> 回間違えました。\n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後にもう一度お試しください。"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM PIN コードが無効です。お使いのデバイスをロック解除するには携帯通信会社にお問い合わせいただく必要があります。"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">SIM PIN コードが無効です。入力できるのはあと <xliff:g id="NUMBER_1">%d</xliff:g> 回です。</item>
- <item quantity="one">SIM PIN コードが無効です。入力できるのはあと <xliff:g id="NUMBER_0">%d</xliff:g> 回です。この回数を超えると、お使いのデバイスをロック解除するのに携帯通信会社にお問い合わせいただく必要があります。</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM PIN コードが無効です。入力できるのはあと # 回です。この回数を超えると、お使いのデバイスをロック解除するのに携帯通信会社にお問い合わせいただく必要があります。}other{SIM PIN コードが無効です。入力できるのはあと # 回です。}}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM は使用できません。携帯通信会社にお問い合わせください。"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">SIM PUK コードが無効です。入力できるのはあと <xliff:g id="NUMBER_1">%d</xliff:g> 回です。この回数を超えると SIM は完全に使用できなくなります。</item>
- <item quantity="one">SIM PUK コードが無効です。入力できるのはあと <xliff:g id="NUMBER_0">%d</xliff:g> 回です。この回数を超えると SIM は完全に使用できなくなります。</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{SIM PUK コードが無効です。入力できるのはあと # 回です。この回数を超えると SIM は完全に使用できなくなります。}other{SIM PUK コードが無効です。入力できるのはあと # 回です。この回数を超えると SIM は完全に使用できなくなります。}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM PIN 操作に失敗しました。"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUK 操作に失敗しました。"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"入力方法の切り替え"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"デバイスは手動でロックされました"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"認識されませんでした"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"顔認証の使用: 設定でカメラアクセスを有効にしてください"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">SIM PIN を入力してください。入力できるのはあと <xliff:g id="NUMBER_1">%d</xliff:g> 回です。</item>
- <item quantity="one">SIM PIN を入力してください。入力できるのはあと <xliff:g id="NUMBER_0">%d</xliff:g> 回です。この回数を超えた場合は、携帯通信会社にお問い合わせください。</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM が無効になりました。続行するには PUK コードを入力してください。入力できるのはあと <xliff:g id="_NUMBER_1">%d</xliff:g> 回です。この回数を超えると SIM は完全に使用できなくなります。詳しくは携帯通信会社にお問い合わせください。</item>
- <item quantity="one">SIM が無効になりました。続行するには PUK コードを入力してください。入力できるのはあと <xliff:g id="_NUMBER_0">%d</xliff:g> 回です。この回数を超えると SIM は完全に使用できなくなります。詳しくは携帯通信会社にお問い合わせください。</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{SIM PIN を入力してください。入力できるのはあと # 回です。この回数を超えると、お使いのデバイスをロック解除するのに携帯通信会社にお問い合わせいただく必要があります。}other{SIM PIN を入力してください。入力できるのはあと # 回です。}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM が無効になりました。続行するには PUK コードを入力してください。入力できるのはあと # 回です。この回数を超えると SIM は完全に使用できなくなります。詳しくは携帯通信会社にお問い合わせください。}other{SIM が無効になりました。続行するには PUK コードを入力してください。入力できるのはあと # 回です。この回数を超えると SIM は完全に使用できなくなります。詳しくは携帯通信会社にお問い合わせください。}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"デフォルト"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"バブル"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"アナログ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ka/strings.xml b/packages/SystemUI/res-keyguard/values-ka/strings.xml
index 6f9fed978296..67f85f0d7b1b 100644
--- a/packages/SystemUI/res-keyguard/values-ka/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ka/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"თქვენ არასწორად აკრიფეთ პაროლი <xliff:g id="NUMBER_0">%1$d</xliff:g>-ჯერ. \n\nცადეთ ხელახლა <xliff:g id="NUMBER_1">%2$d</xliff:g> წამში."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"თქვენ არასწორად დახატეთ განბლოკვის ნიმუში <xliff:g id="NUMBER_0">%1$d</xliff:g>-ჯერ. \n\nცადეთ ხელახლა <xliff:g id="NUMBER_1">%2$d</xliff:g> წამში."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM ბარათის PIN-კოდი არასწორია. ახლა თქვენი მოწყობილობის განსაბლოკად თქვენს ოპერატორთან დაკავშირება მოგიწევთ."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">SIM ბარათის PIN-კოდი არასწორია. თქვენ დაგრჩათ <xliff:g id="NUMBER_1">%d</xliff:g> მცდელობა.</item>
- <item quantity="one">SIM ბარათის PIN-კოდი არასწორია. თქვენ დაგრჩათ <xliff:g id="NUMBER_0">%d</xliff:g> მცდელობა, რომლის შემდეგაც თქვენი მოწყობილობის განსაბლოკად თქვენს ოპერატორთან დაკავშირება მოგიწევთ.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM-ის PIN კოდი არასწორია. თქვენ დაგრჩათ # მცდელობა, რის შემდეგაც მოწყობილობის განსაბლოკად ოპერატორთან დაკავშირება მოგიწევთ.}other{SIM ბარათის PIN-კოდი არასწორია. თქვენ დაგრჩათ # მცდელობა. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM ბარათი გამოუსადეგარია. დაუკავშირდით თქვენს ოპერატორს."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">SIM ბარათის PUK-კოდი არასწორია. თქვენ დაგრჩათ <xliff:g id="NUMBER_1">%d</xliff:g> მცდელობა, რომელთა შემდეგაც თქვენი SIM სამუდამოდ გამოუსადეგარი გახდება.</item>
- <item quantity="one">SIM ბარათის PUK-კოდი არასწორია. თქვენ დაგრჩათ <xliff:g id="NUMBER_0">%d</xliff:g> მცდელობა, რომლის შემდეგაც თქვენი SIM სამუდამოდ გამოუსადეგარი გახდება.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{SIM ბარათის PUK-კოდი არასწორია. თქვენ დაგრჩათ # მცდელობა, რის შემდეგაც თქვენი SIM სამუდამოდ გამოუსადეგარი გახდება.}other{არასწორი SIM ბარათის PUK-კოდი. თქვენ დაგრჩათ # მცდელობა, სანამ SIM ბარათი სამუდამოდ გამოუსადეგარი გახდება.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM ბარათის PIN-კოდით განბლოკვა ვერ მოხერხდა!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM ბარათის PUK-კოდით განბლოკვა ვერ მოხერხდა!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"შეყვანის მეთოდის გადართვა"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"მოწყობილობა ხელით ჩაიკეტა"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"არ არის ამოცნობილი"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"სახით განბლოკვით სარგებლობისთვის, ჩართეთ კამერაზე წვდომა პარამეტრებში"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">შეიყვანეთ SIM ბარათის PIN-კოდი. თქვენ დაგრჩათ <xliff:g id="NUMBER_1">%d</xliff:g> მცდელობა.</item>
- <item quantity="one">შეიყვანეთ SIM ბარათის PIN-კოდი. თქვენ დაგრჩათ <xliff:g id="NUMBER_0">%d</xliff:g> მცდელობა, რომლის შემდეგაც მოწყობილობის განსაბლოკად დაგჭირდებათ თქვენს ოპერატორთან დაკავშირება.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM ბარათი ახლა დეაქტივირებულია. გასაგრძელებლად შეიყვანეთ PUK-კოდი. თქვენ დაგრჩათ <xliff:g id="_NUMBER_1">%d</xliff:g> მცდელობა, სანამ SIM სამუდამოდ გამოუსადეგარი გახდება. დეტალური ინფორმაციისთვის დაუკავშირდით თქვენს ოპერატორს.</item>
- <item quantity="one">SIM ბარათი ახლა დეაქტივირებულია. გასაგრძელებლად შეიყვანეთ PUK-კოდი. თქვენ დაგრჩათ <xliff:g id="_NUMBER_0">%d</xliff:g> მცდელობა, სანამ SIM სამუდამოდ გამოუსადეგარი გახდება. დეტალური ინფორმაციისთვის დაუკავშირდით თქვენს ოპერატორს.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{შეიყვანეთ SIM ბარათის PIN-კოდი. თქვენ დაგრჩათ # მცდელობა, რის შემდეგაც მოწყობილობის განსაბლოკად დაგჭირდებათ თქვენს ოპერატორთან დაკავშირება.}other{შეიყვანეთ SIM ბარათის PIN-კოდი. თქვენ დაგრჩათ # მცდელობა.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM ბარათი ახლა გათიშულია. გასაგრძელებლად შეიყვანეთ PUK-კოდი. თქვენ დაგრჩათ # მცდელობა, სანამ SIM სამუდამოდ გამოუსადეგარი გახდება. დეტალური ინფორმაციისთვის დაუკავშირდით თქვენს ოპერატორს.}other{SIM ბარათი ახლა გათიშულია. გასაგრძელებლად შეიყვანეთ PUK-კოდი. თქვენ დაგრჩათ # მცდელობა, სანამ SIM სამუდამოდ გამოუსადეგარი გახდება. დეტალური ინფორმაციისთვის დაუკავშირდით თქვენს ოპერატორს.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"ნაგულისხმევი"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"ბუშტი"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"ანალოგური"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml
index f4a51bd9173f..71e9a9d20dfc 100644
--- a/packages/SystemUI/res-keyguard/values-kk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Құпия сөз <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате енгізілді. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Құлыпты ашу өрнегі <xliff:g id="NUMBER_0">%1$d</xliff:g> рет қате енгізілді. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секундтан кейін әрекетті қайталаңыз."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM PIN коды дұрыс емес, операторға хабарласып, құрылғының құлпын ашуды сұраңыз."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">SIM PIN коды дұрыс емес. <xliff:g id="NUMBER_1">%d</xliff:g> әрекет қалды.</item>
- <item quantity="one">SIM PIN коды дұрыс емес. <xliff:g id="NUMBER_0">%d</xliff:g> әрекет қалды. Одан кейін құрылғы құлпын ашу үшін операторға хабарласуға тура келеді.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM картасының PIN коды дұрыс емес. # мүмкіндігіңіз қалды. Одан кейін құрылғы құлпын ашу үшін операторға хабарласуға тура келеді.}other{SIM картасының PIN коды дұрыс емес. # әрекет қалды. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM қолданыстан шыққан. Оператормен хабарласыңыз."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">SIM PUK коды дұрыс емес. <xliff:g id="NUMBER_1">%d</xliff:g> әрекет қалды. Одан кейін SIM біржола қолданыстан шығады.</item>
- <item quantity="one">SIM PUK коды дұрыс емес. <xliff:g id="NUMBER_0">%d</xliff:g> әрекет қалды. Одан кейін SIM біржола қолданыстан шығады.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{SIM картасының PUK коды дұрыс емес, # әрекеттен кейін SIM картасы біржола құлыпталады.}other{SIM картасының PUK коды дұрыс емес, # әрекеттен кейін SIM картасы біржола құлыпталады.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM PIN кодымен құлпы ашылмады!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUK кодымен құлпы ашылмады!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Енгізу әдісін ауыстыру"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Құрылғы қолмен құлыпталды"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Танылмады"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Face Unlock функциясын пайдалану үшін параметрлерден камераны пайдалану рұқсатын қосыңыз."</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">SIM PIN кодын енгізіңіз. <xliff:g id="NUMBER_1">%d</xliff:g> мүмкіндік қалды, одан кейін оператордан SIM картасының құлпын ашуды сұрауға тура келеді.</item>
- <item quantity="one">SIM PIN кодын енгізіңіз. <xliff:g id="NUMBER_0">%d</xliff:g> мүмкіндік қалды, одан кейін оператордан SIM картасының құлпын ашуды сұрауға тура келеді.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM картасы өшірілді. Жалғастыру үшін PUK кодын енгізіңіз. <xliff:g id="_NUMBER_1">%d</xliff:g> мүмкіндік қалды, одан кейін SIM картасы біржола құлыпталады. Толығырақ мәліметті оператордан алыңыз.</item>
- <item quantity="one">SIM картасы өшірілді. Жалғастыру үшін PUK кодын енгізіңіз. <xliff:g id="_NUMBER_0">%d</xliff:g> мүмкіндік қалды, одан кейін SIM картасы біржола құлыпталады. Толығырақ мәліметті оператордан алыңыз.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{SIM картасының PIN кодын енгізіңіз. # мүмкіндік қалды, одан кейін оператордан SIM картасының құлпын ашуды сұрауға тура келеді.}other{SIM картасының PIN кодын енгізіңіз. # әрекет қалды.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM картасы өшірілді. Жалғастыру үшін PUK кодын енгізіңіз. # мүмкіндік қалды, одан кейін SIM картасы біржола құлыпталады. Толығырақ мәліметті оператордан алыңыз.}other{SIM картасы өшірілді. Жалғастыру үшін PUK кодын енгізіңіз. # мүмкіндік қалды, одан кейін SIM картасы біржола құлыпталады. Толығырақ мәліметті оператордан алыңыз.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Әдепкі"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Көпіршік"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Аналогтық"</string>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index 8c90dec4f5fe..645f6387ceca 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"អ្នក​បាន​វាយ​បញ្ចូល​ពាក្យ​សម្ងាត់​របស់​អ្នក​មិន​ត្រឹមត្រូវ​ចំនួន <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង​ហើយ។ \n\nសូម​ព្យាយាម​ម្ដង​ទៀត​ក្នុង​រយៈ​ពេល <xliff:g id="NUMBER_1">%2$d</xliff:g> វិនាទី​ទៀត។"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"អ្នក​បាន​គូរ​លំនាំ​ដោះ​សោ​របស់​អ្នក​មិន​ត្រឹមត្រូវ​ចំនួន <xliff:g id="NUMBER_0">%1$d</xliff:g> ដង​ហើយ។ \n\nសូមព្យាយាម​ម្ដង​ទៀត​ក្នុង​រយៈ​ពេល <xliff:g id="NUMBER_1">%2$d</xliff:g> វិនាទី​ទៀត។"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"កូដ PIN របស់​ស៊ីម​មិន​ត្រឹមត្រូវ​ទេ អ្នក​ត្រូវ​ទាក់ទង​ទៅក្រុមហ៊ុន​បម្រើ​សេវា​ទូរសព្ទ​របស់​អ្នក​ឥឡូវ​នេះ ដើម្បី​ដោះ​សោ​ឧបករណ៍​របស់​អ្នក។"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">កូដ PIN របស់​ស៊ីម​មិន​ត្រឹមត្រូវ​ទេ អ្នក​អាច​ព្យាយាម​បាន <xliff:g id="NUMBER_1">%d</xliff:g> ដងទៀត។</item>
- <item quantity="one">កូដ PIN របស់​ស៊ីម​មិន​ត្រឹមត្រូវ​ទេ ប្រសិន​បើ​អ្នក​បញ្ចូល​កូដខុស <xliff:g id="NUMBER_0">%d</xliff:g> ដងទៀត អ្នក​ត្រូវ​ទាក់ទង​ទៅ​ក្រុមហ៊ុន​បម្រើ​សេវាទូរសព្ទ​របស់អ្នក ដើម្បី​ដោះសោ​ឧបករណ៍របស់អ្នក។</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{កូដ PIN របស់ស៊ីម​មិនត្រឹមត្រូវទេ អ្នកអាច​ព្យាយាម​បញ្ចូលបាន # ដងទៀត មុនពេលដែល​ត្រូវទាក់ទង​ទៅក្រុមហ៊ុន​សេវាទូរសព្ទ​របស់អ្នក ដើម្បី​ដោះសោ​ឧបករណ៍​របស់អ្នក។}other{លេខ​កូដ​ PIN របស់​ស៊ីម​មិន​ត្រឹមត្រូវ​ អ្នកនៅ​សល់​ការ​ព្យាយាម # ដង​ទៀត។ }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"ស៊ីម​មិន​អាច​ប្រើ​បាន​ទេ។ សូម​ទាក់ទង​ទៅក្រុមហ៊ុន​បម្រើ​សេវា​ទូរសព្ទ​របស់​អ្នក។"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">កូដ PUK របស់​ស៊ីម​មិន​ត្រឹមត្រូវ​ទេ ប្រសិន​បើ​អ្នក​បញ្ចូល​កូដខុស <xliff:g id="NUMBER_1">%d</xliff:g> ដងទៀត ស៊ីមនឹង​មិន​អាច​ប្រើ​បាន​ជា​អចិន្ត្រៃយ៍។</item>
- <item quantity="one">កូដ PUK របស់​ស៊ីម​មិន​ត្រឹមត្រូវ​ទេ ប្រសិន​បើ​អ្នក​បញ្ចូល​កូដខុស <xliff:g id="NUMBER_0">%d</xliff:g> ដងទៀត ស៊ីមនឹង​មិន​អាច​ប្រើ​បាន​ជា​អចិន្ត្រៃយ៍។</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{កូដ PUK របស់​ស៊ីម​មិន​ត្រឹមត្រូវ​ទេ អ្នក​នៅ​សល់​ការ​ព្យាយាម # ដង​ទៀត​ មុន​ពេល​​ស៊ីម​មិន​អាច​ប្រើ​បាន​ជាអចិន្ត្រៃយ៍។}other{លេខ​កូដ PUK ស៊ីម​មិន​ត្រឹមត្រូវ អ្នកនៅ​សល់​កា​រព្យាយាម # ដង​ទៀត មុន​ពេល​ស៊ីម​មិន​អាច​ប្រើបាន​ជាអចិន្ត្រៃយ៍។}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"មិន​អាច​ដោះ​សោ​ដោយ​ប្រើកូដ​ PIN របស់​ស៊ីម​បានទេ!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"មិន​អាច​ដោះ​សោ​ដោយ​ប្រើកូដ​ PUK របស់​ស៊ីម​បានទេ!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ប្ដូរ​វិធី​បញ្ចូល"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ឧបករណ៍ត្រូវបានចាក់សោដោយអ្នកប្រើផ្ទាល់"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"មិនអាចសម្គាល់បានទេ"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"ដើម្បីដោះសោតាមទម្រង់មុខ សូមបើកសិទ្ធិចូលប្រើកាមេរ៉ានៅក្នុងការកំណត់"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">បញ្ចូល​កូដ PIN របស់ស៊ីម។ អ្នកនៅ​សល់ការ​ព្យាយាម <xliff:g id="NUMBER_1">%d</xliff:g> ដងទៀត។</item>
- <item quantity="one">បញ្ចូលកូដ PIN របស់ស៊ីម។ អ្នក​នៅសល់​ការព្យាយាម <xliff:g id="NUMBER_0">%d</xliff:g> ដង​ទៀត មុន​ពេល​ដែលអ្នក​ត្រូវទាក់ទង​ទៅ​ក្រុមហ៊ុន​សេវា​ទូរសព្ទ​របស់អ្នក​ដើម្បី​ដោះសោ​ឧបករណ៍​របស់អ្នក។</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">ឥឡូវនេះស៊ីមត្រូវបានបិទ។ សូមបញ្ចូលកូដ PUK ដើម្បីបន្ត។ អ្នកនៅសល់ការព្យាយាម <xliff:g id="_NUMBER_1">%d</xliff:g> ដងទៀត​មុនពេល​ស៊ីម​មិនអាច​ប្រើបាន​ជា​អចិន្ត្រៃយ៍។ ទាក់ទង​ទៅ​ក្រុមហ៊ុន​សេវា​ទូរសព្ទ​សម្រាប់ព័ត៌មានលម្អិត។</item>
- <item quantity="one">ឥឡូវនេះស៊ីមត្រូវបានបិទ។ សូមបញ្ចូលកូដ PUK ដើម្បីបន្ត។ អ្នកនៅសល់ការព្យាយាម <xliff:g id="_NUMBER_0">%d</xliff:g> ដងទៀតមុនពេលស៊ីមមិនអាចប្រើបានជាអចិន្ត្រៃយ៍។ ទាក់ទង​ទៅ​ក្រុមហ៊ុន​សេវា​ទូរសព្ទ​សម្រាប់​ព័ត៌មាន​លម្អិត។</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{បញ្ចូលកូដ PIN របស់ស៊ីម។ អ្នក​នៅសល់​ការព្យាយាម # ដង​ទៀត មុន​ពេល​ដែលអ្នក​ត្រូវទាក់ទង​ទៅ​ក្រុមហ៊ុន​សេវា​ទូរសព្ទ​របស់អ្នក​ដើម្បី​ដោះសោ​ឧបករណ៍​របស់អ្នក។}other{បញ្ចូល​កូដ PIN របស់​ស៊ីម។ អ្នកនៅ​សល់​ការ​ព្យាយាម ​# ដង​ទៀត។}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{ឥឡូវនេះស៊ីមត្រូវបានបិទ។ សូមបញ្ចូលកូដ PUK ដើម្បីបន្ត។ អ្នកនៅសល់ការព្យាយាម # ដងទៀតមុនពេលស៊ីមមិនអាចប្រើបានជាអចិន្ត្រៃយ៍។ ទាក់ទង​ទៅ​ក្រុមហ៊ុន​សេវា​ទូរសព្ទ​សម្រាប់ព័ត៌មានលម្អិត។}other{ឥឡូវនេះស៊ីមត្រូវបានបិទ។ សូមបញ្ចូលកូដ PUK ដើម្បីបន្ត។ អ្នកនៅសល់ការព្យាយាម # ដងទៀត​មុនពេល​ស៊ីម​មិនអាច​ប្រើបាន​ជា​អចិន្ត្រៃយ៍។ ទាក់ទង​ទៅ​ក្រុមហ៊ុន​សេវា​ទូរសព្ទ​សម្រាប់ព័ត៌មានលម្អិត។}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"លំនាំដើម"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"ពពុះ"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"អាណាឡូក"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index 3299872b3f3c..e5e4a5f59dfe 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"ನಿಮ್ಮ ಪಾಸ್‍‍ವರ್ಡ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ನಮೂದಿಸಿದ್ದೀರಿ. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"ನಿಮ್ಮ ಅನ್‍‍ಲಾಕ್ ಪ್ಯಾಟರ್ನ್‌ ಅನ್ನು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಎಳೆದಿದ್ದೀರಿ. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"ಸಿಮ್‌ ಪಿನ್‌ ಕೋಡ್‌ ತಪ್ಪಾಗಿದೆ, ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅನ್‌ಲಾಕ್‌ ಮಾಡಲು ನೀವು ಈ ಕೂಡಲೇ ನಿಮ್ಮ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಬೇಕು."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">ಸಿಮ್‌ ಪಿನ್ ಕೋಡ್‌ ತಪ್ಪಾಗಿದೆ, ನಿಮಗೆ <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.</item>
- <item quantity="other">ಸಿಮ್‌ ಪಿನ್ ಕೋಡ್‌ ತಪ್ಪಾಗಿದೆ, ನಿಮಗೆ <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM ಪಿನ್ ಕೋಡ್ ತಪ್ಪಾಗಿದೆ, ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅನ್‌ಲಾಕ್ ಮಾಡುವುದಕ್ಕಾಗಿ ನಿಮ್ಮ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನ ಬಾಕಿ ಉಳಿದಿದೆ.}one{SIM ಪಿನ್ ಕೋಡ್ ತಪ್ಪಾಗಿದೆ, ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ. }other{SIM ಪಿನ್ ಕೋಡ್ ತಪ್ಪಾಗಿದೆ, ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"ಸಿಮ್‌ ನಿಷ್ಪ್ರಯೋಜಕವಾಗಿದೆ. ನಿಮ್ಮ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">ಸಿಮ್‌ PUK ಕೋಡ್‌ ತಪ್ಪಾಗಿದೆ, <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳ ನಂತರ ಸಿಮ್‌ ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಪ್ರಯೋಜಕವಾಗುತ್ತದೆ.</item>
- <item quantity="other">ಸಿಮ್‌ PUK ಕೋಡ್‌ ತಪ್ಪಾಗಿದೆ, <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳ ನಂತರ ಸಿಮ್‌ ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಪ್ರಯೋಜಕವಾಗುತ್ತದೆ.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{SIM PUK ಕೋಡ್ ತಪ್ಪಾಗಿದೆ, SIM ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಪ್ರಯೋಜಕವಾಗುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನ ಬಾಕಿ ಉಳಿದಿದೆ.}one{SIM PUK ಕೋಡ್ ತಪ್ಪಾಗಿದೆ, SIM ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಪ್ರಯೋಜಕವಾಗುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.}other{SIM PUK ಕೋಡ್ ತಪ್ಪಾಗಿದೆ, SIM ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಪ್ರಯೋಜಕವಾಗುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"ಸಿಮ್‌ ಪಿನ್‌ ಕಾರ್ಯಾಚರಣೆ ವಿಫಲಗೊಂಡಿದೆ!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"ಸಿಮ್‌ PUK ಕಾರ್ಯಾಚರಣೆ ವಿಫಲಗೊಂಡಿದೆ!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ಇನ್‌ಪುಟ್‌‌ ವಿಧಾನ ಬದಲಿಸಿ"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ಸಾಧನವನ್ನು ಹಸ್ತಚಾಲಿತವಾಗಿ ಲಾಕ್‌ ಮಾಡಲಾಗಿದೆ"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"ಫೇಸ್ ಅನ್‌ಲಾಕ್ ಬಳಸಲು, ಸೆಟ್ಟಿಂಗ್ಸ್‌ನಲ್ಲಿ ಕ್ಯಾಮರಾ ಪ್ರವೇಶ ಆನ್ ಮಾಡಿ"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ. ನಿಮ್ಮಲ್ಲಿ <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.</item>
- <item quantity="other">ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ. ನಿಮ್ಮಲ್ಲಿ <xliff:g id="NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">ಸಿಮ್ ಅನ್ನು ಈಗ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. ಮುಂದುವರಿಸಲು PUK ಕೋಡ್ ನಮೂದಿಸಿ. ಸಿಮ್ ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಪ್ರಯೋಜಕವಾಗುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ <xliff:g id="_NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ. ವಿವರಗಳಿಗಾಗಿ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಿ.</item>
- <item quantity="other">ಸಿಮ್ ಅನ್ನು ಈಗ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. ಮುಂದುವರಿಸಲು PUK ಕೋಡ್ ನಮೂದಿಸಿ. ಸಿಮ್ ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಪ್ರಯೋಜಕವಾಗುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ <xliff:g id="_NUMBER_1">%d</xliff:g> ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ. ವಿವರಗಳಿಗಾಗಿ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಿ.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{SIM ಪಿನ್ ಅನ್ನು ನಮೂದಿಸಿ, ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅನ್‌ಲಾಕ್ ಮಾಡುವುದಕ್ಕಾಗಿ ನಿಮ್ಮ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನ ಬಾಕಿ ಉಳಿದಿದೆ.}one{SIM ಪಿನ್ ಅನ್ನು ನಮೂದಿಸಿ. ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.}other{SIM ಪಿನ್ ಅನ್ನು ನಮೂದಿಸಿ. ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM ಅನ್ನು ಈಗ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. ಮುಂದುವರಿಸಲು PUK ಕೋಡ್ ಅನ್ನು ನಮೂದಿಸಿ. SIM ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಪ್ರಯೋಜಕವಾಗುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನ ಬಾಕಿ ಉಳಿದಿದೆ. ವಿವರಗಳಿಗಾಗಿ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಿ.}one{SIM ಅನ್ನು ಈಗ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. ಮುಂದುವರಿಸಲು PUK ಕೋಡ್ ಅನ್ನು ನಮೂದಿಸಿ. SIM ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಪ್ರಯೋಜಕವಾಗುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ. ವಿವರಗಳಿಗಾಗಿ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಿ.}other{SIM ಅನ್ನು ಈಗ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. ಮುಂದುವರಿಸಲು PUK ಕೋಡ್ ಅನ್ನು ನಮೂದಿಸಿ. SIM ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಪ್ರಯೋಜಕವಾಗುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ. ವಿವರಗಳಿಗಾಗಿ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಿ.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"ಡೀಫಾಲ್ಟ್"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"ಬಬಲ್"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"ಅನಲಾಗ್"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml
index ebddae216413..4c1cfb7fb6a1 100644
--- a/packages/SystemUI/res-keyguard/values-ko/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"비밀번호를 <xliff:g id="NUMBER_0">%1$d</xliff:g>번 잘못 입력했습니다. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>초 후에 다시 시도하세요."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%1$d</xliff:g>번 잘못 그렸습니다. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g>초 후에 다시 시도하세요."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"잘못된 SIM PIN코드입니다. 이동통신사에 문의하여 기기를 잠금 해제해야 합니다."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">잘못된 SIM PIN 코드입니다. 입력을 <xliff:g id="NUMBER_1">%d</xliff:g>번 더 시도할 수 있습니다.</item>
- <item quantity="one">잘못된 SIM PIN 코드입니다. 입력에 <xliff:g id="NUMBER_0">%d</xliff:g>번 더 실패할 경우 이동통신사에 문의하여 기기를 잠금 해제해야 합니다.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM의 PIN 코드가 잘못되었습니다. #회 이상 입력에 실패할 경우 이동통신사에 문의하여 기기를 잠금 해제해야 합니다.}other{SIM의 PIN 코드가 잘못되었습니다. 남은 시도 횟수는 #회입니다. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM을 사용할 수 없습니다. 이동통신사에 문의하세요."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">잘못된 SIM PUK 코드입니다. 입력에 <xliff:g id="NUMBER_1">%d</xliff:g>번 더 실패할 경우 SIM이 영구적으로 사용 중지됩니다.</item>
- <item quantity="one">잘못된 SIM PUK 코드입니다. 입력에 <xliff:g id="NUMBER_0">%d</xliff:g>번 더 실패할 경우 SIM이 영구적으로 사용 중지됩니다.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{SIM의 PUK 코드가 잘못되었습니다. #회 이상 입력에 실패할 경우 SIM을 완전히 사용할 수 없게 됩니다.}other{SIM의 PUK 코드가 잘못되었습니다. #회 이상 입력에 실패할 경우 SIM을 완전히 사용할 수 없게 됩니다.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM PIN 작업이 실패했습니다."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUK 작업이 실패했습니다."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"입력 방법 전환"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"기기가 수동으로 잠금 설정되었습니다."</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"인식할 수 없음"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"얼굴 인식 잠금 해제를 사용하려면 설정에서 카메라 액세스를 사용 설정하세요."</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">SIM PIN을 입력하세요. 입력은 <xliff:g id="NUMBER_1">%d</xliff:g>번 더 시도할 수 있습니다.</item>
- <item quantity="one">SIM PIN을 입력하세요. 입력에 <xliff:g id="NUMBER_0">%d</xliff:g>번 더 실패하면 이동통신사에 문의하여 기기를 잠금 해제해야 합니다.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM이 사용 중지되었습니다. 계속하려면 PUK 코드를 입력하세요. <xliff:g id="_NUMBER_1">%d</xliff:g>번 더 실패하면 SIM을 완전히 사용할 수 없게 됩니다. 자세한 내용은 이동통신사에 문의하세요.</item>
- <item quantity="one">SIM이 사용 중지되었습니다. 계속하려면 PUK 코드를 입력하세요. <xliff:g id="_NUMBER_0">%d</xliff:g>번 더 실패하면 SIM을 완전히 사용할 수 없게 됩니다. 자세한 내용은 이동통신사에 문의하세요.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{SIM의 PIN을 입력하세요. #회 이상 입력에 실패할 경우 이동통신사에 문의하여 기기를 잠금 해제해야 합니다.}other{SIM의 PIN을 입력하세요. 남은 시도 횟수는 #회입니다.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM이 사용 중지되었습니다. 계속하려면 PUK 코드를 입력하세요. #회 이상 입력에 실패할 경우 SIM을 완전히 사용할 수 없게 됩니다. 자세한 내용은 이동통신사에 문의하세요.}other{SIM이 사용 중지되었습니다. 계속하려면 PUK 코드를 입력하세요. #회 이상 입력에 실패할 경우 SIM을 완전히 사용할 수 없게 됩니다. 자세한 내용은 이동통신사에 문의하세요.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"기본"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"버블"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"아날로그"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index f6ed69dc3958..ec276feeb9e6 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Сырсөзүңүздү <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тердиңиз. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секунддан кийин дагы аракет кылып көрүңүз."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Түзмөктү ачуучу графикалык ачкычты <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тарттыңыз. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секунддан кийин дагы аракет кылып көрүңүз."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM-картанын PIN-коду туура эмес. Эми түзмөктү бөгөттөн чыгаруу үчүн байланыш операторуңузга кайрылышыңыз керек."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">SIM-картанын PIN-коду туура эмес, сизде <xliff:g id="NUMBER_1">%d</xliff:g> аракет калды.</item>
- <item quantity="one">SIM-картанын PIN-коду туура эмес, сизде <xliff:g id="NUMBER_0">%d</xliff:g> аракет калды. Болбосо, түзмөктү бөгөттөн чыгаруу үчүн байланыш операторуңузга кайрылышыңыз керек.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM-картанын PIN коду туура эмес киргизилди. # аракет калды. Болбосо, түзмөктү бөгөттөн чыгаруу үчүн операторуңузга кайрылышыңыз керек болот.}other{SIM-картанын PIN коду туура эмес киргизилди. # аракет калды. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM-карта жараксыз. Байланыш операторуңузга кайрылыңыз."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">SIM-картанын PUK-коду туура эмес, SIM-картанын биротоло жарактан чыгаарына <xliff:g id="NUMBER_1">%d</xliff:g> аракет калды.</item>
- <item quantity="one">SIM-картанын PUK-коду туура эмес, SIM-картанын биротоло жарактан чыгаарына <xliff:g id="NUMBER_0">%d</xliff:g> аракет калды.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{SIM-картанын PUK коду туура эмес киргизилди. SIM-картанын биротоло жарактан чыгаарына # аракет калды.}other{SIM-картанын PUK коду туура эмес киргизилди. SIM-картанын биротоло жарактан чыгаарына # аракет калды.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM-картанын PIN-кодун ачуу кыйрады!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM-картанын PUK-кодун ачуу кыйрады!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Киргизүү ыкмасын өзгөртүү"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Түзмөк кол менен кулпуланды"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Таанылган жок"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Жөндөөлөрдөн камерага уруксат беришиңиз керек"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">SIM-картанын PIN кодун киргизиңиз. Сизде <xliff:g id="NUMBER_1">%d</xliff:g> аракет калды.</item>
- <item quantity="one">SIM-картанын PIN кодун киргизиңиз. Сизде <xliff:g id="NUMBER_0">%d</xliff:g> аракет калды, андан кийин түзмөктү бөгөттөн чыгаруу үчүн байланыш операторуна кайрылышыңыз керек болот.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM-карта азыр жарактан чыкты. Улантуу үчүн PUK-кодду киргизиңиз. SIM-картанын биротоло жарактан чыгарына <xliff:g id="_NUMBER_1">%d</xliff:g> аракет калды. Чоо-жайын билүү үчүн байланыш операторуна кайрылыңыз.</item>
- <item quantity="one">SIM-карта азыр жарактан чыкты. Улантуу үчүн PUK-кодду киргизиңиз. SIM-картанын биротоло жарактан чыгаарына <xliff:g id="_NUMBER_0">%d</xliff:g> аракет калды. Чоо-жайын билүү үчүн байланыш операторуна кайрылыңыз.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{SIM-картанын PIN кодун киргизиңиз. Сизде # аракет калды, андан кийин түзмөктү бөгөттөн чыгаруу үчүн байланыш операторуна кайрылышыңыз керек болот.}other{SIM-картанын PIN кодун киргизиңиз. # аракет калды.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM-карта азыр жарактан чыкты. Улантуу үчүн PUK кодду киргизиңиз. SIM-картанын биротоло жарактан чыгаарына # аракет калды. Чоо-жайын билүү үчүн байланыш операторуна кайрылыңыз.}other{SIM-карта азыр жарактан чыкты. Улантуу үчүн PUK кодду киргизиңиз. SIM-картанын биротоло жарактан чыгарына # аракет калды. Чоо-жайын билүү үчүн байланыш операторуна кайрылыңыз.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Демейки"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Көбүк"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Аналог"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml
index 1948f233fa41..44051d8d1bce 100644
--- a/packages/SystemUI/res-keyguard/values-lo/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"ທ່ານພິມລະຫັດຜ່ານຜິດ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. \n\nໃຫ້ລອງໃໝ່ອີກຄັ້ງໃນອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ວິນາທີ."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"ທ່ານແຕ້ມຮູບແບບປົດລັອກບໍ່ຖືກ <xliff:g id="NUMBER_0">%1$d</xliff:g> ເທື່ອແລ້ວ. \n\nລອງໃໝ່ໃນອີກ <xliff:g id="NUMBER_1">%2$d</xliff:g> ວິນາທີ."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"ລະຫັດ PIN ຂອງ SIM ບໍ່ຖືກຕ້ອງທ່ານຕ້ອງຕິດຕໍ່ຫາຜູ່ໃຫ້ບໍລິການ ເພື່ອປົດລັອກອຸປະກອນຂອງທ່ານ."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">ລະຫັດ SIM PIN ບໍ່ຖືກຕ້ອງ, ທ່ານຍັງພະຍາຍາມໄດ້ອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ຄັ້ງ.</item>
- <item quantity="one">ລະຫັດ PIN ຂອງ SIM ບໍ່ຖືກຕ້ອງ, ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອກ່ອນທີ່ທ່ານຈະຕ້ອງຕິດຕໍ່ຫາຜູ່ໃຫ້ບໍລິການຂອງທ່ານ ເພື່ອປົດລັອກອຸປະກອນຂອງທ່ານ.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{ລະຫັດ PIN ຂອງຊິມບໍ່ຖືກຕ້ອງ, ທ່ານສາມາດລອງໄດ້ອີກ # ເທື່ອກ່ອນທີ່ທ່ານຈະຕ້ອງຕິດຕໍ່ຫາຜູ້ໃຫ້ບໍລິການຂອງທ່ານເພື່ອປົດລັອກອຸປະກອນທ່ານ.}other{ລະຫັດ PIN ຂອງຊິມບໍ່ຖືກຕ້ອງ, ທ່ານສາມາດລອງໄດ້ອີກ # ເທື່ອ. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM ໃຊ້ບໍ່ໄດ້ແລ້ວ. ກະລຸນາຕິດຕໍ່ຫາຜູ່ໃຫ້ບໍລິການຂອງທ່ານ."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">ລະຫັດ PUK ຂອງ SIM ບໍ່ຖືກຕ້ອງ, ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ເທື່ອກ່ອນທີ່ SIM ຂອງທ່ານຈະໃຊ້ບໍ່ໄດ້ຢ່າງຖາວອນ.</item>
- <item quantity="one">ລະຫັດ PUK ຂອງ SIM ບໍ່ຖືກຕ້ອງ, ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອກ່ອນທີ່ SIM ຂອງທ່ານຈະໃຊ້ບໍ່ໄດ້ຢ່າງຖາວອນ.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{ລະຫັດ PUK ຂອງຊິມບໍ່ຖືກຕ້ອງ, ທ່ານສາມາດລອງໄດ້ອີກ # ເທື່ອກ່ອນທີ່ຊິມຂອງທ່ານຈະບໍ່ສາມາດໃຊ້ໄດ້ຖາວອນ.}other{ລະຫັດ PUK ຂອງຊິມບໍ່ຖືກຕ້ອງ, ທ່ານສາມາດລອງໄດ້ອີກ # ເທື່ອກ່ອນທີ່ຊິມຂອງທ່ານຈະບໍ່ສາມາດໃຊ້ໄດ້ຖາວອນ.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"PIN ຂອງ SIM ເຮັດວຽກລົ້ມເຫຼວ!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"PUK ຂອງ SIM ເຮັດວຽກລົ້ມເຫຼວ!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ສະລັບຮູບແບບການປ້ອນຂໍ້ມູນ"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ອຸປະກອນຖືກສັ່ງໃຫ້ລັອກ"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"ບໍ່ຮູ້ຈັກ"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"ເພື່ອໃຊ້ການປົດລັອກດ້ວຍໜ້າ, ໃຫ້ເປີດໃຊ້ສິດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບໃນການຕັ້ງຄ່າ"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">ລະຫັດ SIM PIN ບໍ່ຖືກຕ້ອງ. ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="NUMBER_1">%d</xliff:g> ເທື່ອ.</item>
- <item quantity="one">ໃສ່ລະຫັດ SIM PIN. ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="NUMBER_0">%d</xliff:g> ເທື່ອກ່ອນທີ່ຈະຕ້ອງຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການເພື່ອປົດລັອກ.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">ຕອນນີ້ປິດການນຳໃຊ້ SIM ແລ້ວ. ໃສ່ລະຫັດ PUK ເພື່ອດຳເນີນການຕໍ່. ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="_NUMBER_1">%d</xliff:g> ເທື່ອກ່ອນທີ່ SIM ຈະບໍ່ສາມາດໃຊ້ໄດ້ຖາວອນ. ກະລຸນາຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການສຳລັບລາຍລະອຽດ.</item>
- <item quantity="one">ຕອນນີ້ປິດການນຳໃຊ້ SIM ແລ້ວ. ໃສ່ລະຫັດ PUK ເພື່ອດຳເນີນການຕໍ່. ທ່ານສາມາດລອງໄດ້ອີກ <xliff:g id="_NUMBER_0">%d</xliff:g> ເທື່ອກ່ອນທີ່ SIM ຈະບໍ່ສາມາດໃຊ້ໄດ້ຖາວອນ. ກະລຸນາຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການສຳລັບລາຍລະອຽດ.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{ໃສ່ລະຫັດ PIN ຂອງຊິມ. ທ່ານສາມາດລອງໄດ້ອີກ # ເທື່ອກ່ອນທີ່ຈະຕ້ອງຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການຂອງທ່ານເພື່ອປົດລັອກອຸປະກອນທ່ານ.}other{ໃສ່ລະຫັດ PIN ຂອງຊິມ. ທ່ານສາມາດລອງໄດ້ອີກ # ເທື່ອ.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{ຕອນນີ້ປິດການນຳໃຊ້ຊິມແລ້ວ. ໃສ່ລະຫັດ PUK ເພື່ອສືບຕໍ່. ທ່ານສາມາດລອງໄດ້ອີກ # ເທື່ອກ່ອນທີ່ຊິມຈະບໍ່ສາມາດໃຊ້ໄດ້ຖາວອນ. ກະລຸນາຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການສຳລັບລາຍລະອຽດ.}other{ຕອນນີ້ປິດການນຳໃຊ້ຊິມແລ້ວ. ໃສ່ລະຫັດ PUK ເພື່ອສືບຕໍ່. ທ່ານສາມາດລອງໄດ້ອີກ # ເທື່ອກ່ອນທີ່ຊິມຈະບໍ່ສາມາດໃຊ້ໄດ້ຖາວອນ. ກະລຸນາຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການສຳລັບລາຍລະອຽດ.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"ຄ່າເລີ່ມຕົ້ນ"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"ຟອງ"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"ໂມງເຂັມ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lt/strings.xml b/packages/SystemUI/res-keyguard/values-lt/strings.xml
index 59135a9ab3f8..26c54c0a7008 100644
--- a/packages/SystemUI/res-keyguard/values-lt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lt/strings.xml
@@ -68,19 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"<xliff:g id="NUMBER_0">%1$d</xliff:g> kart. netinkamai įvedėte slaptažodį. \n\nBandykite dar kartą po <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"<xliff:g id="NUMBER_0">%1$d</xliff:g> kart. netinkamai nupiešėte atrakinimo piešinį. \n\nBandykite dar kartą po <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Netinkamas SIM kortelės PIN kodas. Reikės susisiekti su operatoriumi, kad atrakintų įrenginį."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Netinkamas SIM kortelės PIN kodas. Liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymas.</item>
- <item quantity="few">Netinkamas SIM kortelės PIN kodas. Liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymai.</item>
- <item quantity="many">Netinkamas SIM kortelės PIN kodas. Liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymo.</item>
- <item quantity="other">Netinkamas SIM kortelės PIN kodas. Liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymų.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Netinkamas SIM kortelės PIN kodas. Jums liko # bandymas. Paskui reikės susisiekti su operatoriumi, kad atrakintumėte įrenginį.}one{Netinkamas SIM kortelės PIN kodas. Jums liko # bandymas. }few{Netinkamas SIM kortelės PIN kodas. Jums liko # bandymai. }many{Netinkamas SIM kortelės PIN kodas. Jums liko # bandymo. }other{Netinkamas SIM kortelės PIN kodas. Jums liko # bandymų. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM kortelės naudoti nebegalima. Susisiekite su operatoriumi."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Netinkamas SIM kortelės PUK kodas. Liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymas. Paskui visiškai nebegalėsite naudoti SIM kortelės.</item>
- <item quantity="few">Netinkamas SIM kortelės PUK kodas. Liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymai. Paskui visiškai nebegalėsite naudoti SIM kortelės.</item>
- <item quantity="many">Netinkamas SIM kortelės PUK kodas. Liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymo. Paskui visiškai nebegalėsite naudoti SIM kortelės.</item>
- <item quantity="other">Netinkamas SIM kortelės PUK kodas. Liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymų. Paskui visiškai nebegalėsite naudoti SIM kortelės.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Netinkamas SIM kortelės PUK kodas. Jums liko # bandymas. Paskui visiškai nebegalėsite naudoti SIM kortelės.}one{Netinkamas SIM kortelės PUK kodas. Jums liko # bandymas. Paskui visiškai nebegalėsite naudoti SIM kortelės.}few{Netinkamas SIM kortelės PUK kodas. Jums liko # bandymai. Paskui visiškai nebegalėsite naudoti SIM kortelės.}many{Netinkamas SIM kortelės PUK kodas. Jums liko # bandymo. Paskui visiškai nebegalėsite naudoti SIM kortelės.}other{Netinkamas SIM kortelės PUK kodas. Jums liko # bandymų. Paskui visiškai nebegalėsite naudoti SIM kortelės.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Nepavyko atlikti SIM kortelės PIN kodo operacijos."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Nepavyko atlikti SIM kortelės PUK kodo operacijos."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Perjungti įvesties metodą"</string>
@@ -95,18 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Įrenginys užrakintas neautomatiškai"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Neatpažinta"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Jei nor. naud. atr. pagal veidą, įj. pr. prie fotoap. sk. „Nustatymai“"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymas.</item>
- <item quantity="few">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymai.</item>
- <item quantity="many">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymo.</item>
- <item quantity="other">Įveskite SIM kortelės PIN kodą. Jums liko <xliff:g id="NUMBER_1">%d</xliff:g> bandymų.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko <xliff:g id="_NUMBER_1">%d</xliff:g> bandymas. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.</item>
- <item quantity="few">SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko <xliff:g id="_NUMBER_1">%d</xliff:g> bandymai. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.</item>
- <item quantity="many">SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko <xliff:g id="_NUMBER_1">%d</xliff:g> bandymo. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.</item>
- <item quantity="other">SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko <xliff:g id="_NUMBER_1">%d</xliff:g> bandymų. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Įveskite SIM kortelės PIN kodą. Jums liko # bandymas. Paskui reikės susisiekti su operatoriumi, kad atrakintumėte įrenginį.}one{Įveskite SIM kortelės PIN kodą. Jums liko # bandymas.}few{Įveskite SIM kortelės PIN kodą. Jums liko # bandymai.}many{Įveskite SIM kortelės PIN kodą. Jums liko # bandymo.}other{Įveskite SIM kortelės PIN kodą. Jums liko # bandymų.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko # bandymas. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.}one{SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko # bandymas. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.}few{SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko # bandymai. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.}many{SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko # bandymo. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.}other{SIM kortelė dabar yra išjungta. Jei norite tęsti, įveskite PUK kodą. Jums liko # bandymų. Paskui visiškai nebegalėsite naudoti SIM kortelės. Jei reikia išsamios informacijos, susisiekite su operatoriumi.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Numatytasis"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Debesėlis"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analoginis"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lv/strings.xml b/packages/SystemUI/res-keyguard/values-lv/strings.xml
index 88bb11422d74..fb913ea3864e 100644
--- a/packages/SystemUI/res-keyguard/values-lv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lv/strings.xml
@@ -68,17 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Jūs <xliff:g id="NUMBER_0">%1$d</xliff:g> reizi(-es) esat ievadījis nepareizu paroli.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundes(-ēm)."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Jūs <xliff:g id="NUMBER_0">%1$d</xliff:g> reizi(-es) esat nepareizi uzzīmējis atbloķēšanas kombināciju.\n\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%2$d</xliff:g> sekundes(-ēm)."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Nepareizs SIM kartes PIN kods. Lai atbloķētu ierīci, sazinieties ar mobilo sakaru operatoru."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="zero">Nepareizs SIM kartes PIN kods. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizes.</item>
- <item quantity="one">Nepareizs SIM kartes PIN kods. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizi.</item>
- <item quantity="other">Nepareizs SIM kartes PIN kods. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizes.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Nepareizs SIM kartes PIN. Varat mēģināt vēl # reizi. Kļūdas gadījumā būs jāsazinās ar mobilo sakaru operatoru, lai tas atbloķētu jūsu ierīci.}zero{Nepareizs SIM kartes PIN. Varat mēģināt vēl # reizes. }one{Nepareizs SIM kartes PIN. Varat mēģināt vēl # reizi. }other{Nepareizs SIM kartes PIN. Varat mēģināt vēl # reizes. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM karte nav lietojama. Sazinieties ar mobilo sakaru operatoru."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="zero">Nepareizs SIM kartes PUK kods. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizes. Ja pēdējais mēģinājums būs kļūdains, SIM karti vairs nevarēs izmantot.</item>
- <item quantity="one">Nepareizs SIM kartes PUK kods. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizi. Ja pēdējais mēģinājums būs kļūdains, SIM karti vairs nevarēs izmantot.</item>
- <item quantity="other">Nepareizs SIM kartes PUK kods. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizes. Ja pēdējais mēģinājums būs kļūdains, SIM karti vairs nevarēs izmantot.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Nepareizs SIM kartes PUK kods. Varat mēģināt vēl # reizi. Kļūdas gadījumā SIM karti vairs nevarēs izmantot.}zero{Nepareizs SIM kartes PUK kods. Varat mēģināt vēl # reizes. Ja pēdējais mēģinājums būs kļūdains, SIM karti vairs nevarēs izmantot.}one{Nepareizs SIM kartes PUK kods. Varat mēģināt vēl # reizi. Ja pēdējais mēģinājums būs kļūdains, SIM karti vairs nevarēs izmantot.}other{Nepareizs SIM kartes PUK kods. Varat mēģināt vēl # reizes. Ja pēdējais mēģinājums būs kļūdains, SIM karti vairs nevarēs izmantot.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM kartes PIN koda ievadīšana neizdevās!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM kartes PUK koda ievadīšana neizdevās!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Pārslēgt ievades metodi"</string>
@@ -93,16 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Ierīce tika bloķēta manuāli."</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nav atpazīts"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Lai izmantotu autorizāciju pēc sejas, iestatījumos ieslēdziet piekļuvi kamerai."</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="zero">Ievadiet SIM kartes PIN. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizes.</item>
- <item quantity="one">Ievadiet SIM kartes PIN. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizi.</item>
- <item quantity="other">Ievadiet SIM kartes PIN. Varat mēģināt vēl <xliff:g id="NUMBER_1">%d</xliff:g> reizes.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="zero">SIM karte tagad ir atspējota. Ievadiet PUK kodu, lai turpinātu. Varat mēģināt vēl <xliff:g id="_NUMBER_1">%d</xliff:g> reizes. Kļūdas gadījumā SIM karti vairs nevarēs izmantot. Lai iegūtu detalizētu informāciju, sazinieties ar mobilo sakaru operatoru.</item>
- <item quantity="one">SIM karte tagad ir atspējota. Ievadiet PUK kodu, lai turpinātu. Varat mēģināt vēl <xliff:g id="_NUMBER_1">%d</xliff:g> reizi. Kļūdas gadījumā SIM karti vairs nevarēs izmantot. Lai iegūtu detalizētu informāciju, sazinieties ar mobilo sakaru operatoru.</item>
- <item quantity="other">SIM karte tagad ir atspējota. Ievadiet PUK kodu, lai turpinātu. Varat mēģināt vēl <xliff:g id="_NUMBER_1">%d</xliff:g> reizes. Kļūdas gadījumā SIM karti vairs nevarēs izmantot. Lai iegūtu detalizētu informāciju, sazinieties ar mobilo sakaru operatoru.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Ievadiet SIM kartes PIN. Varat mēģināt vēl # reizi. Kļūdas gadījumā jums būs jāsazinās ar mobilo sakaru operatoru, lai atbloķētu savu ierīci.}zero{Ievadiet SIM kartes PIN. Jums ir atlikuši # mēģinājumi.}one{Ievadiet SIM kartes PIN. Jums ir atlicis # mēģinājums.}other{Ievadiet SIM kartes PIN. Jums ir atlikuši # mēģinājumi.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM karte tagad ir atspējota. Ievadiet PUK kodu, lai turpinātu. Varat mēģināt vēl # reizi. Kļūdas gadījumā SIM karti vairs nevarēs izmantot. Lai iegūtu detalizētu informāciju, sazinieties ar mobilo sakaru operatoru.}zero{SIM karte tagad ir atspējota. Ievadiet PUK kodu, lai turpinātu. Varat mēģināt vēl # reizes. Kļūdas gadījumā SIM karti vairs nevarēs izmantot. Lai iegūtu detalizētu informāciju, sazinieties ar mobilo sakaru operatoru.}one{SIM karte tagad ir atspējota. Ievadiet PUK kodu, lai turpinātu. Varat mēģināt vēl # reizi. Kļūdas gadījumā SIM karti vairs nevarēs izmantot. Lai iegūtu detalizētu informāciju, sazinieties ar mobilo sakaru operatoru.}other{SIM karte tagad ir atspējota. Ievadiet PUK kodu, lai turpinātu. Varat mēģināt vēl # reizes. Kļūdas gadījumā SIM karti vairs nevarēs izmantot. Lai iegūtu detalizētu informāciju, sazinieties ar mobilo sakaru operatoru.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Noklusējums"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Burbuļi"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogais"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml
index 7eb7a00ae013..9769c7e17380 100644
--- a/packages/SystemUI/res-keyguard/values-mk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Погрешно сте ја напишале вашата лозинка <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Погрешно сте ја нацртале вашата шема за отклучување <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Погрешен PIN-код за SIM, сега мора да контактирате со вашиот оператор за да го отклучите уредот."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Погрешен PIN-код за SIM, ви преостанува уште <xliff:g id="NUMBER_1">%d</xliff:g> обид.</item>
- <item quantity="other">Погрешен PIN-код за SIM, ви преостануваат уште <xliff:g id="NUMBER_1">%d</xliff:g> обиди.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Погрешен PIN-код за SIM-картичката. Ви преостанува уште # обид пред да мора да контактирате со вашиот оператор за да го отклучи уредот.}one{Погрешен PIN-код за SIM, ви преостанува уште # обид. }other{Погрешен PIN-код за SIM, ви преостануваат уште # обиди. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM-картичката е неупотреблива. Контактирајте со вашиот оператор."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Погрешен PUK-код за SIM, ви преостанува уште <xliff:g id="NUMBER_1">%d</xliff:g> обид пред SIM-картичката да стане трајно неупотреблива.</item>
- <item quantity="other">Погрешен PUK-код за SIM, ви преостануваат уште <xliff:g id="NUMBER_1">%d</xliff:g> обиди пред SIM-картичката да стане трајно неупотреблива.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{ПУК кодот за SIM-картичката е неточен. Ви преостанува уште # обид, а потоа SIM-картичката ќе стане трајно неупотреблива.}one{Погрешен PUK-код за SIM, ви преостанува уште # обид пред SIM-картичката да стане трајно неупотреблива.}other{Погрешен PUK-код за SIM, ви преостануваат уште # обиди пред SIM-картичката да стане трајно неупотреблива.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM-картичката не се отклучи со PIN-кодот!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM-картичката не се отклучи со PUK-кодот!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Префрли метод за внесување"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Уредот е заклучен рачно"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Непознат"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"За „Отклучување со лик“, вклучете пристап до камерата"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Внесете PIN-код за SIM-картичката. Ви преостанува уште <xliff:g id="NUMBER_1">%d</xliff:g> обид.</item>
- <item quantity="other">Внесете PIN-код за SIM-картичката. Ви преостануваат уште <xliff:g id="NUMBER_1">%d</xliff:g> обиди.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">SIM-картичката сега е оневозможена. Внесете PUK-код за да продолжите. Ви преостанува уште <xliff:g id="_NUMBER_1">%d</xliff:g> обид пред SIM-картичката да стане трајно неупотреблива. Контактирајте го операторот за детали.</item>
- <item quantity="other">SIM-картичката сега е оневозможена. Внесете PUK-код за да продолжите. Ви преостануваат уште <xliff:g id="_NUMBER_1">%d</xliff:g> обиди пред SIM-картичката да стане трајно неупотреблива. Контактирајте го операторот за детали.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Внесете PIN-код за SIM-картичката. Ви преостанува уште # обид пред да мора да контактирате со вашиот оператор да го отклучи уредот.}one{Внесете PIN на SIM. Имате уште # обид.}other{Внесете PIN на SIM. Имате уште # обиди.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM-картичката сега е оневозможена. Внесете PUK-код за да продолжите. Ви преостанува уште # обид пред SIM-картичката да стане трајно неупотреблива. Контактирајте го операторот за детали.}one{SIM-картичката сега е оневозможена. Внесете PUK-код за да продолжите. Ви преостанува уште # обид пред SIM-картичката да стане трајно неупотреблива. Контактирајте го операторот за детали.}other{SIM-картичката сега е оневозможена. Внесете PUK-код за да продолжите. Ви преостануваат уште # обиди пред SIM-картичката да стане трајно неупотреблива. Контактирајте го операторот за детали.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Стандарден"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Балонче"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Аналоген"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml
index 32bf03c252b3..e1d3f8209695 100644
--- a/packages/SystemUI/res-keyguard/values-ml/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"നിങ്ങൾ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ നിങ്ങളുടെ പാസ്‌വേഡ് ‌തെറ്റായി ‌ടൈപ്പുചെയ്തു. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> സെക്കന്റിനു‌ശേഷം വീണ്ടും ശ്രമിക്കുക."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"നിങ്ങൾ <xliff:g id="NUMBER_0">%1$d</xliff:g> തവണ അൺലോക്ക് പാറ്റേൺ ‌തെറ്റായി ‌വരച്ചു. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> സെക്കന്റിനു‌ശേഷം വീണ്ടും ശ്രമിക്കുക."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"സിം പിൻ കോഡ് തെറ്റാണ്, നിങ്ങളുടെ ഉപകരണം അൺലോക്കുചെയ്യാൻ ഇനി നിങ്ങളുടെ കാരിയറുമായി ബന്ധപ്പെടണം."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">സിം പിൻ കോഡ് തെറ്റാണ്, നിങ്ങൾക്ക് <xliff:g id="NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി ശേഷിക്കുന്നു.</item>
- <item quantity="one">സിം പിൻ കോഡ് തെറ്റാണ്, ഉപകരണം അൺലോക്കുചെയ്യാൻ സേവനദാതാവുമായി ബന്ധപ്പെടേണ്ടിവരുന്നതിനു മുമ്പായി നിങ്ങൾക്ക് <xliff:g id="NUMBER_0">%d</xliff:g> ശ്രമം കൂടി ശേഷിക്കുന്നു.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{സിം പിൻ കോഡ് തെറ്റാണ്, # ശ്രമം ശേഷിക്കുന്നു, അതുകഴിഞ്ഞാൽ ഉപകരണം അൺലോക്ക് ചെയ്യാൻ സേവനദാതാവിനെ ബന്ധപ്പെണം.}other{സിം പിൻ കോഡ് തെറ്റാണ്, # ശ്രമങ്ങൾ ശേഷിക്കുന്നു. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"സിം ഉപയോഗയോഗ്യമല്ല. നിങ്ങളുടെ കാരിയറെ ബന്ധപ്പെടുക."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">സിം PUK കോഡ് തെറ്റാണ്, സിം ശാശ്വതമായി ഉപയോഗശൂന്യമാകുന്നതിന് മുമ്പായി നിങ്ങൾക്ക് <xliff:g id="NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി ശേഷിക്കുന്നു.</item>
- <item quantity="one">സിം PUK കോഡ് തെറ്റാണ്, സിം ശാശ്വതമായി ഉപയോഗശൂന്യമാകുന്നതിന് മുമ്പായി നിങ്ങൾക്ക് <xliff:g id="NUMBER_0">%d</xliff:g> ശ്രമം കൂടി ശേഷിക്കുന്നു.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{സിം PUK കോഡ് തെറ്റാണ്, # ശ്രമം ശേഷിക്കുന്നു, അതുകഴിഞ്ഞാൽ സിം ശാശ്വതമായി ഉപയോഗശൂന്യമാകും.}other{സിം PUK കോഡ് തെറ്റാണ്, # ശ്രമങ്ങൾ ശേഷിക്കുന്നു, അതുകഴിഞ്ഞാൽ സിം ശാശ്വതമായി ഉപയോഗശൂന്യമാകും.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"പിൻ ഉപയോഗിച്ച് സിം അൺലോക്കു‌ചെയ്യാനുള്ള ‌ശ്രമം പരാജയപ്പെട്ടു!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"PUK ഉപയോഗിച്ച് സിം അൺലോക്കു‌ചെയ്യാനുള്ള ‌ശ്രമം പരാജയപ്പെട്ടു!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ഇൻപുട്ട് രീതി മാറുക"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ഉപകരണം നേരിട്ട് ലോക്കുചെയ്തു"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"തിരിച്ചറിയുന്നില്ല"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"ഫെയ്സ് അൺലോക്കിന് ക്രമീകരണത്തിൽ ക്യാമറാ ആക്സസ് ഓണാക്കൂ"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">സിം പിൻ നൽകുക. നിങ്ങൾക്ക് <xliff:g id="NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി ശേഷിക്കുന്നു.</item>
- <item quantity="one">സിം പിൻ നൽകുക. ഉപകരണം അൺലോക്ക് ചെയ്യാൻ കാരിയറുമായി ബന്ധപ്പെടേണ്ടിവരുന്നതിന് മുമ്പ് <xliff:g id="NUMBER_0">%d</xliff:g> ശ്രമം കൂടി ശേഷിക്കുന്നു.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">സിം ഇപ്പോൾ പ്രവർത്തനരഹിതമാക്കി. തുടരുന്നതിന് PUK കോഡ് നൽകുക. സിം ശാശ്വതമായി ഉപയോഗശൂന്യമാകുന്നതിന് മുമ്പായി <xliff:g id="_NUMBER_1">%d</xliff:g> ശ്രമങ്ങൾ കൂടി ശേഷിക്കുന്നു. വിശദാംശങ്ങൾക്ക് കാരിയറുമായി ബന്ധപ്പെടുക.</item>
- <item quantity="one">സിം ഇപ്പോൾ പ്രവർത്തനരഹിതമാക്കി. തുടരുന്നതിന് PUK കോഡ് നൽകുക. സിം ശാശ്വതമായി ഉപയോഗശൂന്യമാകുന്നതിന് മുമ്പായി <xliff:g id="_NUMBER_0">%d</xliff:g> ശ്രമം കൂടി ശേഷിക്കുന്നു. വിശദാംശങ്ങൾക്ക് കാരിയറുമായി ബന്ധപ്പെടുക.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{സിം പിൻ നൽകുക. # ശ്രമം ശേഷിക്കുന്നു, അതുകഴിഞ്ഞാൽ ഉപകരണം അൺലോക്ക് ചെയ്യാൻ സേവനദാതാവിനെ ബന്ധപ്പെടണം.}other{സിം പിൻ നൽകുക. # ശ്രമങ്ങൾ ശേഷിക്കുന്നു.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{സിം ഇപ്പോൾ പ്രവർത്തനരഹിതമായിരിക്കുന്നു. തുടരുന്നതിന് PUK കോഡ് നൽകുക. # ശ്രമം ശേഷിക്കുന്നു, അതുകഴിഞ്ഞാൽ സിം ശാശ്വതമായി ഉപയോഗശൂന്യമാകും. വിശദാംശങ്ങൾക്ക് സേവനദാതാവിനെ ബന്ധപ്പെടുക.}other{സിം ഇപ്പോൾ പ്രവർത്തനരഹിതമായിരിക്കുന്നു. തുടരുന്നതിന് PUK കോഡ് നൽകുക. # ശ്രമങ്ങൾ ശേഷിക്കുന്നു, അതുകഴിഞ്ഞാൽ സിം ശാശ്വതമായി ഉപയോഗശൂന്യമാകും. വിശദാംശങ്ങൾക്ക് സേവനദാതാവിനെ ബന്ധപ്പെടുക.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"ഡിഫോൾട്ട്"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"ബബിൾ"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"അനലോഗ്"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml
index ba167cf1bc24..8c42abcb1355 100644
--- a/packages/SystemUI/res-keyguard/values-mn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Та нууц үгээ <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу орууллаа. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Та тайлах хээг <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу орууллаа. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секундын дараа дахин оролдоно уу."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM-н ПИН кодыг буруу оруулсан тул та төхөөрөмжийнхөө түгжээг тайлахын тулд оператор компанитайгаа холбогдоно уу."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">SIM-н ПИН код буруу байна. Танд <xliff:g id="NUMBER_1">%d</xliff:g> оролдлого үлдлээ.</item>
- <item quantity="one">SIM-н ПИН код буруу байна, танд мобайл оператортойгоо холбогдохгүйгээр төхөөрөмжийн түгжээг тайлахад <xliff:g id="NUMBER_0">%d</xliff:g> оролдлого хийх боломж үлдсэн байна.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM-н ПИН код буруу байна, танд оператор компанитайгаа холбогдохгүйгээр төхөөрөмжийн түгжээг тайлах # оролдлого үлдлээ.}other{SIM-н ПИН код буруу байна, танд # оролдлого үлдлээ. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM-г ашиглах боломжгүй байна. Оператор компанитайгаа холбогдоно уу."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">SIM-н PUK код буруу байна. Таны SIM бүрмөсөн хаагдах хүртэл танд <xliff:g id="NUMBER_1">%d</xliff:g> оролдлого үлдлээ.</item>
- <item quantity="one">SIM-н PUK код буруу байна. Таны SIM бүрмөсөн хаагдах хүртэл танд <xliff:g id="NUMBER_0">%d</xliff:g> оролдлого үлдлээ.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{SIM-н PUK код буруу байна, таны SIM бүрмөсөн хүчингүй болох хүртэл # оролдлого үлдлээ.}other{SIM-н PUK код буруу байна, таны SIM бүрмөсөн хүчингүй болох хүртэл # оролдлого үлдлээ.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM-н ПИН-г буруу орууллаа!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM-н PUK-г буруу орууллаа!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Оруулах аргыг сэлгэх"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Төхөөрөмжийг гараар түгжсэн"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Таньж чадсангүй"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Царайгаар түгжээ тайлахыг ашиглахын тулд Тохиргоо хэсэгт камерын хандалтыг асаана уу"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">SIM-н ПИН кодыг оруулна уу. Танд <xliff:g id="NUMBER_1">%d</xliff:g> оролдлого үлдлээ.</item>
- <item quantity="one">SIM-н ПИН кодыг оруулна уу. Танд оператор компанитайгаа холбогдохгүйгээр төхөөрөмжийн түгжээг тайлах <xliff:g id="NUMBER_0">%d</xliff:g> оролдлого үлдлээ.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM-г идэвхгүй болголоо. Үргэлжлүүлэхийн тулд PUK кодыг оруулна уу. Таны SIM бүрмөсөн хүчингүй болох хүртэл <xliff:g id="_NUMBER_1">%d</xliff:g> оролдлого үлдлээ. Дэлгэрэнгүй мэдээлэл авахын тулд оператор компанитайгаа холбогдоно уу.</item>
- <item quantity="one">SIM-г идэвхгүй болголоо. Үргэлжлүүлэхийн тулд PUK кодыг оруулна уу. Таны SIM бүрмөсөн хүчингүй болох хүртэл <xliff:g id="_NUMBER_0">%d</xliff:g> оролдлого үлдлээ. Дэлгэрэнгүй мэдээлэл авахын тулд оператор компанитайгаа холбогдоно уу.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{SIM-н ПИН-г оруулна уу. Танд оператор компанитайгаа холбогдохгүйгээр төхөөрөмжийн түгжээг тайлах # оролдлого үлдлээ.}other{SIM-н ПИН-г оруулна уу. Танд # оролдлого үлдлээ.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM-г идэвхгүй болголоо. Үргэлжлүүлэхийн тулд PUK кодыг оруулна уу. Таны SIM бүрмөсөн хүчингүй болох хүртэл # оролдлого үлдлээ. Дэлгэрэнгүй мэдээлэл авахын тулд оператор компанитайгаа холбогдоно уу.}other{SIM-г идэвхгүй болголоо. Үргэлжлүүлэхийн тулд PUK кодыг оруулна уу. Таны SIM бүрмөсөн хүчингүй болох хүртэл # оролдлого үлдлээ. Дэлгэрэнгүй мэдээлэл авахын тулд оператор компанитайгаа холбогдоно уу.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Өгөгдмөл"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Бөмбөлөг"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Aналог"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml
index 28368f9038dd..2c0bf0fc950e 100644
--- a/packages/SystemUI/res-keyguard/values-mr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"तुम्ही तुमचा पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीच्या पद्धतीने टाइप केला आहे. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"तुम्ही तुमचा अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने काढला. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"सिम पिन कोड चुकीचा आहे तुम्ही आता तुमचे डिव्हाइस अनलॉक करण्‍यासाठी तुमच्या वाहकाशी संपर्क साधावा."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">चुकीचा सिम पिन कोड, तुमच्याकडे <xliff:g id="NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहेत.</item>
- <item quantity="one"> चुकीचा सिम पिन कोड, तुमचे डिव्हाइस अनलॉक करण्‍यासाठी तुमच्या वाहकाशी संपर्क साधण्‍यापूर्वी तुमच्याकडे <xliff:g id="NUMBER_0">%d</xliff:g> प्रयत्न शिल्लक आहेत.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{सिमचा पिन कोड चुकीचा आहे, तुम्ही तुमचे डिव्‍हाइस अनलॉक करण्‍यासाठी तुमच्या वाहकाशी संपर्क साधण्‍यापूर्वी तुमच्याकडे # प्रयत्न शिल्लक आहे.}other{सिमचा पिन कोड चुकीचा आहे, तुमच्याकडे # प्रयत्न शिल्लक आहेत. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"सिम निरुपयोगी आहे. आपल्या वाहकाशी संपर्क साधा."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">चुकीचा सिम PUK कोड, सिम कायमचे निरुपयोगी होण्यापूर्वी आपल्याकडे <xliff:g id="NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहेत.</item>
- <item quantity="one">चुकीचा सिम PUK कोड, सिम कायमचे निरुपयोगी होण्यापूर्वी आपल्याकडे <xliff:g id="NUMBER_0">%d</xliff:g> प्रयत्न शिल्लक आहे.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{सिमचा PUK कोड चुकीचा आहे, सिम कायमचे निरुपयोगी होण्‍यापूर्वी तुमच्याकडे # प्रयत्न शिल्लक आहे.}other{सिमचा PUK कोड चुकीचा आहे, सिम कायमचे निरुपयोगी होण्‍यापूर्वी तुमच्याकडे # प्रयत्न शिल्लक आहेत.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"सिम पिन ऑपरेशन अयशस्वी झाले!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"सिम PUK कार्य अयशस्‍वी झाले!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"इनपुट पद्धत स्विच करा"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"डिव्हाइस मॅन्युअली लॉक केले होते"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"ओळखले नाही"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"फेस अनलॉक साठी, सेटिंग्ज मध्ये कॅमेराचा अ‍ॅक्सेस द्या"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">सिम पिन एंटर करा, तुमच्याकडे <xliff:g id="NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहेत.</item>
- <item quantity="one">सिम पिन एंटर करा. तुम्ही तुमचे डिव्‍हाइस अनलॉक करण्‍यासाठी तुमच्या वाहकाशी संपर्क साधण्‍यापूर्वी, तुमच्याकडे <xliff:g id="NUMBER_0">%d</xliff:g> प्रयत्न शिल्लक आहे.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">सिम आता बंद केलेले आहे. सुरू ठेवण्यासाठी PUK कोड टाका. सिम कायमचे बंद होण्याआधी तुमच्याकडे <xliff:g id="_NUMBER_1">%d</xliff:g> प्रयत्न शिल्लक आहेत. तपशीलांसाठी वाहकाशी संपर्क साधा.</item>
- <item quantity="one">सिम आता बंद केलेले आहे. सुरू ठेवण्यासाठी PUK कोड टाका. सिम कायमचे बंद होण्याआधी तुमच्याकडे <xliff:g id="_NUMBER_0">%d</xliff:g> प्रयत्न शिल्लक आहे. तपशीलांसाठी वाहकाशी संपर्क साधा.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{सिमचा पिन एंटर करा. तुम्ही तुमचे डिव्‍हाइस अनलॉक करण्‍यासाठी तुमच्या वाहकाशी संपर्क साधण्‍यापूर्वी, तुमच्याकडे # प्रयत्न शिल्लक आहे.}other{सिमचा पिन एंटर करा. तुमच्याकडे # शिल्लक प्रयत्न आहेत.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{सिम आता बंद केलेले आहे. पुढे सुरू ठेवण्यासाठी PUK कोड एंटर करा. सिम कायमचे निरुपयोगी होण्यापूर्वी तुमच्याकडे # प्रयत्न शिल्लक आहे. तपशिलांसाठी वाहकाशी संपर्क साधा.}other{सिम आता बंद केलेले आहे. पुढे सुरू ठेवण्यासाठी PUK कोड एंटर करा. सिम कायमचे निरुपयोगी होण्यापूर्वी तुमच्याकडे # प्रयत्न शिल्लक आहेत. तपशिलांसाठी वाहकाशी संपर्क साधा.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"डीफॉल्ट"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"बबल"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"अ‍ॅनालॉग"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml
index e2efa906627e..fc18b42bb509 100644
--- a/packages/SystemUI/res-keyguard/values-ms/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Anda telah tersilap taip kata laluan sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. \n\nCuba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> saat."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Anda telah tersilap lukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. \n\nCuba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> saat."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Kod PIN SIM salah. Anda mesti menghubungi pembawa anda untuk membuka kunci peranti."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Kod PIN SIM salah, tinggal <xliff:g id="NUMBER_1">%d</xliff:g> percubaan.</item>
- <item quantity="one">Kod PIN SIM salah. Tinggal <xliff:g id="NUMBER_0">%d</xliff:g> percubaan sebelum anda perlu menghubungi pembawa anda untuk membuka kunci peranti.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Kod PIN SIM tidak betul, anda mempunyai # percubaan lagi sebelum perlu menghubungi pembawa anda untuk membuka kunci peranti.}other{Kod PIN SIM tidak betul, anda mempunyai # percubaan lagi. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM tidak boleh digunakan. Hubungi pembawa anda."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Kod PUK SIM salah. Tinggal <xliff:g id="NUMBER_1">%d</xliff:g> percubaan sebelum SIM tidak boleh digunakan secara kekal.</item>
- <item quantity="one">Kod PUK SIM salah. Tinggal <xliff:g id="NUMBER_0">%d</xliff:g> percubaan sebelum SIM tidak boleh digunakan secara kekal.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Kod PUK SIM tidak betul, anda mempunyai # percubaan lagi sebelum SIM tidak boleh digunakan secara kekal.}other{Kod PUK SIM tidak betul, anda mempunyai # percubaan lagi sebelum SIM tidak boleh digunakan secara kekal.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Pengendalian PIN SIM gagal!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Pengendalian PUK SIM gagal!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Tukar kaedah masukan"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Peranti telah dikunci secara manual"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Tidak dikenali"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Hidupkan kamera dalam Tetapan untuk Buka Kunci Wajah"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Masukkan PIN SIM. Tinggal <xliff:g id="NUMBER_1">%d</xliff:g> percubaan lagi.</item>
- <item quantity="one">Masukkan PIN SIM. Tinggal <xliff:g id="NUMBER_0">%d</xliff:g> percubaan lagi sebelum anda perlu menghubungi pembawa anda untuk membuka kunci peranti.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">Kini SIM dilumpuhkan. Masukkan kod PUK untuk meneruskan. Tinggal <xliff:g id="_NUMBER_1">%d</xliff:g> percubaan sebelum SIM tidak boleh digunakan secara kekal. Hubungi pembawa untuk mendapatkan butiran.</item>
- <item quantity="one">Kini SIM dilumpuhkan. Masukkan kod PUK untuk meneruskan. Tinggal <xliff:g id="_NUMBER_0">%d</xliff:g> percubaan sebelum SIM tidak boleh digunakan secara kekal. Hubungi pembawa untuk mendapatkan butiran.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Masukkan PIN SIM. Anda mempunyai # percubaan lagi sebelum perlu menghubungi pembawa anda untuk membuka kunci peranti.}other{Masukkan PIN SIM. Anda mempunyai # percubaan lagi.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Kini SIM dilumpuhkan. Masukkan kod PUK untuk meneruskan. Anda mempunyai # percubaan lagi sebelum SIM tidak boleh digunakan secara kekal. Hubungi pembawa untuk mendapatkan butiran.}other{Kini SIM dilumpuhkan. Masukkan kod PUK untuk meneruskan. Anda mempunyai # percubaan lagi sebelum SIM tidak boleh digunakan secara kekal. Hubungi pembawa untuk mendapatkan butiran.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Lalai"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Gelembung"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
diff --git a/packages/SystemUI/res-keyguard/values-my/strings.xml b/packages/SystemUI/res-keyguard/values-my/strings.xml
index fd624502a276..38d94e4ab7f6 100644
--- a/packages/SystemUI/res-keyguard/values-my/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-my/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"သင်သည် စကားဝှက်ကို <xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိမ်မှားယွင်းစွာ ထည့်ခဲ့ပါသည်။ \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> စက္ကန့်အကြာတွင် ထပ်စမ်းကြည့်ပါ။"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"သင်သည် ပုံစံကို <xliff:g id="NUMBER_0">%1$d</xliff:g> ကြိမ်မှားယွင်းစွာ ဆွဲခဲ့ပါသည်။ \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> စက္ကန့်အကြာတွင် ထပ်စမ်းကြည့်ပါ။"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"ဆင်းမ်ကဒ်ပင်နံပါတ် မှားယွင်းနေသောကြောင့် ယခုအခါ သင့်စက်ပစ္စည်းအား လော့ခ်ဖွင့်ရန် ဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်ရပါမည်။"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">ဆင်းမ်ပင်နံပါတ် မှန်ကန်မှုမရှိပါ။ <xliff:g id="NUMBER_1">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့်ရှိပါသေးသည်။</item>
- <item quantity="one">ဆင်းမ်ပင်နံပါတ် မှန်ကန်မှုမရှိပါ။ သင့်စက်ပစ္စည်းအား လော့ခ်ဖွင့်ပေးရန်အတွက် မိုဘိုင်းဝန်ဆောင်မှုပေးသူကို မဆက်သွယ်မီ <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့်ရှိပါသေးသည်။</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{ဆင်းမ်ပင်နံပါတ်ကုဒ် မှားနေသည်။ စက်ကို လော့ခ်ဖွင့်ရန် မိုဘိုင်းဖုန်းကုမ္ပဏီသို့ မဆက်သွယ်မီ သင့်တွင် # ကြိမ် ကြိုးစားခွင့်ရှိသေးသည်။}other{ဆင်းမ်ပင်နံပါတ်ကုဒ် မှားနေသည်။ သင့်တွင် # ကြိမ် ကြိုးစားခွင့်ရှိသေးသည်။ }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"ဆင်းမ်ကဒ်ကို အသုံးပြု၍ မရတော့ပါ။ ဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်ပါ။"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">ဆင်းမ် ပင်နံပါတ် ပြန်ဖွင့်သည့်ကုဒ် မှန်ကန်မှုမရှိပါ။ ဆင်းမ်ကဒ်ကို အပြီးအပိုင်မပိတ်ခင် <xliff:g id="NUMBER_1">%d</xliff:g> ကြိမ်စမ်းသပ်ခွင့် ရှိပါသေးသည်။</item>
- <item quantity="one">ဆင်းမ် ပင်နံပါတ် ပြန်ဖွင့်သည့်ကုဒ် မှန်ကန်မှုမရှိပါ။ ဆင်းမ်ကဒ်ကို အပြီးအပိုင်မပိတ်ခင် <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ်စမ်းသပ်ခွင့် ရှိပါသေးသည်။</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{ဆင်းမ် PUK ကုဒ် မှားနေသည်။ ဆင်းမ် အပြီးပိတ်မသွားမီ သင့်တွင် # ကြိမ် ကြိုးစားခွင့်ရှိသေးသည်။}other{ဆင်းမ် PUK ကုဒ် မှားနေသည်။ ဆင်းမ် အပြီးပိတ်မသွားမီ သင့်တွင် # ကြိမ် ကြိုးစားခွင့်ရှိသေးသည်။}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"ဆင်းမ်ကဒ်ပင်နံပါတ် လုပ်ဆောင်ချက် မအောင်မြင်ပါ။"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"ဆင်းမ်ကတ် ပင်နံပါတ် ပြန်ဖွင့်သည့်ကုဒ် လုပ်ဆောင်ချက် မအောင်မြင်ပါ။"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"စာရိုက်စနစ်ပြောင်းရန်"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"စက်ပစ္စည်းကို ကိုယ်တိုင်ကိုယ်ကျ လော့ခ်ချထားခဲ့သည်"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"မသိ"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"‘မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း’ သုံးရန် ‘ဆက်တင်များ’ တွင်ကင်မရာသုံးခွင့်ကိုဖွင့်ပါ"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">ဆင်းမ်ကတ် ပင်နံပါတ် ထည့်ပါ။ <xliff:g id="NUMBER_1">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့်ရှိပါသေးသည်။</item>
- <item quantity="one">ဆင်းမ်ကတ် ပင်နံပါတ် ထည့်ပါ။ သင့်စက်ကို လော့ခ်ဖွင့်ပေးရန်အတွက် ဝန်ဆောင်မှုပေးသူသို့ မဆက်သွယ်မီ <xliff:g id="NUMBER_0">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့်ရှိပါသေးသည်။</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">ဆင်းမ်ကတ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် PUK ကုဒ်ကို ထည့်ပါ။ ဆင်းမ်ကတ် အပြီးပိတ်မသွားမီ သင့်တွင် <xliff:g id="_NUMBER_1">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့် ကျန်ပါသေးသည်။ အသေးစိတ်အချက်များအတွက် ဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်ပါ။</item>
- <item quantity="one">ဆင်းမ်ကတ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် PUK ကုဒ်ကို ထည့်ပါ။ ဆင်းမ်ကတ် အပြီးပိတ်မသွားမီ သင့်တွင် <xliff:g id="_NUMBER_0">%d</xliff:g> ကြိမ် စမ်းသပ်ခွင့် ကျန်ပါသေးသည်။ အသေးစိတ်အချက်များအတွက် ဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်ပါ။</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{ဆင်းမ်ပင်နံပါတ် ထည့်သွင်းပါ။ သင့်စက်ကို လော့ခ်ဖွင့်ရန် မိုဘိုင်းဖုန်းကုမ္ပဏီသို့ မဆက်သွယ်မီ # ကြိမ် ကြိုးစားခွင့်ရှိသေးသည်။}other{ဆင်းမ်ပင်နံပါတ် ထည့်သွင်းပါ။ သင့်တွင် # ကြိမ် ကြိုးစားခွင့်ရှိသေးသည်။}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{ဆင်းမ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် PUK ကုဒ်ကို ထည့်ပါ။ ဆင်းမ် အပြီးပိတ်မသွားမီ သင့်တွင် # ကြိမ် ကြိုးစားခွင့်ရှိသေးသည်။ အသေးစိတ်အတွက် မိုဘိုင်းဖုန်းကုမ္ပဏီကို ဆက်သွယ်ပါ။}other{ဆင်းမ်သည် ယခု ပိတ်သွားပါပြီ။ ရှေ့ဆက်ရန် PUK ကုဒ်ကို ထည့်ပါ။ ဆင်းမ် အပြီးပိတ်မသွားမီ သင့်တွင် # ကြိမ် ကြိုးစားခွင့်ရှိသေးသည်။ အသေးစိတ်အတွက် မိုဘိုင်းဖုန်းကုမ္ပဏီကို ဆက်သွယ်ပါ။}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"မူလ"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"ပူဖောင်းကွက်"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"ရိုးရိုး"</string>
diff --git a/packages/SystemUI/res-keyguard/values-nb/strings.xml b/packages/SystemUI/res-keyguard/values-nb/strings.xml
index 474700c88e22..87307232447a 100644
--- a/packages/SystemUI/res-keyguard/values-nb/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nb/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Du har tastet inn passordet ditt feil <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger. \n\nPrøv på nytt om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Du har tegnet opplåsningsmønsteret ditt feil <xliff:g id="NUMBER_0">%1$d</xliff:g> ganger. \n\nPrøv på nytt om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Feil PIN-kode for SIM-kortet. Du må nå kontakte operatøren din for å låse opp enheten."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Feil PIN-kode for SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøk igjen.</item>
- <item quantity="one">Feil PIN-kode for SIM-kortet. Du har <xliff:g id="NUMBER_0">%d</xliff:g> forsøk igjen før du må kontakte operatøren din for å låse opp enheten.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Feil PIN-kode for SIM-kortet. Du har # forsøk igjen før du må kontakte operatøren din for å låse opp enheten.}other{Feil PIN-kode for SIM-kortet. Du har # forsøk igjen. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM-kortet er ubrukelig. Ta kontakt med operatøren din."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Feil PUK-kode for SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøk igjen før SIM-kortet blir permanent ubrukelig.</item>
- <item quantity="one">Feil PUK-kode for SIM-kortet. Du har <xliff:g id="NUMBER_0">%d</xliff:g> forsøk igjen før SIM-kortet blir permanent ubrukelig.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Feil PUK-kode for SIM-kortet. Du har # forsøk igjen før SIM-kortet blir permanent ubrukelig.}other{Feil PUK-kode for SIM-kortet. Du har # forsøk igjen før SIM-kortet blir permanent ubrukelig.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"PIN-koden for SIM-kortet ble avvist."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"PUK-koden for SIM-kortet ble avvist."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Bytt inndatametode"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Enheten ble låst manuelt"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Ikke gjenkjent"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Slå på kameratilgang i Innstillinger for å bruke ansiktslås"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Skriv inn PIN-koden for SIM-kortet. Du har <xliff:g id="NUMBER_1">%d</xliff:g> forsøk igjen.</item>
- <item quantity="one">Skriv inn PIN-koden for SIM-kortet. Du har <xliff:g id="NUMBER_0">%d</xliff:g> forsøk igjen før du må kontakte operatøren din for å låse opp enheten.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM-kortet er deaktivert nå. Skriv inn PUK-koden for å fortsette. Du har <xliff:g id="_NUMBER_1">%d</xliff:g> forsøk igjen før SIM-kortet blir permanent ubrukelig. Kontakt operatøren for å få vite mer.</item>
- <item quantity="one">SIM-kortet er deaktivert nå. Skriv inn PUK-koden for å fortsette. Du har <xliff:g id="_NUMBER_0">%d</xliff:g> forsøk igjen før SIM-kortet blir permanent ubrukelig. Kontakt operatøren for å få vite mer.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Skriv inn PIN-koden for SIM-kortet. Du har # forsøk igjen før du må kontakte operatøren din for å låse opp enheten.}other{Skriv inn PIN-koden for SIM-kortet. Du har # forsøk igjen.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM-kortet er deaktivert nå. Skriv inn PUK-koden for å fortsette. Du har # forsøk igjen før SIM-kortet blir permanent ubrukelig. Kontakt operatøren for å få vite mer.}other{SIM-kortet er deaktivert nå. Skriv inn PUK-koden for å fortsette. Du har # forsøk igjen før SIM-kortet blir permanent ubrukelig. Kontakt operatøren for å få vite mer.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Standard"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Boble"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index c55aa8f24951..98e98131e341 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"तपाईंले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक आफ्नो गलत पासवर्ड प्रविष्ट गर्नुभएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"तपाईंले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले आफ्नो अनलक प्याटर्न कोर्नुभएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि कोसिस गर्नुहोस्।"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM को PIN कोड गलत छ। तपाईंले अब आफ्नो यन्त्र खोल्न आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नै पर्ने हुन्छ।"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">SIM को PIN कोड गलत छ, तपाईं अझै <xliff:g id="NUMBER_1">%d</xliff:g> पटक प्रयास गर्न सक्नुहुन्छ।</item>
- <item quantity="one">SIM को PIN कोड गलत छ,तपाईंले आफ्नो डिभाइस अनलक गर्न आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नैपर्ने अवस्था आउनु अघि तपाईं अझै <xliff:g id="NUMBER_0">%d</xliff:g> पटक प्रयास गर्न सक्नुहुन्छ।</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{तपाईंले SIM को गलत PIN कोड हाल्नुभयो, तपाईं अब # पटक PIN हाल्ने प्रयास गर्न सक्नुहुन्छ। त्यसपछि आफ्नो डिभाइस अनलक गर्न तपाईंले आफ्नो सेवा प्रदायकमा सम्पर्क गर्नु पर्ने हुन्छ।}other{तपाईंले SIM को गलत PIN कोड हाल्नुभयो, तपाईं अब # पटक PIN हाल्ने प्रयास गर्न सक्नुहुन्छ। }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM काम नलाग्ने भएको छ। आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">SIM को PUK कोड गलत छ, SIM सदाका लागि काम नलाग्ने हुनु अघि तपाईं अझै <xliff:g id="NUMBER_1">%d</xliff:g> पटक प्रयास गर्न सक्नुहुन्छ।</item>
- <item quantity="one">SIM को PUK कोड गलत छ, SIM सदाका लागि काम नलाग्ने हुनु अघि तपाईं अझै <xliff:g id="NUMBER_0">%d</xliff:g> पटक प्रयास गर्न सक्नुहुन्छ।</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{तपाईंले SIM को गलत PUK कोड हाल्नुभयो, तपाईं अब # पटक PUK हाल्ने प्रयास गर्न सक्नुहुन्छ। त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुने छ।}other{तपाईंले SIM को गलत PUK कोड हाल्नुभयो, तपाईं अब # पटक PUK हाल्ने प्रयास गर्न सक्नुहुन्छ। त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुने छ।}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM को PIN कोड राखेर अनलक गर्ने कार्य असफल भयो!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM को PUK कोड राखेर अनलक गर्ने कार्य असफल भयो!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"इनपुट विधिलाई स्विच गर्नुहोस्"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"यन्त्रलाई म्यानुअल तरिकाले लक गरिएको थियो"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"पहिचान भएन"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"तपाईं \"फेस अनलक\" प्रयोग गर्न चाहनुहुन्छ भने सेटिङमा गई क्यामेरा प्रयोग गर्ने अनुमति दिनुहोस्"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">SIM को PIN प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="NUMBER_1">%d</xliff:g>  प्रयासहरू बाँकी छन्।</item>
- <item quantity="one">SIM को PIN प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="NUMBER_0">%d</xliff:g> प्रयास बाँकी छ, त्यसपछि भने आफ्नो डिभाइस अनलक गर्नका लागि तपाईंले अनिवार्य रूपमा आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नु पर्ने हुन्छ।</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM लाई असक्षम पारिएको छ। जारी राख्न PUK कोड प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="_NUMBER_1">%d</xliff:g> प्रयासहरू बाँकी छन्, त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुन्छ। विवरणहरूका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।</item>
- <item quantity="one">SIM लाई असक्षम पारिएको छ। जारी राख्न PUK कोड प्रविष्टि गर्नुहोस्। तपाईंसँग <xliff:g id="_NUMBER_0">%d</xliff:g> प्रयास बाँकी छ, त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुन्छ। विवरणहरूका लागि सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{SIM को PIN हाल्नुहोस्। तपाईं अब # पटक PIN हाल्ने प्रयास गर्न सक्नुहुन्छ। त्यसपछि आफ्नो डिभाइस अनलक गर्न तपाईंले आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नु पर्ने हुन्छ।}other{SIM को PIN हाल्नुहोस्। तपाईं अब # पटक PIN हाल्ने प्रयास गर्न सक्नुहुन्छ।}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM अहिले अफ गरिएको छ। जारी राख्न PUK कोड हाल्नुहोस्। तपाईं अब # पटक PUK हाल्ने प्रयास गर्न सक्नुहुन्छ। त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुने छ। थप जानकारी प्राप्त गर्न आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।}other{SIM अहिले अफ गरिएको छ। जारी राख्न PUK कोड हाल्नुहोस्। तपाईं अब # पटक PUK हाल्ने प्रयास गर्न सक्नुहुन्छ। त्यसपछि SIM सदाका लागि प्रयोग गर्न नमिल्ने हुने छ। थप जानकारी प्राप्त गर्न आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"डिफल्ट"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"बबल"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"एनालग"</string>
diff --git a/packages/SystemUI/res-keyguard/values-nl/strings.xml b/packages/SystemUI/res-keyguard/values-nl/strings.xml
index 18558eb3dbe7..77bf29c85ff6 100644
--- a/packages/SystemUI/res-keyguard/values-nl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-nl/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Je hebt je wachtwoord <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getypt. \n\nProbeer het over <xliff:g id="NUMBER_1">%2$d</xliff:g> seconden opnieuw."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Je hebt je ontgrendelingspatroon <xliff:g id="NUMBER_0">%1$d</xliff:g> keer onjuist getekend. \n\nProbeer het over <xliff:g id="NUMBER_1">%2$d</xliff:g> seconden opnieuw."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Onjuiste pincode voor simkaart. Je moet nu contact opnemen met je provider om je apparaat te ontgrendelen."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Onjuiste pincode voor simkaart. Je hebt nog <xliff:g id="NUMBER_1">%d</xliff:g> pogingen over.</item>
- <item quantity="one">Onjuiste pincode voor simkaart. Je hebt nog <xliff:g id="NUMBER_0">%d</xliff:g> poging over voordat je contact met je provider moet opnemen om je apparaat te ontgrendelen.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Onjuiste pincode voor simkaart. Je hebt nog # poging over voordat je contact met je provider moet opnemen om je apparaat te laten ontgrendelen.}other{Onjuiste pincode voor simkaart. Je hebt nog # pogingen over. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Simkaart is onbruikbaar. Neem contact op met je provider."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Onjuiste pukcode voor simkaart. Je hebt nog <xliff:g id="NUMBER_1">%d</xliff:g> pogingen over voordat de simkaart definitief onbruikbaar wordt.</item>
- <item quantity="one">Onjuiste pukcode voor simkaart. Je hebt nog <xliff:g id="NUMBER_0">%d</xliff:g> poging over voordat de simkaart definitief onbruikbaar wordt.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Onjuiste pukcode voor simkaart. Je hebt nog # poging over voordat de simkaart definitief onbruikbaar wordt.}other{Onjuiste pukcode voor simkaart. Je hebt nog # pogingen over voordat de simkaart definitief onbruikbaar wordt.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Bewerking met pincode voor simkaart is mislukt."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Bewerking met pukcode voor simkaart is mislukt."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Invoermethode wijzigen"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Apparaat is handmatig vergrendeld"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Niet herkend"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Zet cameratoegang aan in Instellingen om Ontgrendelen via gezichtsherkenning te gebruiken"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Geef de pincode van de simkaart op. Je hebt nog <xliff:g id="NUMBER_1">%d</xliff:g> pogingen over.</item>
- <item quantity="one">Geef de pincode van de simkaart op. Je hebt nog <xliff:g id="NUMBER_0">%d</xliff:g> poging over voordat je contact met je provider moet opnemen om het apparaat te ontgrendelen.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">De simkaart is nu uitgezet. Geef de pukcode op om door te gaan. Je hebt nog <xliff:g id="_NUMBER_1">%d</xliff:g> pogingen over voordat de simkaart definitief onbruikbaar wordt. Neem contact op met je provider voor meer informatie.</item>
- <item quantity="one">De simkaart is nu uitgezet. Geef de pukcode op om door te gaan. Je hebt nog <xliff:g id="_NUMBER_0">%d</xliff:g> poging over voordat de simkaart definitief onbruikbaar wordt. Neem contact op met je provider voor meer informatie.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Geef de pincode van de simkaart op. Je hebt nog # poging over voordat je contact met je provider moet opnemen om het apparaat te laten ontgrendelen.}other{Geef de pincode van de simkaart op. Je hebt nog # pogingen.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{De simkaart is nu gedeactiveerd. Geef de pukcode op om door te gaan. Je hebt nog # poging over voordat de simkaart definitief onbruikbaar wordt. Neem contact op met je provider voor meer informatie.}other{De simkaart is nu gedeactiveerd. Geef de pukcode op om door te gaan. Je hebt nog # pogingen over voordat de simkaart definitief onbruikbaar wordt. Neem contact op met je provider voor meer informatie.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Standaard"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bel"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analoog"</string>
diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml
index a90e25604a64..8765781cb4fd 100644
--- a/packages/SystemUI/res-keyguard/values-or/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-or/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"ଆପଣଙ୍କ ପାସ୍‌ୱର୍ଡକୁ ଆପଣ <xliff:g id="NUMBER_0">%1$d</xliff:g> ଥର ଭୁଲ ଭାବେ ଟାଇପ୍ କରିଛନ୍ତି। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"ଆପଣଙ୍କ ଲକ୍‍ ଖୋଲିବା ପାଟର୍ନକୁ ଆପଣ <xliff:g id="NUMBER_0">%1$d</xliff:g>ଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"ଭୁଲ SIM PIN କୋଡ୍‌, ଆପଣଙ୍କ ଡିଭାଇସକୁ ଅନଲକ୍‌ କରିବା ପାଇଁ ଏବେ ହିଁ ନିଜ କେରିଅର୍‌ଙ୍କ ସହ ସମ୍ପର୍କ କରନ୍ତୁ।"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">ଭୁଲ SIM PIN କୋଡ୍, ଆପଣଙ୍କର ଆଉ <xliff:g id="NUMBER_1">%d</xliff:g>ଟି ପ୍ରୟାସ ବାକି ରହିଛି।</item>
- <item quantity="one">ଭୁଲ SIM PIN କୋଡ୍, ଡିଭାଇସ୍‍ ଅନଲକ୍‍ କରିବା ପାଇଁ କେରିଅରଙ୍କ ସହିତ ଯୋଗାଯୋଗ କରିବା ପୂର୍ବରୁ ଆପଣଙ୍କ ପାଖରେ ଆଉ <xliff:g id="NUMBER_0">%d</xliff:g>ଟି ପ୍ରୟାସ ବାକି ରହିଛି।</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{ଭୁଲ SIM PIN କୋଡ, ଆପଣଙ୍କ ଡିଭାଇସକୁ ଅନଲକ କରିବା ପାଇଁ ଆପଣ ଆପଣଙ୍କର କ୍ୟାରିଅର ସହ ନିଶ୍ଚିତରୂପେ ଯୋଗାଯୋଗ କରିବା ପୂର୍ବରୁ ଆପଣଙ୍କ ପାଖରେ #ଟି ପ୍ରଚେଷ୍ଟା ବାକି ଅଛି।}other{ଭୁଲ SIM PIN କୋଡ, ଆପଣଙ୍କ ପାଖରେ #ଟି ପ୍ରଚେଷ୍ଟା ବାକି ଅଛି। }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM ବ୍ୟବହାର କରାଯାଇପାରିବ ନାହିଁ। ନିଜ କେରିଅର୍‌ଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">ଭୁଲ SIM PUK କୋଡ୍‍, SIMଟି ଆଉ <xliff:g id="NUMBER_1">%d</xliff:g>ଟି ପ୍ରୟାସ ପରେ ସ୍ଥାୟୀ ଭାବେ ବ୍ୟବହାର କରାଯାଇରିବ ନାହିଁ।</item>
- <item quantity="one">ଭୁଲ SIM PUK କୋଡ୍‍, SIMଟି ଆଉ <xliff:g id="NUMBER_0">%d</xliff:g>ଟି ପ୍ରୟାସ ପରେ ସ୍ଥାୟୀ ଭାବେ ବ୍ୟବହାର କରାଯାଇରିବ ନାହିଁ।</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{ଭୁଲ SIM PUK କୋଡ, SIM ସ୍ଥାୟୀ ଭାବେ ବ୍ୟବହାର ଅଯୋଗ୍ୟ ହେବା ପୂର୍ବରୁ ଆପଣଙ୍କ ପାଖରେ #ଟି ପ୍ରଚେଷ୍ଟା ବାକି ଅଛି।}other{ଭୁଲ SIM PUK କୋଡ, SIM ସ୍ଥାୟୀ ଭାବେ ବ୍ୟବହାର ଅଯୋଗ୍ୟ ହେବା ପୂର୍ବରୁ ଆପଣଙ୍କ ପାଖରେ #ଟି ପ୍ରଚେଷ୍ଟା ବାକି ଅଛି।}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM PIN କାମ ବିଫଳ ହେଲା!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUKର କାମ ବିଫଳ ହେଲା!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ଇନପୁଟ୍‌ ପଦ୍ଧତି ବଦଳାନ୍ତୁ"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ଡିଭାଇସ୍‍ ମାନୁଆଲ ଭାବେ ଲକ୍‍ କରାଗଲା"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"ଫେସ ଅନଲକର ବ୍ୟବହାର ପାଇଁ ସେଟିଂସରେ କ୍ୟାମେରାକୁ ଆକ୍ସେସ ଦିଅ"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">SIM PIN ପ୍ରବେଶ କରନ୍ତୁ। ଆପଣଙ୍କ ପାଇଁ <xliff:g id="NUMBER_1">%d</xliff:g>ଟି ପ୍ରୟାସ ବଳକା ଅଛି।</item>
- <item quantity="one">SIM PIN ପ୍ରବେଶ କରନ୍ତୁ। ଆପଣଙ୍କ ଡିଭାଇସ୍‍କୁ ଅନଲକ୍ କରିବା ପାଇଁ ପାଖରେ ବଳକା ଥିବା <xliff:g id="NUMBER_0">%d</xliff:g>ଟି ପ୍ରୟାସର ବ୍ୟବହାର କରିବା ପୂର୍ବରୁ ନିଜର କେରିଅର୍‍ଙ୍କୁ ସମ୍ପର୍କ କରନ୍ତୁ।</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM କାର୍ଡକୁ ବର୍ତ୍ତମାନ ଅକ୍ଷମ କରିଦିଆଯାଇଛି। ଜାରି ରଖିବାକୁ PUK କୋଡ୍‍ ଲେଖନ୍ତୁ। ଆଉ <xliff:g id="_NUMBER_1">%d</xliff:g> ଥର ଭୁଲ କୋଡ୍‍ ଲେଖିବା ପରେ SIM କାର୍ଡ ସ୍ଥାୟୀ ଭାବେ ଅନୁପଯୋଗୀ ହୋଇଯିବ। ବିବରଣୀ ପାଇଁ କେରିଅର୍‌ର ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।</item>
- <item quantity="one">SIM କାର୍ଡକୁ ବର୍ତ୍ତମାନ ଅକ୍ଷମ କରିଦିଆଯାଇଛି। ଜାରି ରଖିବାକୁ PUK କୋଡ୍‍ ଲେଖନ୍ତୁ। ଆଉ <xliff:g id="_NUMBER_0">%d</xliff:g> ଥର ଭୁଲ କୋଡ୍‍ ଲେଖିବା ପରେ SIM କାର୍ଡ ସ୍ଥାୟୀ ଭାବେ ଅନୁପଯୋଗୀ ହୋଇଯିବ। ବିବରଣୀ ପାଇଁ କେରିଅର୍‌ର ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{SIM PIN ଲେଖନ୍ତୁ। ଆପଣଙ୍କ ଡିଭାଇସକୁ ଅନଲକ କରିବା ପାଇଁ ଆପଣ ଆପଣଙ୍କର କ୍ୟାରିଅର ସହ ନିଶ୍ଚିତରୂପେ ଯୋଗାଯୋଗ କରିବା ପୂର୍ବରୁ ଆପଣଙ୍କ ପାଖରେ #ଟି ପ୍ରଚେଷ୍ଟା ବାକି ଅଛି।}other{SIM PIN ଲେଖନ୍ତୁ। ଆପଣଙ୍କର #ଟି ପ୍ରଚେଷ୍ଟା ବାକି ଅଛି।}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIMକୁ ବର୍ତ୍ତମାନ ଅକ୍ଷମ କରାଯାଇଛି। ଜାରି ରଖିବାକୁ PUK କୋଡ ଲେଖନ୍ତୁ। SIM ସ୍ଥାୟୀ ଭାବେ ବ୍ୟବହାର ଅଯୋଗ୍ୟ ହେବା ପୂର୍ବରୁ ଆପଣଙ୍କ ପାଖରେ #ଟି ପ୍ରଚେଷ୍ଟା ବାକି ଅଛି। ବିବରଣୀ ପାଇଁ କ୍ୟାରିଅର ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।}other{SIMକୁ ବର୍ତ୍ତମାନ ଅକ୍ଷମ କରାଯାଇଛି। ଜାରି ରଖିବାକୁ PUK କୋଡ ଲେଖନ୍ତୁ। SIM ସ୍ଥାୟୀ ଭାବେ ବ୍ୟବହାର ଅଯୋଗ୍ୟ ହେବା ପୂର୍ବରୁ ଆପଣଙ୍କ ପାଖରେ #ଟି ପ୍ରଚେଷ୍ଟା ବାକି ଅଛି। ବିବରଣୀ ପାଇଁ କ୍ୟାରିଅର ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"ଡିଫଲ୍ଟ"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"ବବଲ୍"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"ଆନାଲଗ୍"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index 243bbc3b6291..e1c7d2629f3b 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਪਾਸਵਰਡ ਗਲਤ ਢੰਗ ਨਾਲ ਟਾਈਪ ਕੀਤਾ ਹੈ।\n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"ਤੁਸੀਂ <xliff:g id="NUMBER_0">%1$d</xliff:g> ਵਾਰ ਆਪਣਾ ਅਣਲਾਕ ਪੈਟਰਨ ਗਲਤ ਢੰਗ ਨਾਲ ਉਲੀਕਿਆ ਹੈ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"ਗਲਤ ਸਿਮ ਪਿੰਨ ਕੋਡ, ਆਪਣੇ ਡੀਵਾਈਸ ਨੂੰ ਅਣਲਾਕ ਕਰਨ ਲਈ ਹੁਣ ਤੁਹਾਨੂੰ ਲਾਜ਼ਮੀ ਤੌਰ \'ਤੇ ਆਪਣੇ ਕੈਰੀਅਰ ਨਾਲ ਸੰਪਰਕ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ।"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">ਗਲਤ ਸਿਮ ਪਿੰਨ ਕੋਡ, ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ।</item>
- <item quantity="other">ਗਲਤ ਸਿਮ ਪਿੰਨ ਕੋਡ, ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ।</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{ਗਲਤ ਸਿਮ ਪਿੰਨ ਕੋਡ, ਤੁਹਾਡੇ ਕੋਲ # ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ ਉਸ ਤੋਂ ਬਾਅਦ ਆਪਣੇ ਡੀਵਾਈਸ ਨੂੰ ਅਣਲਾਕ ਕਰਨ ਲਈ ਤੁਹਾਨੂੰ ਆਪਣੇ ਕੈਰੀਅਰ ਨਾਲ ਸੰਪਰਕ ਕਰਨਾ ਪਵੇਗਾ।}one{ਗਲਤ ਸਿਮ ਪਿੰਨ ਕੋਡ, ਤੁਹਾਡੇ ਕੋਲ # ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ। }other{ਗਲਤ ਸਿਮ ਪਿੰਨ ਕੋਡ, ਤੁਹਾਡੇ ਕੋਲ # ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ। }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM ਨਾ-ਵਰਤਣਯੋਗ ਹੈ। ਆਪਣੇ ਕੈਰੀਅਰ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">ਗਲਤ ਸਿਮ PUK ਕੋਡ, ਇਸਤੋਂ ਪਹਿਲਾਂ ਕਿ ਸਿਮ ਸਥਾਈ ਤੌਰ \'ਤੇ ਵਰਤਣਯੋਗ ਨਾ ਰਹੇ, ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ।</item>
- <item quantity="other">ਗਲਤ ਸਿਮ PUK ਕੋਡ, ਇਸਤੋਂ ਪਹਿਲਾਂ ਕਿ ਸਿਮ ਸਥਾਈ ਤੌਰ \'ਤੇ ਵਰਤਣਯੋਗ ਨਾ ਰਹੇ, ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ।</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{ਗਲਤ ਸਿਮ PUK ਕੋਡ, ਇਸ ਤੋਂ ਪਹਿਲਾਂ ਕਿ ਸਿਮ ਬਿਲਕੁਲ ਬੇਕਾਰ ਹੋ ਜਾਵੇ, ਤੁਹਾਡੇ ਕੋਲ # ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ।}one{ਗਲਤ ਸਿਮ PUK ਕੋਡ, ਇਸ ਤੋਂ ਪਹਿਲਾਂ ਕਿ ਸਿਮ ਬਿਲਕੁਲ ਬੇਕਾਰ ਹੋ ਜਾਵੇ, ਤੁਹਾਡੇ ਕੋਲ # ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ।}other{ਗਲਤ ਸਿਮ PUK ਕੋਡ, ਇਸ ਤੋਂ ਪਹਿਲਾਂ ਕਿ ਸਿਮ ਬਿਲਕੁਲ ਬੇਕਾਰ ਹੋ ਜਾਵੇ, ਤੁਹਾਡੇ ਕੋਲ # ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ।}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"ਸਿਮ ਪਿੰਨ ਕਾਰਵਾਈ ਅਸਫਲ ਰਹੀ!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUK ਕਾਰਵਾਈ ਅਸਫਲ ਰਹੀ!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ਇਨਪੁੱਟ ਵਿਧੀ ਸਵਿੱਚ ਕਰੋ"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"ਡੀਵਾਈਸ ਨੂੰ ਹੱਥੀਂ ਲਾਕ ਕੀਤਾ ਗਿਆ"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"ਫ਼ੇਸ ਅਣਲਾਕ ਵਰਤਣ ਲਈ, ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਕੈਮਰਾ ਪਹੁੰਚ ਚਾਲੂ ਕਰੋ"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ। ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ।</item>
- <item quantity="other">ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ। ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ।</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">ਸਿਮ ਹੁਣ ਬੰਦ ਹੋ ਗਿਆ ਹੈ। ਜਾਰੀ ਰੱਖਣ ਲਈ PUK ਕੋਡ ਦਾਖਲ ਕਰੋ। ਸਿਮ ਦੇ ਪੱਕੇ ਤੌਰ \'ਤੇ ਬੇਕਾਰ ਹੋ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="_NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਕੈਰੀਅਰ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।</item>
- <item quantity="other">ਸਿਮ ਹੁਣ ਬੰਦ ਹੋ ਗਿਆ ਹੈ। ਜਾਰੀ ਰੱਖਣ ਲਈ PUK ਕੋਡ ਦਾਖਲ ਕਰੋ। ਸਿਮ ਦੇ ਪੱਕੇ ਤੌਰ \'ਤੇ ਬੇਕਾਰ ਹੋ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="_NUMBER_1">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ। ਵੇਰਵਿਆਂ ਲਈ ਕੈਰੀਅਰ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ। ਤੁਹਾਡੇ ਕੋਲ # ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ, ਉਸ ਤੋਂ ਬਾਅਦ ਆਪਣੇ ਡੀਵਾਈਸ ਨੂੰ ਅਣਲਾਕ ਕਰਨ ਲਈ ਤੁਹਾਨੂੰ ਆਪਣੇ ਕੈਰੀਅਰ ਨਾਲ ਸੰਪਰਕ ਕਰਨਾ ਪਵੇਗਾ।}one{ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ। ਤੁਹਾਡੇ ਕੋਲ # ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ।}other{ਸਿਮ ਪਿੰਨ ਦਾਖਲ ਕਰੋ। ਤੁਹਾਡੇ ਕੋਲ # ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ।}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{ਸਿਮ ਹੁਣ ਬੰਦ ਹੋ ਗਿਆ ਹੈ। ਜਾਰੀ ਰੱਖਣ ਲਈ PUK ਕੋਡ ਦਾਖਲ ਕਰੋ। ਇਸ ਤੋਂ ਪਹਿਲਾਂ ਕਿ ਸਿਮ ਬਿਲਕੁਲ ਬੇਕਾਰ ਹੋ ਜਾਵੇ, ਤੁਹਾਡੇ ਕੋਲ # ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਕੈਰੀਅਰ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।}one{ਸਿਮ ਹੁਣ ਬੰਦ ਹੋ ਗਿਆ ਹੈ। ਜਾਰੀ ਰੱਖਣ ਲਈ PUK ਕੋਡ ਦਾਖਲ ਕਰੋ। ਇਸ ਤੋਂ ਪਹਿਲਾਂ ਕਿ ਸਿਮ ਬਿਲਕੁਲ ਬੇਕਾਰ ਹੋ ਜਾਵੇ, ਤੁਹਾਡੇ ਕੋਲ # ਕੋਸ਼ਿਸ਼ ਬਾਕੀ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਕੈਰੀਅਰ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।}other{ਸਿਮ ਹੁਣ ਬੰਦ ਹੋ ਗਿਆ ਹੈ। ਜਾਰੀ ਰੱਖਣ ਲਈ PUK ਕੋਡ ਦਾਖਲ ਕਰੋ। ਇਸ ਤੋਂ ਪਹਿਲਾਂ ਕਿ ਸਿਮ ਬਿਲਕੁਲ ਬੇਕਾਰ ਹੋ ਜਾਵੇ, ਤੁਹਾਡੇ ਕੋਲ # ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ। ਵੇਰਵਿਆਂ ਲਈ ਕੈਰੀਅਰ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"ਬੁਲਬੁਲਾ"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"ਐਨਾਲੌਗ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index 5165b10f02d0..7cf1784d1134 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -68,19 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> wpisałeś nieprawidłowe hasło. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> nieprawidłowo narysowałeś wzór odblokowania. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Nieprawidłowy kod PIN karty SIM. Musisz teraz skontaktować się z operatorem, by odblokował Twoje urządzenie."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="few">Nieprawidłowy kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> próby.</item>
- <item quantity="many">Nieprawidłowy kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> prób.</item>
- <item quantity="other">Nieprawidłowy kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> próby.</item>
- <item quantity="one">Nieprawidłowy kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_0">%d</xliff:g> próbę, zanim będziesz musiał skontaktować się z operatorem, by odblokował Twoje urządzenie.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Nieprawidłowy kod PIN karty SIM. Masz jeszcze # próbę, zanim trzeba będzie skontaktować się z operatorem, aby odblokował Twoje urządzenie.}few{Nieprawidłowy kod PIN karty SIM. Masz jeszcze # próby. }many{Nieprawidłowy kod PIN karty SIM. Masz jeszcze # prób. }other{Nieprawidłowy kod PIN karty SIM. Masz jeszcze # próby. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Karta SIM została trwale zablokowana. Skontaktuj się z operatorem."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="few">Nieprawidłowy kod PUK karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> próby, zanim karta SIM zostanie trwale zablokowana.</item>
- <item quantity="many">Nieprawidłowy kod PUK karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> prób, zanim karta SIM zostanie trwale zablokowana.</item>
- <item quantity="other">Nieprawidłowy kod PUK karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> próby, zanim karta SIM zostanie trwale zablokowana.</item>
- <item quantity="one">Nieprawidłowy kod PUK karty SIM. Masz jeszcze <xliff:g id="NUMBER_0">%d</xliff:g> próbę, zanim karta SIM zostanie trwale zablokowana.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Nieprawidłowy kod PUK karty SIM. Masz jeszcze # próbę, zanim karta SIM zostanie trwale zablokowana.}few{Nieprawidłowy kod PUK karty SIM. Masz jeszcze # próby, zanim karta SIM zostanie trwale zablokowana.}many{Nieprawidłowy kod PUK karty SIM. Masz jeszcze # prób, zanim karta SIM zostanie trwale zablokowana.}other{Nieprawidłowy kod PUK karty SIM. Masz jeszcze # próby, zanim karta SIM zostanie trwale zablokowana.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Operacja z kodem PIN karty SIM nie udała się."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Operacja z kodem PUK karty SIM nie udała się."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Przełączanie metody wprowadzania"</string>
@@ -95,18 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Urządzenie zostało zablokowane ręcznie"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nie rozpoznano"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Aby używać rozpoznawania twarzy, włącz w Ustawieniach dostęp do aparatu"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="few">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> próby.</item>
- <item quantity="many">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> prób.</item>
- <item quantity="other">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_1">%d</xliff:g> próby.</item>
- <item quantity="one">Wpisz kod PIN karty SIM. Masz jeszcze <xliff:g id="NUMBER_0">%d</xliff:g> próbę, zanim będzie trzeba skontaktować się z operatorem, by odblokować to urządzenie.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="few">Karta SIM została wyłączona. Wpisz kod PUK, by przejść dalej. Masz jeszcze <xliff:g id="_NUMBER_1">%d</xliff:g> próby, zanim karta SIM zostanie trwale zablokowana. Aby uzyskać szczegółowe informacje, skontaktuj się z operatorem.</item>
- <item quantity="many">Karta SIM została wyłączona. Wpisz kod PUK, by przejść dalej. Masz jeszcze <xliff:g id="_NUMBER_1">%d</xliff:g> prób, zanim karta SIM zostanie trwale zablokowana. Aby uzyskać szczegółowe informacje, skontaktuj się z operatorem.</item>
- <item quantity="other">Karta SIM została wyłączona. Wpisz kod PUK, by przejść dalej. Masz jeszcze <xliff:g id="_NUMBER_1">%d</xliff:g> próby, zanim karta SIM zostanie trwale zablokowana. Aby uzyskać szczegółowe informacje, skontaktuj się z operatorem.</item>
- <item quantity="one">Karta SIM została wyłączona. Wpisz kod PUK, by przejść dalej. Masz jeszcze <xliff:g id="_NUMBER_0">%d</xliff:g> próbę, zanim karta SIM zostanie trwale zablokowana. Aby uzyskać szczegółowe informacje, skontaktuj się z operatorem.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Wpisz kod PIN karty SIM. Masz jeszcze # próbę, zanim będzie trzeba skontaktować się z operatorem, aby odblokować to urządzenie.}few{Wpisz kod PIN karty SIM. Masz jeszcze # próby.}many{Wpisz kod PIN karty SIM. Masz jeszcze # prób.}other{Wpisz kod PIN karty SIM. Masz jeszcze # próby.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Karta SIM została wyłączona. Wpisz kod PUK, aby przejść dalej. Masz jeszcze # próbę, zanim karta SIM zostanie trwale zablokowana. Aby uzyskać szczegółowe informacje, skontaktuj się z operatorem.}few{Karta SIM została wyłączona. Wpisz kod PUK, aby przejść dalej. Masz jeszcze # próby, zanim karta SIM zostanie trwale zablokowana. Aby uzyskać szczegółowe informacje, skontaktuj się z operatorem.}many{Karta SIM została wyłączona. Wpisz kod PUK, aby przejść dalej. Masz jeszcze # prób, zanim karta SIM zostanie trwale zablokowana. Aby uzyskać szczegółowe informacje, skontaktuj się z operatorem.}other{Karta SIM została wyłączona. Wpisz kod PUK, aby przejść dalej. Masz jeszcze # próby, zanim karta SIM zostanie trwale zablokowana. Aby uzyskać szczegółowe informacje, skontaktuj się z operatorem.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Domyślna"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bąbelkowy"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogowy"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
index 04206fdc36de..863a4195e5b0 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Código PIN do chip incorreto. Entre em contato com a operadora para desbloquear o dispositivo."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Código PIN do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>.</item>
- <item quantity="other">Código PIN do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Código PIN do chip incorreto. Você tem # tentativa restante. Se o código correto não for digitado, será necessário entrar em contato com a operadora para desbloquear o dispositivo.}one{Código PIN do chip incorreto. Você tem # tentativa restante. }other{Código PIN do chip incorreto. Você tem # tentativas restantes. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"O chip não pode ser utilizado. Entre em contato com a operadora."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Código PUK do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip se tornará permanentemente inutilizável.</item>
- <item quantity="other">Código PUK do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip se tornará permanentemente inutilizável.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Código PUK do chip incorreto. Você tem # tentativa restante. Se o código correto não for digitado, o chip vai ficar permanentemente inutilizável.}one{Código PUK do chip incorreto. Você tem # tentativa restante. Se o código correto não for digitado, o chip vai ficar permanentemente inutilizável.}other{Código PUK do chip incorreto. Você tem # tentativas restantes. Se o código correto não for digitado, o chip vai ficar permanentemente inutilizável.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Falha na operação de PIN do chip."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Falha na operação de PUK do chip."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Alterar o método de entrada"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"O dispositivo foi bloqueado manualmente"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Não reconhecido"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Para usar o Desbloqueio facial, ative o acesso à câmera nas Configurações"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
- <item quantity="other">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">O chip agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativa restante antes de o chip se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
- <item quantity="other">O chip agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativas restantes antes de o chip se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Informe o PIN do chip. Você tem # tentativa restante antes de precisar entrar em contato com a operadora para desbloquear seu dispositivo.}one{Informe o PIN do chip. Você tem # tentativa restante.}other{Informe o PIN do chip. Você tem # tentativas restantes.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{O chip está desativado. Informe o código PUK para continuar. Você tem # tentativa restante antes que o chip fique permanentemente inutilizável. Consulte sua operadora para saber mais.}one{O chip está desativado. Informe o código PUK para continuar. Você tem # tentativa restante antes que o chip fique permanentemente inutilizável. Consulte sua operadora para saber mais.}other{O chip está desativado. Informe o código PUK para continuar. Você tem # tentativas restantes antes que o chip fique permanentemente inutilizável. Consulte sua operadora para saber mais.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Padrão"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bolha"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analógico"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
index abfed2e177cb..6073951ce35c 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Introduziu a palavra-passe incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente dentro de <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Desenhou a sua padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente dentro de <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Código PIN do cartão SIM incorreto. Tem de contactar o seu operador para desbloquear o dispositivo."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Código PIN do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas.</item>
- <item quantity="one">Código PIN do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de precisar de contactar o seu operador para desbloquear o dispositivo.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Código PIN do SIM incorreto. Tem mais # tentativa antes de ser necessário contactar o operador para desbloquear o dispositivo.}other{Código PIN do SIM incorreto. Tem mais # tentativas. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Cartão SIM inutilizável. Contacte o seu operador."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Código PUK do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas antes de o cartão SIM ficar permanentemente inutilizável.</item>
- <item quantity="one">Código PUK do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de o cartão SIM ficar permanentemente inutilizável.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Código PUK do SIM incorreto. Tem mais # tentativa antes de o SIM ficar permanentemente inutilizável.}other{Código PUK do SIM incorreto. Tem mais # tentativas antes de o SIM ficar permanentemente inutilizável.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Falha ao introduzir o PIN do cartão SIM!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Falha ao introduzir o PUK do cartão SIM!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Alternar o método de introdução"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"O dispositivo foi bloqueado manualmente"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Não reconhecido."</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Ative acesso à câmara nas Def. p/ usar Desbl. facial"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Introduza o PIN do cartão SIM. Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas.</item>
- <item quantity="one">Introduza o PIN do cartão SIM. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de ser necessário contactar o operador para desbloquear o dispositivo.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">O SIM encontra-se desativado. Introduza o código PUK para continuar. Tem mais <xliff:g id="_NUMBER_1">%d</xliff:g> tentativas antes de o cartão SIM ficar permanentemente inutilizável. Contacte o operador para obter mais detalhes.</item>
- <item quantity="one">O SIM encontra-se desativado. Introduza o código PUK para continuar. Tem mais <xliff:g id="_NUMBER_0">%d</xliff:g> tentativa antes de o cartão SIM ficar permanentemente inutilizável. Contacte o operador para obter mais detalhes.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Introduza o PIN do SIM. Tem mais # tentativa antes de ser necessário contactar o operador para desbloquear o dispositivo.}other{Introduza o PIN do SIM. Tem mais # tentativas.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{O SIM está desativado. Introduza o código PUK para continuar. Tem mais # tentativa antes de o SIM ficar permanentemente inutilizável. Contacte o operador para obter mais detalhes.}other{O SIM está desativado. Introduza o código PUK para continuar. Tem mais # tentativas antes de o SIM ficar permanentemente inutilizável. Contacte o operador para obter mais detalhes.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Predefinido"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Balão"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analógico"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml
index 04206fdc36de..863a4195e5b0 100644
--- a/packages/SystemUI/res-keyguard/values-pt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Código PIN do chip incorreto. Entre em contato com a operadora para desbloquear o dispositivo."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Código PIN do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>.</item>
- <item quantity="other">Código PIN do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Código PIN do chip incorreto. Você tem # tentativa restante. Se o código correto não for digitado, será necessário entrar em contato com a operadora para desbloquear o dispositivo.}one{Código PIN do chip incorreto. Você tem # tentativa restante. }other{Código PIN do chip incorreto. Você tem # tentativas restantes. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"O chip não pode ser utilizado. Entre em contato com a operadora."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Código PUK do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip se tornará permanentemente inutilizável.</item>
- <item quantity="other">Código PUK do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip se tornará permanentemente inutilizável.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Código PUK do chip incorreto. Você tem # tentativa restante. Se o código correto não for digitado, o chip vai ficar permanentemente inutilizável.}one{Código PUK do chip incorreto. Você tem # tentativa restante. Se o código correto não for digitado, o chip vai ficar permanentemente inutilizável.}other{Código PUK do chip incorreto. Você tem # tentativas restantes. Se o código correto não for digitado, o chip vai ficar permanentemente inutilizável.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Falha na operação de PIN do chip."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Falha na operação de PUK do chip."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Alterar o método de entrada"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"O dispositivo foi bloqueado manualmente"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Não reconhecido"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Para usar o Desbloqueio facial, ative o acesso à câmera nas Configurações"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
- <item quantity="other">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">O chip agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativa restante antes de o chip se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
- <item quantity="other">O chip agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativas restantes antes de o chip se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Informe o PIN do chip. Você tem # tentativa restante antes de precisar entrar em contato com a operadora para desbloquear seu dispositivo.}one{Informe o PIN do chip. Você tem # tentativa restante.}other{Informe o PIN do chip. Você tem # tentativas restantes.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{O chip está desativado. Informe o código PUK para continuar. Você tem # tentativa restante antes que o chip fique permanentemente inutilizável. Consulte sua operadora para saber mais.}one{O chip está desativado. Informe o código PUK para continuar. Você tem # tentativa restante antes que o chip fique permanentemente inutilizável. Consulte sua operadora para saber mais.}other{O chip está desativado. Informe o código PUK para continuar. Você tem # tentativas restantes antes que o chip fique permanentemente inutilizável. Consulte sua operadora para saber mais.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Padrão"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bolha"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analógico"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index 884d8d26dc7f..5a0dfed5ae97 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -68,17 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Ați introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Codul PIN pentru cardul SIM este incorect. Contactați operatorul pentru a vă debloca dispozitivul."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="few">Codul PIN pentru cardul SIM este incorect. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> încercări.</item>
- <item quantity="other">Codul PIN pentru cardul SIM este incorect. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> de încercări.</item>
- <item quantity="one">Codul PIN pentru cardul SIM este incorect. V-a mai rămas <xliff:g id="NUMBER_0">%d</xliff:g> încercare, după care va trebui să contactați operatorul pentru a vă debloca dispozitivul.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Codul PIN pentru cardul SIM este incorect. V-a mai rămas # încercare, după care va trebui să contactați operatorul pentru a vă debloca dispozitivul.}few{Codul PIN pentru cardul SIM este incorect. V-au mai rămas # încercări. }other{Codul PIN pentru cardul SIM este incorect. V-au mai rămas # de încercări. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Cardul SIM nu poate fi utilizat. Contactați operatorul."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="few">Codul PUK pentru cardul SIM este incorect. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> încercări până când cardul SIM va deveni inutilizabil definitiv.</item>
- <item quantity="other">Codul PUK pentru cardul SIM este incorect. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> de încercări până când cardul SIM va deveni inutilizabil definitiv.</item>
- <item quantity="one">Codul PUK pentru cardul SIM este incorect. V-a mai rămas <xliff:g id="NUMBER_0">%d</xliff:g> încercare până când cardul SIM va deveni inutilizabil definitiv.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Codul PUK pentru cardul SIM este incorect. V-a mai rămas # încercare până când cardul SIM va deveni inutilizabil definitiv.}few{Codul PUK pentru cardul SIM este incorect. V-au mai rămas # încercări până când cardul SIM va deveni inutilizabil definitiv.}other{Codul PUK pentru cardul SIM este incorect. V-au mai rămas # de încercări până când cardul SIM va deveni inutilizabil definitiv.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Deblocarea cu ajutorul codului PIN pentru cardul SIM nu a reușit!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Deblocarea cu ajutorul codului PUK pentru cardul SIM nu a reușit!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Comutați metoda de introducere"</string>
@@ -93,16 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Dispozitivul a fost blocat manual"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nu este recunoscut"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Pentru Deblocare facială, activați accesul la cameră"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="few">Introduceți codul PIN pentru cardul SIM. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> încercări.</item>
- <item quantity="other">Introduceți codul PIN pentru cardul SIM. V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> de încercări.</item>
- <item quantity="one">Introduceți codul PIN pentru cardul SIM. V-a mai rămas <xliff:g id="NUMBER_0">%d</xliff:g> încercare, după care va trebui să contactați operatorul pentru a vă debloca dispozitivul.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="few">Cardul SIM este dezactivat acum. Introduceți codul PUK pentru a continua. V-au mai rămas <xliff:g id="_NUMBER_1">%d</xliff:g> încercări până când cardul SIM va deveni inutilizabil definitiv. Contactați operatorul pentru detalii.</item>
- <item quantity="other">Cardul SIM este dezactivat acum. Introduceți codul PUK pentru a continua. V-au mai rămas <xliff:g id="_NUMBER_1">%d</xliff:g> de încercări până când cardul SIM va deveni inutilizabil definitiv. Contactați operatorul pentru detalii.</item>
- <item quantity="one">Cardul SIM este dezactivat acum. Introduceți codul PUK pentru a continua. V-a mai rămas <xliff:g id="_NUMBER_0">%d</xliff:g> încercare până când cardul SIM va deveni inutilizabil definitiv. Contactați operatorul pentru detalii.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Introduceți codul PIN pentru cardul SIM. V-a mai rămas # încercare, după care va trebui să contactați operatorul pentru a vă debloca dispozitivul.}few{Introduceți codul PIN al cardului SIM. V-au rămas # încercări.}other{Introduceți codul PIN al cardului SIM. V-au rămas # de încercări.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Cardul SIM este dezactivat acum. Introduceți codul PUK pentru a continua. V-a mai rămas # încercare până când cardul SIM va deveni inutilizabil definitiv. Contactați operatorul pentru detalii.}few{Cardul SIM este dezactivat acum. Introduceți codul PUK pentru a continua. V-au mai rămas # încercări până când cardul SIM va deveni inutilizabil definitiv. Contactați operatorul pentru detalii.}other{Cardul SIM este dezactivat acum. Introduceți codul PUK pentru a continua. V-au mai rămas # de încercări până când cardul SIM va deveni inutilizabil definitiv. Contactați operatorul pentru detalii.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Prestabilit"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Balon"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogic"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index 435a40cf3c9a..a21dd6d6adab 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -68,19 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Вы ввели неверный пароль несколько раз (<xliff:g id="NUMBER_0">%1$d</xliff:g>).\n\nПовторите попытку через <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Вы начертили неверный графический ключ несколько раз (<xliff:g id="NUMBER_0">%1$d</xliff:g>).\n\nПовторите попытку через <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Неверный PIN-код. Обратитесь к оператору связи, чтобы разблокировать SIM-карту."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Неверный PIN-код. Осталась <xliff:g id="NUMBER_1">%d</xliff:g> попытка.</item>
- <item quantity="few">Неверный PIN-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попытки.</item>
- <item quantity="many">Неверный PIN-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попыток.</item>
- <item quantity="other">Неверный PIN-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попытки.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Неверный PIN-код. Осталась # попытка. Если ввести неправильный PIN-код ещё раз, SIM-карта будет заблокирована и вам придется обратиться к оператору связи.}one{Неверный PIN-код. Осталась # попытка. }few{Неверный PIN-код. Осталось # попытки. }many{Неверный PIN-код. Осталось # попыток. }other{Неверный PIN-код. Осталось # попытки. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM-карта заблокирована навсегда. Обратитесь к оператору связи."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Неверный PUK-код. Осталась <xliff:g id="NUMBER_1">%d</xliff:g> попытка. После этого SIM-карта будет заблокирована навсегда.</item>
- <item quantity="few">Неверный PUK-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попытки. После этого SIM-карта будет заблокирована навсегда.</item>
- <item quantity="many">Неверный PUK-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попыток. После этого SIM-карта будет заблокирована навсегда.</item>
- <item quantity="other">Неверный PUK-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попытки. После этого SIM-карта будет заблокирована навсегда.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Неверный PUK-код. Осталась # попытка. Если ввести неправильный PUK-код ещё раз, SIM-карта будет заблокирована навсегда.}one{Неверный PUK-код. Осталась # попытка. После того как попытки закончатся, SIM-карта будет заблокирована навсегда.}few{Неверный PUK-код. Осталось # попытки. После того как они закончатся, SIM-карта будет заблокирована навсегда.}many{Неверный PUK-код. Осталось # попыток. После того как они закончатся, SIM-карта будет заблокирована навсегда.}other{Неверный PUK-код. Осталось # попытки. После того как попытки закончатся, SIM-карта будет заблокирована навсегда.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Не удалось разблокировать SIM-карту"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Не удалось разблокировать SIM-карту"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Сменить способ ввода"</string>
@@ -95,18 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Устройство было заблокировано вручную"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Не распознано"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"В настройках разрешите фейсконтролю доступ к камере."</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Введите PIN-код. Осталась <xliff:g id="NUMBER_1">%d</xliff:g> попытка.</item>
- <item quantity="few">Введите PIN-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попытки.</item>
- <item quantity="many">Введите PIN-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попыток.</item>
- <item quantity="other">Введите PIN-код. Осталось <xliff:g id="NUMBER_1">%d</xliff:g> попытки.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">SIM-карта отключена. Чтобы продолжить, введите PUK-код. Осталась <xliff:g id="_NUMBER_1">%d</xliff:g> попытка. После этого SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.</item>
- <item quantity="few">SIM-карта отключена. Чтобы продолжить, введите PUK-код. Осталось <xliff:g id="_NUMBER_1">%d</xliff:g> попытки. После этого SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.</item>
- <item quantity="many">SIM-карта отключена. Чтобы продолжить, введите PUK-код. Осталось <xliff:g id="_NUMBER_1">%d</xliff:g> попыток. После этого SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.</item>
- <item quantity="other">SIM-карта отключена. Чтобы продолжить, введите PUK-код. Осталось <xliff:g id="_NUMBER_1">%d</xliff:g> попытки. После этого SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Введите PIN-код. Осталась # попытка. Если указать неверный PIN-код ещё раз, SIM-карта будет заблокирована и вам придется обратиться к оператору связи.}one{Введите PIN-код SIM-карты. Осталась # попытка.}few{Введите PIN-код SIM-карты. Осталось # попытки.}many{Введите PIN-код SIM-карты. Осталось # попыток.}other{Введите PIN-код SIM-карты. Осталось # попытки.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM-карта заблокирована. Чтобы продолжить, введите PUK-код. Осталась # попытка. Если ввести неверный PUK-код, SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.}one{SIM-карта заблокирована. Чтобы продолжить, введите PUK-код. Осталась # попытка. После того как попытки закончатся, SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.}few{SIM-карта заблокирована. Чтобы продолжить, введите PUK-код. Осталось # попытки. После того как они закончатся, SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.}many{SIM-карта заблокирована. Чтобы продолжить, введите PUK-код. Осталось # попыток. После того как они закончатся, SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.}other{SIM-карта заблокирована. Чтобы продолжить, введите PUK-код. Осталось # попытки. После того как попытки закончатся, SIM-карта будет заблокирована навсегда. За подробной информацией обратитесь к оператору связи.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"По умолчанию"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Пузырь"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Стрелки"</string>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index ada19dd42602..0f828c123725 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"ඔබ මුරපදය වාර <xliff:g id="NUMBER_0">%1$d</xliff:g> ක් වැරදියට ටයිප්කොට ඇත. \n\nතත්පර <xliff:g id="NUMBER_1">%2$d</xliff:g> කින් නැවත උත්සහ කරන්න."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"ඔබ <xliff:g id="NUMBER_0">%1$d</xliff:g> වාරයක් අගුළු ඇරීමේ රටාව වැරදියට ඇඳ ඇත. \n\nතත්පර <xliff:g id="NUMBER_1">%2$d</xliff:g> ක් ඇතුළත නැවත උත්සාහ කරන්න."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"වැරදී SIM PIN කේතයකි, ඔබගේ දුරකතනයේ අඟුල හැරීමට ඔබගේ වාහකයා ඔබ දැන් සම්බන්ධ කරගත යුතුය."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">වැරදී SIM PIN කේතයකි, ඔබගේ දුරකථනයේ අඟුල හැරීමට ඔබගේ වාහකයා සම්බන්ධ කරගැනීමට පෙර ඔබ සතුව තවත් උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
- <item quantity="other">වැරදී SIM PIN කේතයකි, ඔබගේ දුරකථනයේ අගුල හැරීමට ඔබගේ වාහකයා සම්බන්ධ කරගැනීමට පෙර ඔබ සතුව තවත් උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{වැරදි SIM PIN කේතයකි, ඔබේ උපාංගයේ අගුළු හැරීමට ඔබ ඔබේ වාහකයා සම්බන්ධ කර ගත යුතු වීමට පෙර ඔබ සතුව # උත්සාහයක් ඉතිරිව ඇත.}one{වැරදි SIM PIN කේතයකි, ඔබ සතුව උත්සාහයන් #ක් ඉතිරිව ඇත. }other{වැරදි SIM PIN කේතයකි, ඔබ සතුව උත්සාහයන් #ක් ඉතිරිව ඇත. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM කාඩ් පත භාවිතා කළ නොහැක. ඔබගේ වාහකය සම්බන්ධ කරගන්න."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">වැරදි SIM PUK කේතයකි, SIM කාඩ්පත ස්ථිරවම භාවිත කළ නොහැකි බවට පත්වීමට පෙර ඔබට තවත් උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
- <item quantity="other">වැරදි SIM PUK කේතයකි, SIM කාඩ්පත ස්ථිරවම භාවිත කළ නොහැකි බවට පත්වීමට පෙර ඔබට තවත් උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{වැරදි SIM PUK කේතයකි, SIM කාඩ්පත ස්ථිරවම භාවිත කළ නොහැකි බවට පත් වීමට පෙර ඔබට # උත්සාහයක් ඉතිරිව ඇත.}one{වැරදි SIM PUK කේතයකි, SIM කාඩ්පත ස්ථිරවම භාවිත කළ නොහැකි බවට පත් වීමට පෙර ඔබට උත්සාහයන් #ක් ඉතිරිව ඇත.}other{වැරදි SIM PUK කේතයකි, SIM කාඩ්පත ස්ථිරවම භාවිත කළ නොහැකි බවට පත් වීමට පෙර ඔබට උත්සාහයන් #ක් ඉතිරිව ඇත.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM PIN මෙහෙයුම අසාර්ථක විය!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUK මෙහෙයුම අසාර්ථක විය!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ආදාන ක්‍රමය මාරු කිරීම"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"උපාංගය හස්තීයව අගුලු දමන ලදී"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"හඳුනා නොගන්නා ලදී"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"මුහුණෙන් අගුලු හැරීමට, සැකසීම් තුළ කැමරා ප්‍රවේශය සක්‍රීය කරන්න"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">SIM PIN ඇතුළු කරන්න, ඔබ සතුව උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
- <item quantity="other">SIM PIN ඇතුළු කරන්න, ඔබ සතුව උත්සාහයන් <xliff:g id="NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">SIM දැන් අබල කර ඇත. දිගටම කරගෙන යාමට PUK කේතය ඇතුළු කරන්න. SIM ස්ථිරවම භාවිත කළ නොහැකි බවට පත් වීමට පෙර ඔබ සතුව උත්සාහයන් <xliff:g id="_NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත. විස්තර සඳහා වාහක සම්බන්ධ කර ගන්න.</item>
- <item quantity="other">SIM දැන් අබල කර ඇත. දිගටම කරගෙන යාමට PUK කේතය ඇතුළු කරන්න. SIM ස්ථිරවම භාවිත කළ නොහැකි බවට පත් වීමට පෙර ඔබ සතුව උත්සාහයන් <xliff:g id="_NUMBER_1">%d</xliff:g>ක් ඉතිරිව ඇත. විස්තර සඳහා වාහක සම්බන්ධ කර ගන්න.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{SIM PIN ඇතුළු කරන්න, ඔබේ උපාංගය අගුළු හැරීමට ඔබේ වාහකය සම්බන්ධ කර ගැනීමට පෙර ඔබ සතුව # උත්සාහයක් ඉතිරිව ඇත.}one{SIM PIN ඇතුළු කරන්න. ඔබ සතුව උත්සාහයන් #ක් ඉතිරිව ඇත.}other{SIM PIN ඇතුළු කරන්න. ඔබ සතුව උත්සාහයන් #ක් ඉතිරිව ඇත.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM දැන් අබල කර ඇත. ඉදිරියට යාමට PUK කේතය ඇතුළු කරන්න. SIM ස්ථිරවම භාවිත කළ නොහැකි බවට පත් වීමට පෙර ඔබ සතුව # උත්සාහයක් ඉතිරිව ඇත. විස්තර සඳහා වාහකය සම්බන්ධ කර ගන්න.}one{SIM දැන් අබල කර ඇත. ඉදිරියට යාමට PUK කේතය ඇතුළු කරන්න. SIM ස්ථිරවම භාවිත කළ නොහැකි බවට පත් වීමට පෙර ඔබ සතුව උත්සාහයන් #ක් ඉතිරිව ඇත. විස්තර සඳහා වාහකය සම්බන්ධ කර ගන්න.}other{SIM දැන් අබල කර ඇත. ඉදිරියට යාමට PUK කේතය ඇතුළු කරන්න. SIM ස්ථිරවම භාවිත කළ නොහැකි බවට පත් වීමට පෙර ඔබ සතුව උත්සාහයන් #ක් ඉතිරිව ඇත. විස්තර සඳහා වාහකය සම්බන්ධ කර ගන්න.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"පෙරනිමි"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"බුබුළ"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"ප්‍රතිසමය"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sk/strings.xml b/packages/SystemUI/res-keyguard/values-sk/strings.xml
index b1bff53ea513..e98157c30335 100644
--- a/packages/SystemUI/res-keyguard/values-sk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sk/strings.xml
@@ -68,19 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Už <xliff:g id="NUMBER_0">%1$d</xliff:g>-krát ste zadali nesprávne heslo. \n\nSkúste to znova o <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Už <xliff:g id="NUMBER_0">%1$d</xliff:g>-krát ste použili nesprávny bezpečnostný vzor. \n\nSkúste to znova o <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Nesprávny kód PIN SIM karty. Teraz musíte kontaktovať svojho operátora, aby vám odomkol zariadenie."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="few">Nesprávny kód PIN SIM karty. Zostávajú vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusy.</item>
- <item quantity="many">Nesprávny kód PIN SIM karty. Zostáva vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusu.</item>
- <item quantity="other">Nesprávny kód PIN SIM karty. Zostáva vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusov.</item>
- <item quantity="one">Nesprávny kód PIN SIM karty. Zostáva vám <xliff:g id="NUMBER_0">%d</xliff:g> pokus. Potom budete musieť kontaktovať operátora, aby vám odomkol zariadenie.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Nesprávny kód PIN SIM karty. Zostáva vám # pokus, potom budete musieť kontaktovať svojho operátora, aby zariadenie odomkol.}few{Nesprávny kód PIN SIM karty. Zostávajú vám # pokusy. }many{Incorrect SIM PIN code, you have # remaining attempts. }other{Nesprávny kód PIN SIM karty. Zostáva vám # pokusov. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM karta je nepoužiteľná. Kontaktujte svojho operátora."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="few">Nesprávny kód PUK SIM karty. Zostávajú vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusy. Potom sa SIM karta natrvalo zablokuje.</item>
- <item quantity="many">Nesprávny kód PUK SIM karty. Zostáva vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusu. Potom sa SIM karta natrvalo zablokuje.</item>
- <item quantity="other">Nesprávny kód PUK SIM karty. Zostáva vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusov. Potom sa SIM karta natrvalo zablokuje.</item>
- <item quantity="one">Nesprávny kód PUK SIM karty. Zostáva vám <xliff:g id="NUMBER_0">%d</xliff:g> pokus. Potom sa vaša SIM karta natrvalo zablokuje.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Nesprávny kód PUK SIM karty. Zostáva vám # pokus, potom sa SIM karta natrvalo zablokuje.}few{Nesprávny kód PUK SIM karty. Zostávajú vám # pokusy, potom sa SIM karta natrvalo zablokuje.}many{Incorrect SIM PUK code, you have # remaining attempts before SIM becomes permanently unusable.}other{Nesprávny kód PUK SIM karty. Zostáva vám # pokusov, potom sa SIM karta natrvalo zablokuje.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Operácia kódu PIN SIM karty zlyhala!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Operácia kódu PUK SIM karty zlyhala!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Prepnúť metódu vstupu"</string>
@@ -95,18 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Zariadenie bolo uzamknuté ručne"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nerozpoznané"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"V nastaveniach zapnite prístup ku kamere"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="few">Zadajte kód PIN SIM karty. Zostávajú vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusy.</item>
- <item quantity="many">Enter SIM PIN. You have <xliff:g id="NUMBER_1">%d</xliff:g> remaining attempts.</item>
- <item quantity="other">Zadajte kód PIN SIM karty. Zostáva vám <xliff:g id="NUMBER_1">%d</xliff:g> pokusov.</item>
- <item quantity="one">Zadajte kód PIN SIM karty. Zostáva vám <xliff:g id="NUMBER_0">%d</xliff:g> pokus, potom budete musieť kontaktovať svojho operátora, aby zariadenie odomkol.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="few">SIM karta je deaktivovaná. Pokračujte zadaním kódu PUK. Zostávajú vám <xliff:g id="_NUMBER_1">%d</xliff:g> pokusy, potom sa SIM karta natrvalo zablokuje. Podrobnosti vám poskytne operátor.</item>
- <item quantity="many">SIM karta je deaktivovaná. Pokračujte zadaním kódu PUK. Zostáva vám <xliff:g id="_NUMBER_1">%d</xliff:g> pokusu, potom sa SIM karta natrvalo zablokuje. Podrobnosti vám poskytne operátor.</item>
- <item quantity="other">SIM karta je deaktivovaná. Pokračujte zadaním kódu PUK. Zostáva vám <xliff:g id="_NUMBER_1">%d</xliff:g> pokusov, potom sa SIM karta natrvalo zablokuje. Podrobnosti vám poskytne operátor.</item>
- <item quantity="one">SIM karta je deaktivovaná. Pokračujte zadaním kódu PUK. Zostáva vám <xliff:g id="_NUMBER_0">%d</xliff:g> pokus, potom sa SIM karta natrvalo zablokuje. Podrobnosti vám poskytne operátor.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Zadajte kód PIN SIM karty. Zostáva vám # pokus, potom budete musieť kontaktovať svojho operátora, aby zariadenie odomkol.}few{Zadajte PIN SIM karty. Zostávajú vám # pokusy.}many{Enter SIM PIN. You have # remaining attempts.}other{Zadajte PIN SIM karty. Zostáva vám # pokusov.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM karta je deaktivovaná. Pokračujte zadaním kódu PUK. Zostáva vám # pokus, potom sa SIM karta natrvalo zablokuje. Podrobnosti vám poskytne operátor.}few{SIM karta je deaktivovaná. Pokračujte zadaním kódu PUK. Zostávajú vám # pokusy, potom sa SIM karta natrvalo zablokuje. Podrobnosti vám poskytne operátor.}many{SIM is now disabled. Enter PUK code to continue. You have # remaining attempts before SIM becomes permanently unusable. Contact carrier for details.}other{SIM karta je deaktivovaná. Pokračujte zadaním kódu PUK. Zostáva vám # pokusov, potom sa SIM karta natrvalo zablokuje. Podrobnosti vám poskytne operátor.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Predvolený"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bublina"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analógový"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml
index 274a24ea4717..1ad19267f820 100644
--- a/packages/SystemUI/res-keyguard/values-sl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml
@@ -68,19 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Geslo ste <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat vnesli napačno. \n\nPoskusite znova čez <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Vzorec za odklepanje ste <xliff:g id="NUMBER_0">%1$d</xliff:g>-krat nepravilno narisali. \n\nPoskusite znova čez <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Napačna koda PIN kartice SIM. Zdaj se boste morali za odklenitev naprave obrniti na operaterja."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Napačna koda PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskus.</item>
- <item quantity="two">Napačna koda PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskusa.</item>
- <item quantity="few">Napačna koda PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskuse.</item>
- <item quantity="other">Napačna koda PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskusov.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Napačna koda PIN kartice SIM. Na voljo imate še # poskus. Nato se boste morali za odklepanje naprave obrniti na operaterja.}one{Napačna koda PIN kartice SIM. Na voljo imate še # poskus. }two{Napačna koda PIN kartice SIM. Na voljo imate še # poskusa. }few{Napačna koda PIN kartice SIM. Na voljo imate še # poskuse. }other{Napačna koda PIN kartice SIM. Na voljo imate še # poskusov. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Kartica SIM ni več uporabna. Obrnite se na operaterja."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Napačna koda PUK kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskus. Potem bo kartica SIM postala trajno neuporabna.</item>
- <item quantity="two">Napačna koda PUK kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskusa. Potem bo kartica SIM postala trajno neuporabna.</item>
- <item quantity="few">Napačna koda PUK kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskuse. Potem bo kartica SIM postala trajno neuporabna.</item>
- <item quantity="other">Napačna koda PUK kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskusov. Potem bo kartica SIM postala trajno neuporabna.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Napačna koda PUK kartice SIM. Na voljo imate še # poskus. Nato bo kartica SIM postala trajno neuporabna.}one{Napačna koda PUK kartice SIM. Na voljo imate še # poskus. Nato bo kartica SIM postala trajno neuporabna.}two{Napačna koda PUK kartice SIM. Na voljo imate še # poskusa. Nato bo kartica SIM postala trajno neuporabna.}few{Napačna koda PUK kartice SIM. Na voljo imate še # poskuse. Nato bo kartica SIM postala trajno neuporabna.}other{Napačna koda PUK kartice SIM. Na voljo imate še # poskusov. Nato bo kartica SIM postala trajno neuporabna.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Postopek za odklepanje s kodo PIN kartice SIM ni uspel."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Postopek za odklepanje s kodo PUK kartice SIM ni uspel."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Preklop načina vnosa"</string>
@@ -95,18 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Naprava je bila ročno zaklenjena"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Ni prepoznano"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Odklepanje z obrazom potrebuje dostop do fotoaparata."</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskus.</item>
- <item quantity="two">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskusa.</item>
- <item quantity="few">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskuse.</item>
- <item quantity="other">Vnesite kodo PIN kartice SIM. Na voljo imate še <xliff:g id="NUMBER_1">%d</xliff:g> poskusov.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še <xliff:g id="_NUMBER_1">%d</xliff:g> poskus. Potem bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.</item>
- <item quantity="two">Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še <xliff:g id="_NUMBER_1">%d</xliff:g> poskusa. Potem bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.</item>
- <item quantity="few">Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še <xliff:g id="_NUMBER_1">%d</xliff:g> poskuse. Potem bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.</item>
- <item quantity="other">Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še <xliff:g id="_NUMBER_1">%d</xliff:g> poskusov. Potem bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Vnesite kodo PIN kartice SIM. Na voljo imate še # poskus. Nato se boste morali za odklepanje naprave obrniti na operaterja.}one{Vnesite kodo PIN kartice SIM. Na voljo imate še # poskus.}two{Vnesite kodo PIN kartice SIM. Na voljo imate še # poskusa.}few{Vnesite kodo PIN kartice SIM. Na voljo imate še # poskuse.}other{Vnesite kodo PIN kartice SIM. Na voljo imate še # poskusov.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še # poskus. Nato bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.}one{Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še # poskus. Nato bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.}two{Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še # poskusa. Nato bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.}few{Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še # poskuse. Nato bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.}other{Kartica SIM je zdaj onemogočena. Če želite nadaljevati, vnesite kodo PUK. Na voljo imate še # poskusov. Nato bo kartica SIM postala trajno neuporabna. Za podrobnosti se obrnite na operaterja.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Privzeto"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Mehurček"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogno"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sq/strings.xml b/packages/SystemUI/res-keyguard/values-sq/strings.xml
index 58773aeea473..7eb442c19849 100644
--- a/packages/SystemUI/res-keyguard/values-sq/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sq/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"E ke shkruar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë gabimisht fjalëkalimin.\n\nProvo sërish për <xliff:g id="NUMBER_1">%2$d</xliff:g> sekonda."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Ke tentuar <xliff:g id="NUMBER_0">%1$d</xliff:g> herë pa sukses për të vizatuar motivin tënd. \n\nProvo sërish për <xliff:g id="NUMBER_1">%2$d</xliff:g> sekonda."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Kodi PIN i kartës SIM është i pasaktë. Tani duhet të kontaktosh me operatorin për ta shkyçur pajisjen tënde."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Kodi PIN i kartës SIM është i pasaktë. Të kanë mbetur edhe <xliff:g id="NUMBER_1">%d</xliff:g> tentativa.</item>
- <item quantity="one">Kodi PIN i kartës SIM është i pasaktë. Të ka mbetur edhe <xliff:g id="NUMBER_0">%d</xliff:g> tentativë para se të duhet të kontaktosh me operatorin tënd celular për të shkyçur pajisjen.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Kodi PIN i kartës SIM është i pasaktë. Të ka mbetur edhe # përpjekje përpara se të të duhet të kontaktosh me operatorin celular për ta shkyçur pajisjen.}other{Kodi PIN i kartës SIM është i pasaktë. Të kanë mbetur edhe # përpjekje. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Karta SIM është e papërdorshme. Kontakto me operatorin."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">PUK-u i kartës SIM është i pasaktë. Të kanë mbetur edhe <xliff:g id="NUMBER_1">%d</xliff:g> tentativa para se karta SIM të bëhet e papërdorshme përgjithmonë.</item>
- <item quantity="one">PUK-u i kartës SIM është i pasaktë. Të ka mbetur edhe <xliff:g id="NUMBER_0">%d</xliff:g> tentativë para se karta SIM të bëhet e papërdorshme përgjithmonë.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Kodi PUK i kartës SIM është i pasaktë. Të ka mbetur edhe # përpjekje përpara se karta SIM të bëhet përgjithmonë e papërdorshme.}other{Kodi PUK i kartës SIM është i pasaktë. Të kanë mbetur edhe # përpjekje përpara se karta SIM të bëhet përgjithmonë e papërdorshme.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Operacioni i kodit PIN të kartës SIM dështoi!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Operacioni i kodit PUK të kartës SIM dështoi!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Ndërro metodën e hyrjes"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Pajisja është kyçur manualisht"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Nuk njihet"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Për \"Shkyçjen me fytyrë\", aktivizo qasjen te kamera te \"Cilësimet\""</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Fut kodin PIN të kartës SIM. Të kanë mbetur edhe <xliff:g id="NUMBER_1">%d</xliff:g> tentativa.</item>
- <item quantity="one">Fut kodin PIN të kartës SIM. Të ka mbetur edhe <xliff:g id="NUMBER_0">%d</xliff:g> tentativë para se të duhet të kontaktosh me operatorin tënd celular për ta shkyçur pajisjen.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">Karta SIM tani është çaktivizuar. Fut kodin PUK për të vazhduar. Të kanë mbetur edhe <xliff:g id="_NUMBER_1">%d</xliff:g> përpjekje përpara se karta SIM të bëhet përgjithmonë e papërdorshme. Kontakto me operatorin për detaje.</item>
- <item quantity="one">Karta SIM tani është çaktivizuar. Fut kodin PUK për të vazhduar. Të ka mbetur edhe <xliff:g id="_NUMBER_0">%d</xliff:g> përpjekje përpara se karta SIM të bëhet përgjithmonë e papërdorshme. Kontakto me operatorin për detaje.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Fut kodin PIN të kartës SIM. Të ka mbetur edhe # përpjekje përpara se të të duhet të kontaktosh me operatorin celular për ta shkyçur pajisjen.}other{Fut kodin PIN të kartës SIM. Të kanë mbetur edhe # përpjekje.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Karta SIM është çaktivizuar tani. Fut kodin PUK për të vazhduar. Të ka mbetur edhe # përpjekje përpara se karta SIM të bëhet përgjithmonë e papërdorshme. Kontakto me operatorin celular për detajet.}other{Karta SIM tani është çaktivizuar. Fut kodin PUK për të vazhduar. Të kanë mbetur edhe # përpjekje përpara se karta SIM të bëhet përgjithmonë e papërdorshme. Kontakto me operatorin celular për detajet.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"E parazgjedhur"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Flluskë"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analoge"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml
index 15c31a0eb981..568be9f3ad0b 100644
--- a/packages/SystemUI/res-keyguard/values-sr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml
@@ -68,17 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Унели сте погрешну лозинку <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. \n\nПробајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Нацртали сте нетачан шаблон за откључавање <xliff:g id="NUMBER_0">%1$d</xliff:g> пута. \n\nПробајте поново за <xliff:g id="NUMBER_1">%2$d</xliff:g> сек."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Нетачан PIN кôд за SIM. Сада морате да контактирате мобилног оператера да бисте откључали уређај."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Нетачан PIN кôд за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушај.</item>
- <item quantity="few">Нетачан PIN кôд за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушаја.</item>
- <item quantity="other">Нетачан PIN кôд за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушаја.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Нетачан PIN за SIM кôд. Имате још # покушај, а онда морате да се обратите мобилном оператеру да бисте откључали уређај.}one{Нетачан PIN за SIM кôд. Имате још # покушај. }few{Нетачан PIN за SIM кôд. Имате још # покушаја. }other{Нетачан PIN за SIM кôд. Имате још # покушаја. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM картица је неупотребљива. Контактирајте мобилног оператера."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Нетачан PUK кôд за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушај пре него што SIM картица постане трајно неупотребљива.</item>
- <item quantity="few">Нетачан PUK кôд за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушаја пре него што SIM картица постане трајно неупотребљива.</item>
- <item quantity="other">Нетачан PUK кôд за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушаја пре него што SIM картица постане трајно неупотребљива.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Нетачан SIM PUK кôд. Имате још # покушај пре него што SIM картица постане трајно неупотребљива.}one{Нетачан PUK кôд за SIM. Имате још # покушај пре него што SIM картица постане трајно неупотребљива.}few{Нетачан PUK кôд за SIM. Имате још # покушаја пре него што SIM картица постане трајно неупотребљива.}other{Нетачан PUK кôд за SIM. Имате још # покушаја пре него што SIM картица постане трајно неупотребљива.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Радња са PIN кодом за SIM није успела!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Радња са PUK кодом за SIM није успела!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Промени метод уноса"</string>
@@ -93,16 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Уређај је ручно закључан"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Није препознат"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Откључавање лицем тражи приступ камери у Подешавањима"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Унесите PIN за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушај.</item>
- <item quantity="few">Унесите PIN за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушаја.</item>
- <item quantity="other">Унесите PIN за SIM. Имате још <xliff:g id="NUMBER_1">%d</xliff:g> покушаја.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још <xliff:g id="_NUMBER_1">%d</xliff:g> покушај пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.</item>
- <item quantity="few">SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још <xliff:g id="_NUMBER_1">%d</xliff:g> покушаја пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.</item>
- <item quantity="other">SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још <xliff:g id="_NUMBER_1">%d</xliff:g> покушаја пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Унесите PIN за SIM. Још # покушај и мораћете да се обратите мобилном оператеру да бисте откључали уређај.}one{Унесите PIN за SIM. Имате још # покушај.}few{Унесите PIN за SIM. Имате још # покушаја.}other{Унесите PIN за SIM. Имате још # покушаја.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још # покушај пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.}one{SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још # покушај пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.}few{SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још # покушаја пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.}other{SIM је сада онемогућен. Унесите PUK кôд да бисте наставили. Имате још # покушаја пре него што SIM постане трајно неупотребљив. Детаљне информације потражите од мобилног оператера.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Подразумевани"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Мехурићи"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Аналогни"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sv/strings.xml b/packages/SystemUI/res-keyguard/values-sv/strings.xml
index 8f8bddeed051..e0414c82c928 100644
--- a/packages/SystemUI/res-keyguard/values-sv/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sv/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%1$d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%2$d</xliff:g> sekunder."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Du angav fel pinkod för SIM-kortet och måste nu kontakta operatören för att låsa upp enheten."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Du angav fel pinkod för SIM-kortet. <xliff:g id="NUMBER_1">%d</xliff:g> försök återstår.</item>
- <item quantity="one">Du angav fel pinkod för SIM-kortet. <xliff:g id="NUMBER_0">%d</xliff:g> försök återstår innan du måste kontakta operatören för att låsa upp enheten.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Du angav fel pinkod för SIM-kortet. # försök återstår innan du måste kontakta operatören för att låsa upp enheten.}other{Du angav fel pinkod för SIM-kortet. # försök återstår. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM-kortet är obrukbart. Kontakta operatören."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Du angav fel PUK-kod för SIM-kortet. <xliff:g id="NUMBER_1">%d</xliff:g> försök återstår innan SIM-kortet blir obrukbart.</item>
- <item quantity="one">Du angav fel PUK-kod för SIM-kortet. <xliff:g id="NUMBER_0">%d</xliff:g> försök återstår innan SIM-kortet blir obrukbart.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Du angav fel PUK-kod för SIM-kortet. # försök återstår innan SIM-kortet blir obrukbart.}other{Du angav fel PUK-kod för SIM-kortet. # försök återstår innan SIM-kortet blir obrukbart.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Det gick inte att låsa upp med pinkoden för SIM-kortet."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Det gick inte att låsa upp med PUK-koden för SIM-kortet."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Byt inmatningsmetod"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Enheten har låsts manuellt"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Identifierades inte"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"För ansiktslås aktiverar du kameraåtkomst i Inställn."</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Ange pinkod för SIM-kortet. <xliff:g id="NUMBER_1">%d</xliff:g> försök återstår.</item>
- <item quantity="one">Ange pinkod för SIM-kortet. <xliff:g id="NUMBER_0">%d</xliff:g> försök återstår innan du måste kontakta operatören för att låsa upp enheten.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM-kortet är inaktiverat. Ange PUK-koden om du vill fortsätta. <xliff:g id="_NUMBER_1">%d</xliff:g> försök återstår innan SIM-kortet blir obrukbart. Kontakta operatören för mer information.</item>
- <item quantity="one">SIM-kortet är inaktiverat. Ange PUK-koden om du vill fortsätta. <xliff:g id="_NUMBER_0">%d</xliff:g> försök återstår innan SIM-kortet blir obrukbart. Kontakta operatören för mer information.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Ange pinkod för SIM-kortet. # försök återstår innan du måste kontakta operatören för att låsa upp enheten.}other{Ange pinkod för SIM-kortet. # försök återstår.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM-kortet är inaktiverat. Ange PUK-koden om du vill fortsätta. # försök återstår innan SIM-kortet blir obrukbart. Kontakta operatören för mer information.}other{SIM-kortet är inaktiverat. Ange PUK-koden om du vill fortsätta. # försök återstår innan SIM-kortet blir obrukbart. Kontakta operatören för mer information.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Standard"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bubbla"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sw/strings.xml b/packages/SystemUI/res-keyguard/values-sw/strings.xml
index 06d168b488c5..70483649fb77 100644
--- a/packages/SystemUI/res-keyguard/values-sw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sw/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Umeandika vibaya nenosiri lako mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Umechora vibaya mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Nambari ya PIN ya SIM si sahihi, sasa lazima uwasiliane na mtoa huduma za mtandao ndipo ufungue kifaa chako."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Nambari ya PIN ya SIM si sahihi. Una nafasi zingine <xliff:g id="NUMBER_1">%d</xliff:g> za kujaribu.</item>
- <item quantity="one">Nambari ya PIN ya SIM si sahihi. Una nafasi zingine <xliff:g id="NUMBER_0">%d</xliff:g> za kujaribu kabla ulazimike kuwasiliana na mtoa huduma wako ili akufungulie kifaa chako.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Nambari ya PIN ya SIM si sahihi, unaweza kujaribu mara # kabla ya kulazimika kuwasiliana na mtoa huduma wako ili afungue kifaa chako.}other{Nambari ya PIN ya SIM si sahihi, unaweza kujaribu mara #. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM haiwezi kutumika. Wasiliana na mtoa huduma wako."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Nambari ya PUK ya SIM si sahihi, bado unaweza kujaribu mara <xliff:g id="NUMBER_1">%d</xliff:g> kabla ya SIM kuacha kutumika kabisa.</item>
- <item quantity="one">Nambari ya PUK ya SIM si sahihi, bado unaweza kujaribu mara <xliff:g id="NUMBER_0">%d</xliff:g> kabla ya SIM kuacha kutumika kabisa.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Nambari ya PUK ya SIM si sahihi, unaweza kujaribu mara # kabla ya SIM kuacha kutumika kabisa.}other{Nambari ya PUK ya SIM si sahihi, unaweza kujaribu mara # kabla ya SIM kuacha kutumika kabisa.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Utendakazi wa PIN ya SIM haujafanikiwa!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Utendakazi wa PUK ya SIM haujafanikiwa!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Kubadili mbinu ya kuingiza data"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Umefunga kifaa mwenyewe"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Haitambuliwi"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Ili ufungue kwa Uso, ruhusu kamera ifikiwe"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Weka PIN ya SIM. Zimesalia mara <xliff:g id="NUMBER_1">%d</xliff:g> za kujaribu.</item>
- <item quantity="one">Weka PIN ya SIM. Ukijaribu tena mara <xliff:g id="NUMBER_0">%d</xliff:g> bila kufaulu, kifaa chako kitafungwa na utalazimika uwasiliane na mtoa huduma wako ili akifungue.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">Sasa SIM imefungwa. Weka msimbo wa PUK ili uendelee. Umesalia na majaribio <xliff:g id="_NUMBER_1">%d</xliff:g> kabla ya SIM kuacha kufanya kazi kabisa. Wasiliana na mtoa huduma kwa maelezo.</item>
- <item quantity="one">Sasa SIM imefungwa. Weka msimbo wa PUK ili uendelee. Umesalia na jaribio <xliff:g id="_NUMBER_0">%d</xliff:g> kabla ya SIM kuacha kufanya kazi kabisa. Wasiliana na mtoa huduma kwa maelezo.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Weka PIN ya SIM. Unaweza kujaribu mara # kuweka PIN ya SIM kabla ya kulazimika kuwasiliana na mtoa huduma wako ili afungue kifaa chako endapo kitafungwa.}other{Weka PIN ya SIM. Unaweza kujaribu mara #.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Sasa SIM imefungwa. Weka msimbo wa PUK ili uendelee. Unaweza kujaribu mara # kabla ya SIM kuacha kutumika kabisa. Wasiliana na mtoa huduma kwa maelezo.}other{Sasa SIM imefungwa. Weka msimbo wa PUK ili uendelee. Unaweza kujaribu mara # kabla ya SIM kuacha kutumika kabisa. Wasiliana na mtoa huduma kwa maelezo.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Chaguomsingi"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Kiputo"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analogi"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index 3531aa367ec4..ca519353c84a 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"உங்கள் கடவுச்சொல்லை <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக உள்ளிட்டுவிட்டீர்கள். \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> வினாடிகளில் மீண்டும் முயலவும்."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"அன்லாக் பேட்டர்னை, <xliff:g id="NUMBER_0">%1$d</xliff:g> முறை தவறாக வரைந்துவிட்டீர்கள். \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> வினாடிகளில் மீண்டும் முயலவும்."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"சிம்மின் பின் குறியீடு தவறானது. இனி சாதனத்தை அன்லாக் செய்ய, உங்கள் தொலைத்தொடர்பு நிறுவனத்தைத் தொடர்புகொள்ள வேண்டும்."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">சிம்மின் பின் குறியீடு தவறானது, இன்னும் நீங்கள் <xliff:g id="NUMBER_1">%d</xliff:g> முறை முயலலாம்.</item>
- <item quantity="one">சிம்மின் பின் குறியீடு தவறானது, மேலும் <xliff:g id="NUMBER_0">%d</xliff:g> முயற்சிகளுக்குப் பின்னர், உங்கள் தொலைத்தொடர்பு நிறுவனத்தைத் தொடர்பு கொண்டு மட்டுமே சாதனத்தை அன்லாக் செய்ய முடியும்.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{சிம் பின் குறியீடு தவறானது. உங்கள் சாதனத்தை அன்லாக் செய்ய, உங்கள் மொபைல் நிறுவனத்தைத் தொடர்புகொள்வதற்கு முன்பு மேலும் # முறை முயலலாம்.}other{சிம் பின் குறியீடு தவறானது. இன்னும் # முறை மட்டுமே முயலலாம். }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"பயன்படுத்த முடியாத சிம். உங்கள் தொலைத்தொடர்பு நிறுவனத்தைத் தொடர்புகொள்ளவும்."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">சிம்மின் PUK குறியீடு தவறானது, மேலும் <xliff:g id="NUMBER_1">%d</xliff:g> முறை முயலலாம். தொடர்ந்து தவறான குறியீடு உள்ளிடப்பட்டால், அதன் பிறகு சிம்மை நிரந்தரமாகப் பயன்படுத்த முடியாமல் போகும்.</item>
- <item quantity="one">சிம்மின் PUK குறியீடு தவறானது, மேலும் <xliff:g id="NUMBER_0">%d</xliff:g> முறை முயலலாம். தொடர்ந்து தவறான குறியீடு உள்ளிடப்பட்டால், அதன் பிறகு சிம்மை நிரந்தரமாகப் பயன்படுத்த முடியாமல் போகும்.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{சிம் PUK குறியீடு தவறானது. சிம் நிரந்தரமாகப் பயன்படுத்த முடியாமல் போவதற்கு முன்பு நீங்கள் # முறை முயலலாம்.}other{சிம் PUK குறியீடு தவறானது. சிம் நிரந்தரமாகப் பயன்படுத்த முடியாமல் போவதற்கு முன்பு நீங்கள் # முறை முயலலாம்.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"சிம் பின் செயல்பாடு தோல்வியடைந்தது!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"சிம் PUK செயல்பாடு தோல்வியடைந்தது!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"உள்ளீட்டு முறையை மாற்றும்"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"பயனர் சாதனத்தைப் பூட்டியுள்ளார்"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"அடையாளங்காணபடவில்லை"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"முகம் காட்டித் திறத்தல் அம்சத்தைப் பயன்படுத்த, அமைப்புகளில் கேமரா அணுகலை இயக்கவும்"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">சிம் பின்னை உள்ளிடவும். மேலும், <xliff:g id="NUMBER_1">%d</xliff:g> வாய்ப்புகள் மீதமுள்ளன.</item>
- <item quantity="one">சிம் பின்னை உள்ளிடவும். மீதமுள்ள <xliff:g id="NUMBER_0">%d</xliff:g> வாய்ப்பில் தவறுதலான பின் உள்ளிடப்பட்டால், உங்கள் தொலைத்தொடர்பு நிறுவனத்தைத் தொடர்பு கொண்டு மட்டுமே சாதனத்தை அன்லாக் செய்ய முடியும்.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">சிம் தற்போது முடக்கப்பட்டுள்ளது. தொடர்வதற்கு, PUK குறியீட்டை உள்ளிடவும். நீங்கள் <xliff:g id="_NUMBER_1">%d</xliff:g> முறை மட்டுமே முயற்சிக்க முடியும். அதன்பிறகு சிம் நிரந்தரமாக முடக்கப்படும். விவரங்களுக்கு, மொபைல் நிறுவனத்தைத் தொடர்புகொள்ளவும்.</item>
- <item quantity="one">சிம் தற்போது முடக்கப்பட்டுள்ளது. தொடர்வதற்கு, PUK குறியீட்டை உள்ளிடவும். நீங்கள் <xliff:g id="_NUMBER_0">%d</xliff:g> முறை மட்டுமே முயற்சிக்க முடியும். அதன்பிறகு சிம் நிரந்தரமாக முடக்கப்படும். விவரங்களுக்கு, மொபைல் நிறுவனத்தைத் தொடர்புகொள்ளவும்.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{சிம் பின்னை உள்ளிடவும். உங்கள் சாதனத்தை அன்லாக் செய்ய, உங்கள் மொபைல் நிறுவனத்தைத் தொடர்புகொள்வதற்கு முன்பு மேலும் # முறை முயலலாம்.}other{சிம் பின்னை உள்ளிடவும். இன்னும் # முறை மட்டுமே முயல முடியும்.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{சிம் இப்போது முடக்கப்பட்டுள்ளது. தொடர்வதற்கு PUK குறியீட்டை உள்ளிடவும். நீங்கள் # முறை மட்டுமே முயற்சிக்க முடியும். அதன்பிறகு சிம் நிரந்தரமாக முடக்கப்படும். விவரங்களுக்கு மொபைல் நிறுவனத்தைத் தொடர்புகொள்ளவும்.}other{சிம் இப்போது முடக்கப்பட்டுள்ளது. தொடர்வதற்கு PUK குறியீட்டை உள்ளிடவும். நீங்கள் # முறை மட்டுமே முயற்சிக்க முடியும். அதன்பிறகு சிம் நிரந்தரமாக முடக்கப்படும். விவரங்களுக்கு மொபைல் நிறுவனத்தைத் தொடர்புகொள்ளவும்.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"இயல்பு"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"பபிள்"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"அனலாக்"</string>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index 9a9e803cb72a..b6ae96d1dea7 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"మీరు మీ పాస్‌వర్డ్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM పిన్ కోడ్ తప్పు, ఇప్పుడు మీ డివైజ్‌ను అన్‌లాక్ చేయాలంటే, మీరు తప్పనిసరిగా మీ క్యారియర్‌ను సంప్రదించాలి."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">SIM పిన్ కోడ్ తప్పు, మీకు మరో <xliff:g id="NUMBER_1">%d</xliff:g> ప్రయత్నాలు మిగిలి ఉన్నాయి.</item>
- <item quantity="one">SIM పిన్ కోడ్ తప్పు, మీకు మరో <xliff:g id="NUMBER_0">%d</xliff:g> ప్రయత్నం మిగిలి ఉంది, ఆ తర్వాత మీ డివైజ్‌ను అన్‌లాక్ చేయాలంటే, మీరు తప్పనిసరిగా మీ క్యారియర్‌ని సంప్రదించాలి.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{తప్పు SIM PIN కోడ్, మీరు మీ డివైజ్‌ను అన్‌లాక్ చేయడానికి మీరు తప్పనిసరిగా మీ క్యారియర్‌ను కాంటాక్ట్ చేయడానికి ముందు మీకు # ప్రయత్నం మిగిలి ఉంది.}other{తప్పు SIM PIN కోడ్, మీకు # ప్రయత్నాలు మిగిలి ఉన్నాయి. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM నిరుపయోగకరంగా మారింది. మీ క్యారియర్‌ను సంప్రదించండి."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">SIM PUK కోడ్ తప్పు, మీకు మరో <xliff:g id="NUMBER_1">%d</xliff:g> ప్రయత్నాలు మిగిలి ఉన్నాయి, ఆ తర్వాత SIM శాశ్వతంగా నిరుపయోగకరంగా మారుతుంది.</item>
- <item quantity="one">SIM PUK కోడ్ తప్పు, మీకు మరో <xliff:g id="NUMBER_0">%d</xliff:g> ప్రయత్నం మిగిలి ఉంది, ఆ తర్వాత SIM శాశ్వతంగా నిరుపయోగకరంగా మారుతుంది.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{తప్పు SIM PUK కోడ్, SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు # ప్రయత్నం మిగిలి ఉంది.}other{తప్పు SIM PUK కోడ్, SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు # ప్రయత్నాలు మిగిలి ఉన్నాయి.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM పిన్ చర్య విఫలమైంది!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUK చర్య విఫలమైంది!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ఇన్‌పుట్ పద్ధతిని మార్చు"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"పరికరం మాన్యువల్‌గా లాక్ చేయబడింది"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"గుర్తించలేదు"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"ఫేస్ అన్‌లాక్ వాడేందుకు కెమెరా యాక్సెస్ ఆన్‌లో ఉండాలి"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">SIM పిన్‌ని నమోదు చేయండి. మీకు <xliff:g id="NUMBER_1">%d</xliff:g> ప్రయత్నలు మిగిలి ఉన్నాయి.</item>
- <item quantity="one">SIM పిన్‌ని నమోదు చేయండి, మీరు మీ పరికరాన్ని అన్‌లాక్ చేయడానికి తప్పనిసరిగా మీ క్యారియర్‌ను సంప్రదించడానికి ముందు మీకు <xliff:g id="NUMBER_0">%d</xliff:g> ప్రయత్నం మిగిలి ఉంది.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM ఇప్పుడు నిలిపివేయబడింది. PUK కోడ్‌ను నమోదు చేయండి. SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు <xliff:g id="_NUMBER_1">%d</xliff:g> ప్రయత్నాలు మిగిలి ఉన్నాయి. వివరాల కోసం కారియర్‌ను సంప్రదించండి.</item>
- <item quantity="one">SIM ఇప్పుడు నిలిపివేయబడింది. PUK కోడ్‌ను నమోదు చేయండి. SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు <xliff:g id="_NUMBER_0">%d</xliff:g> ప్రయత్నం మిగిలి ఉంది వివరాల కోసం కారియర్‌ను సంప్రదించండి.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{SIM PINను ఎంటర్ చేయండి. మీరు మీ పరికరాన్ని అన్‌లాక్ చేయడానికి మీరు తప్పనిసరిగా మీ క్యారియర్‌ను కాంటాక్ట్ చేయడానికి ముందు మీకు # ప్రయత్నం మిగిలి ఉంది.}other{SIM PINను ఎంటర్ చేయండి. మీకు # ప్రయత్నాలు మిగిలి ఉన్నాయి.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM ఇప్పుడు డిజేబుల్ చేయబడింది. కొనసాగించడానికి PUK కోడ్‌ను ఎంటర్ చేయండి. SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు # ప్రయత్నం మిగిలి ఉంది. వివరాల కోసం క్యారియర్‌ను కాంటాక్ట్ చేయండి.}other{SIM ఇప్పుడు డిజేబుల్ చేయబడింది. కొనసాగించడానికి PUK కోడ్‌ను ఎంటర్ చేయండి. SIM శాశ్వతంగా నిరుపయోగం కాకుండా ఉండటానికి మీకు # ప్రయత్నాలు మిగిలి ఉన్నాయి. వివరాల కోసం క్యారియర్‌ను కాంటాక్ట్ చేయండి.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"ఆటోమేటిక్"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"బబుల్"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"ఎనలాగ్"</string>
diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml
index 69920542d20f..00092bb3da3a 100644
--- a/packages/SystemUI/res-keyguard/values-th/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-th/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"คุณพิมพ์รหัสผ่านไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว \n\nโปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%2$d</xliff:g> วินาที"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%1$d</xliff:g> ครั้งแล้ว \n\nโปรดลองอีกครั้งในอีก <xliff:g id="NUMBER_1">%2$d</xliff:g> วินาที"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"รหัส PIN ของซิมไม่ถูกต้อง ตอนนี้คุณต้องติดต่อผู้ให้บริการเพื่อปลดล็อกอุปกรณ์ของคุณ"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">รหัส PIN ของซิมไม่ถูกต้อง คุณพยายามได้อีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง</item>
- <item quantity="one">รหัส PIN ของซิมไม่ถูกต้อง คุณพยายามได้อีก <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งก่อนที่จะต้องติดต่อผู้ให้บริการเพื่อปลดล็อกอุปกรณ์ของคุณ</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{รหัส PIN ของซิมไม่ถูกต้อง คุณพยายามได้อีก # ครั้งก่อนที่จะต้องติดต่อผู้ให้บริการเพื่อปลดล็อกอุปกรณ์}other{รหัส PIN ของซิมไม่ถูกต้อง คุณพยายามได้อีก # ครั้ง }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"ซิมไม่สามารถใช้งานได้ โปรดติดต่อผู้ให้บริการ"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">รหัส PUK ของซิมไม่ถูกต้อง คุณพยายามได้อีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้งก่อนที่ซิมจะไม่สามารถใช้งานได้อย่างถาวร</item>
- <item quantity="one">รหัส PUK ของซิมไม่ถูกต้อง คุณพยายามได้อีก <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งก่อนที่ซิมจะไม่สามารถใช้งานได้อย่างถาวร</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{รหัส PUK ของซิมไม่ถูกต้อง คุณพยายามได้อีก # ครั้งก่อนที่ซิมจะใช้งานไม่ได้อย่างถาวร}other{รหัส PUK ของซิมไม่ถูกต้อง คุณพยายามได้อีก # ครั้งก่อนที่ซิมจะใช้งานไม่ได้อย่างถาวร}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"การปลดล็อกด้วย PIN ของซิมล้มเหลว!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"การปลดล็อกด้วย PUK ของซิมล้มเหลว!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"สลับวิธีการป้อนข้อมูล"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"มีการล็อกอุปกรณ์ด้วยตัวเอง"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"ไม่รู้จัก"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"เปิดการเข้าถึงกล้องในการตั้งค่าเพื่อใช้การปลดล็อกด้วยใบหน้า"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">โปรดป้อน PIN ของซิม คุณพยายามได้อีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง</item>
- <item quantity="one">โปรดป้อน PIN ของซิม คุณพยายามได้อีก <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งก่อนที่จะต้องติดต่อผู้ให้บริการเพื่อปลดล็อกอุปกรณ์</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">ซิมถูกปิดใช้งานในขณะนี้ โปรดป้อนรหัส PUK เพื่อทำต่อ คุณพยายามได้อีก <xliff:g id="_NUMBER_1">%d</xliff:g> ครั้งก่อนที่ซิมจะไม่สามารถใช้งานได้อย่างถาวร โปรดติดต่อสอบถามรายละเอียดจากผู้ให้บริการ</item>
- <item quantity="one">ซิมถูกปิดใช้งานในขณะนี้ โปรดป้อนรหัส PUK เพื่อทำต่อ คุณพยายามได้อีก <xliff:g id="_NUMBER_0">%d</xliff:g> ครั้งก่อนที่ซิมจะไม่สามารถใช้งานได้อย่างถาวร โปรดติดต่อสอบถามรายละเอียดจากผู้ให้บริการ</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{ป้อน PIN ของซิม คุณพยายามได้อีก # ครั้งก่อนที่จะต้องติดต่อผู้ให้บริการเพื่อปลดล็อกอุปกรณ์}other{ป้อน PIN ของซิม คุณลองได้อีก # ครั้ง}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{ตอนนี้ซิมถูกปิดใช้แล้ว ป้อนรหัส PUK เพื่อดำเนินการต่อ คุณพยายามได้อีก # ครั้งก่อนที่ซิมจะใช้งานไม่ได้อย่างถาวร โปรดติดต่อสอบถามรายละเอียดจากผู้ให้บริการ}other{ตอนนี้ซิมถูกปิดใช้แล้ว ป้อนรหัส PUK เพื่อดำเนินการต่อ คุณพยายามได้อีก # ครั้งก่อนที่ซิมจะใช้งานไม่ได้อย่างถาวร โปรดติดต่อสอบถามรายละเอียดจากผู้ให้บริการ}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"ค่าเริ่มต้น"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"บับเบิล"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"แอนะล็อก"</string>
diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml
index 12b7635fe637..7b07251e7882 100644
--- a/packages/SystemUI/res-keyguard/values-tl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Na-type mo nang hindi tama ang iyong password nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%2$d</xliff:g> (na) segundo."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%2$d</xliff:g> (na) segundo."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Mali ang PIN code ng SIM, dapat ka nang makipag-ugnayan sa iyong carrier upang i-unlock ang iyong device."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Mali ang PIN code ng SIM, mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> natitirang pagsubok.</item>
- <item quantity="other">Mali ang PIN code ng SIM, mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> na natitirang pagsubok.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Mali ang PIN code ng SIM, mayroon kang # natitirang pagsubok bago mo kailanganing makipag-ugnayan sa iyong carrier para ma-unlock ang device mo.}one{Mali ang PIN code ng SIM, mayroon kang # natitirang pagsubok. }other{Mali ang PIN code ng SIM, mayroon kang # na natitirang pagsubok. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Hindi magagamit ang SIM. Makipag-ugnayan sa iyong carrier."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Mali ang PUK code ng SIM, mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> natitirang pagsubok bago tuluyang hindi magamit ang SIM.</item>
- <item quantity="other">Mali ang PUK code ng SIM, mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> na natitirang pagsubok bago tuluyang hindi magamit ang SIM.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Mali ang PUK code ng SIM, mayroon kang # natitirang pagsubok bago tuluyang hindi magamit ang SIM.}one{Mali ang PUK code ng SIM, mayroon kang # natitirang pagsubok bago tuluyang hindi magamit ang SIM.}other{Mali ang PUK code ng SIM, mayroon kang # na natitirang pagsubok bago tuluyang hindi magamit ang SIM.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Nabigo ang operasyon ng PIN ng SIM!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Nabigo ang operasyon ng PUK ng SIM!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Magpalit ng pamamaraan ng pag-input"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Manual na na-lock ang device"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Hindi nakilala"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Para sa Pag-unlock Gamit ang Mukha, i-on ang access ng camera sa Mga Setting"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Ilagay ang PIN ng SIM. Mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> natitirang pagsubok.</item>
- <item quantity="other">Ilagay ang PIN ng SIM. Mayroon kang <xliff:g id="NUMBER_1">%d</xliff:g> na natitirang pagsubok.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">Naka-disable na ang SIM. Ilagay ang PUK code upang magpatuloy. Mayroon kang <xliff:g id="_NUMBER_1">%d</xliff:g> natitirang pagsubok bago tuluyang hindi magamit ang SIM. Makipag-ugnayan sa carrier para sa mga detalye.</item>
- <item quantity="other">Naka-disable na ang SIM. Ilagay ang PUK code upang magpatuloy. Mayroon kang <xliff:g id="_NUMBER_1">%d</xliff:g> na natitirang pagsubok bago tuluyang hindi magamit ang SIM. Makipag-ugnayan sa carrier para sa mga detalye.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Ilagay ang PIN ng SIM. Mayroon kang # natitirang pagsubok bago mo kailanganing makipag-ugnayan sa iyong carrier para ma-unlock ang device mo.}one{Ilagay ang PIN ng SIM. Mayroon kang # natitirang pagsubok.}other{Ilagay ang PIN ng SIM. Mayroon kang # na natitirang pagsubok.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{Na-disable na ang SIM. Ilagay ang PUK code para magpatuloy. Mayroon kang # natitirang pagsubok bago tuluyang hindi magamit ang SIM. Makipag-ugnayan sa carrier para sa mga detalye.}one{Na-disable na ang SIM. Ilagay ang PUK code para magpatuloy. Mayroon kang # natitirang pagsubok bago tuluyang hindi magamit ang SIM. Makipag-ugnayan sa carrier para sa mga detalye.}other{Na-disable na ang SIM. Ilagay ang PUK code para magpatuloy. Mayroon kang # na natitirang pagsubok bago tuluyang hindi magamit ang SIM. Makipag-ugnayan sa carrier para sa mga detalye.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Default"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bubble"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml
index fb08042a81f0..7ec564239cd0 100644
--- a/packages/SystemUI/res-keyguard/values-tr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Şifrenizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış yazdınız. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> saniye içinde tekrar deneyin."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış çizdiniz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> saniye içinde tekrar deneyin."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Yanlış SIM PIN kodu. Cihazınızın kilidini açmak için artık operatörünüzle bağlantı kurmanız gerekiyor."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Yanlış SIM PIN kodu, <xliff:g id="NUMBER_1">%d</xliff:g> deneme hakkınız kaldı.</item>
- <item quantity="one">Yanlış SIM PIN kodu. Cihazının kilidini açmak için operatörünüzle bağlantı kurmak zorunda kalmadan önce <xliff:g id="NUMBER_0">%d</xliff:g> deneme hakkınız kaldı.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Yanlış SIM PIN kodu. Cihazınızın kilidini açması için operatörünüzle bağlantı kurmak zorunda kalmadan önce # deneme hakkınız kaldı.}other{Yanlış SIM PIN kodu. # deneme hakkınız kaldı. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM kullanılamaz. Operatörünüzle bağlantı kurun."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Yanlış SIM PUK kodu, SIM kalıcı olarak kullanılmaz hale gelmeden önce <xliff:g id="NUMBER_1">%d</xliff:g> deneme hakkınız kaldı.</item>
- <item quantity="one">Yanlış SIM PUK kodu. SIM kalıcı olarak kullanılmaz hale gelmeden önce <xliff:g id="NUMBER_0">%d</xliff:g> deneme hakkınız kaldı.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Yanlış SIM PUK kodu. SIM kalıcı olarak kullanılmaz hale gelmeden önce # deneme hakkınız kaldı.}other{Yanlış SIM PUK kodu. SIM kalıcı olarak kullanılmaz hale gelmeden önce # deneme hakkınız kaldı.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM PIN işlemi başarısız oldu!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM PUK işlemi başarısız oldu!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Giriş yöntemini değiştir"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Cihazın manuel olarak kilitlendi"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Tanınmadı"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Yüz Tanıma Kilidi için Ayarlar\'da kamera erişimini açın"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">SIM PIN\'inizi girin. <xliff:g id="NUMBER_1">%d</xliff:g> deneme hakkınız kaldı.</item>
- <item quantity="one">SIM PIN\'inizi girin. Cihazınızın kilidini açmak için operatörünüzle bağlantı kurmak zorunda kalmadan önce <xliff:g id="NUMBER_0">%d</xliff:g> deneme hakkınız kaldı.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM artık devre dışı. Devam etmek için PUK kodunu girin. SIM kalıcı olarak kullanım dışı kalmadan önce <xliff:g id="_NUMBER_1">%d</xliff:g> deneme hakkınız kaldı. Ayrıntılı bilgi için operatörünüzle iletişim kurun.</item>
- <item quantity="one">SIM artık devre dışı. Devam etmek için PUK kodunu girin. SIM kalıcı olarak kullanım dışı kalmadan önce <xliff:g id="_NUMBER_0">%d</xliff:g> deneme hakkınız kaldı. Ayrıntılı bilgi için operatörünüzle iletişim kurun.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{SIM PIN\'inizi girin. Cihazınızın kilidini açmak için operatörünüzle bağlantı kurmak zorunda kalmadan önce # deneme hakkınız kaldı.}other{SIM PIN kodunu girin. # deneme hakkınız kaldı.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM artık devre dışı. Devam etmek için PUK kodunu girin. SIM kalıcı olarak kullanım dışı kalmadan önce # deneme hakkınız kaldı. Ayrıntılı bilgi için operatörünüzle iletişim kurun.}other{SIM artık devre dışı. Devam etmek için PUK kodunu girin. SIM kalıcı olarak kullanım dışı kalmadan önce # deneme hakkınız kaldı. Ayrıntılı bilgi için operatörünüzle iletişim kurun.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Varsayılan"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Baloncuk"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index 2aa4efc482b7..096adb7fa122 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -68,19 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Пароль неправильно введено стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\nПовторіть спробу через <xliff:g id="NUMBER_1">%2$d</xliff:g> с."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\nПовторіть спробу через <xliff:g id="NUMBER_1">%2$d</xliff:g> с."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Неправильний PIN-код SIM-карти. Зв’яжіться зі своїм оператором, щоб розблокувати пристрій."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Неправильний PIN-код SIM-карти. У вас залишилася <xliff:g id="NUMBER_1">%d</xliff:g> спроба.</item>
- <item quantity="few">Неправильний PIN-код SIM-карти. У вас залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроби.</item>
- <item quantity="many">Неправильний PIN-код SIM-карти. У вас залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроб.</item>
- <item quantity="other">Неправильний PIN-код SIM-карти. У вас залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроби.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Неправильний PIN-код SIM-карти. У вас залишилась # спроба. Після цього, щоб розблокувати пристрій, потрібно буде зв’язатися з оператором.}one{Неправильний PIN-код SIM-карти. У вас залишилася # спроба. }few{Неправильний PIN-код SIM-карти. У вас залишилося # спроби. }many{Неправильний PIN-код SIM-карти. У вас залишилося # спроб. }other{Неправильний PIN-код SIM-карти. У вас залишилося # спроби. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM-карту заблоковано. Зв’яжіться з оператором."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Неправильний PUK-код SIM-карти. У вас залишилася <xliff:g id="NUMBER_1">%d</xliff:g> спроба. Після цього SIM-карту буде назавжди заблоковано.</item>
- <item quantity="few">Неправильний PUK-код SIM-карти. У вас залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроби. Після цього SIM-карту буде назавжди заблоковано.</item>
- <item quantity="many">Неправильний PUK-код SIM-карти. У вас залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроб. Після цього SIM-карту буде назавжди заблоковано.</item>
- <item quantity="other">Неправильний PUK-код SIM-карти. У вас залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроби. Після цього SIM-карту буде назавжди заблоковано.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Неправильний PUK-код SIM-карти. У вас залишилась # спроба. Після цього SIM-карту буде назавжди заблоковано.}one{Неправильний PUK-код SIM-карти. У вас залишилася # спроба. Після цього SIM-карту буде назавжди заблоковано.}few{Неправильний PUK-код SIM-карти. У вас залишилося # спроби. Після цього SIM-карту буде назавжди заблоковано.}many{Неправильний PUK-код SIM-карти. У вас залишилося # спроб. Після цього SIM-карту буде назавжди заблоковано.}other{Неправильний PUK-код SIM-карти. У вас залишилося # спроби. Після цього SIM-карту буде назавжди заблоковано.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Помилка введення PIN-коду SIM-карти."</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Помилка введення PUK-коду SIM-карти."</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Змінити метод введення"</string>
@@ -95,18 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Пристрій заблоковано вручну"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Не розпізнано"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Для фейсконтролю надайте доступ до камери"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Введіть PIN-код SIM-карти. Залишилася <xliff:g id="NUMBER_1">%d</xliff:g> спроба.</item>
- <item quantity="few">Введіть PIN-код SIM-карти. Залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроби.</item>
- <item quantity="many">Введіть PIN-код SIM-карти. Залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроб.</item>
- <item quantity="other">Введіть PIN-код SIM-карти. Залишилося <xliff:g id="NUMBER_1">%d</xliff:g> спроби.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. Залишилася <xliff:g id="_NUMBER_1">%d</xliff:g> спроба. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.</item>
- <item quantity="few">SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. Залишилося <xliff:g id="_NUMBER_1">%d</xliff:g> спроби. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.</item>
- <item quantity="many">SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. Залишилося <xliff:g id="_NUMBER_1">%d</xliff:g> спроб. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.</item>
- <item quantity="other">SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. Залишилося <xliff:g id="_NUMBER_1">%d</xliff:g> спроби. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Введіть PIN-код SIM-карти. У вас залишилась # спроба. Після цього, щоб розблокувати пристрій, потрібно буде зв’язатися з оператором.}one{Введіть PIN-код SIM-карти. У вас залишилася # спроба.}few{Введіть PIN-код SIM-карти. У вас залишилося # спроби.}many{Введіть PIN-код SIM-карти. У вас залишилося # спроб.}other{Введіть PIN-код SIM-карти. У вас залишилося # спроби.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. У вас залишилась # спроба. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.}one{SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. У вас залишилася # спроба. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.}few{SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. У вас залишилося # спроби. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.}many{SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. У вас залишилося # спроб. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.}other{SIM-карту заблоковано. Щоб продовжити, введіть PUK-код. У вас залишилося # спроби. Після цього SIM-карту буде назавжди заблоковано. Щоб дізнатися більше, зверніться до свого оператора.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"За умовчанням"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Бульбашковий"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Аналоговий"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml
index dafa2f638898..c30dbfcbc49d 100644
--- a/packages/SystemUI/res-keyguard/values-ur/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"آپ نے اپنا پاس ورڈ <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ٹائپ کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"‏غلط SIM PIN کوڈ، اب آپ کو اپنا آلہ غیر مقفل کرنے کیلئے اپنے کیریئر سے رابطہ کرنا ہوگا۔"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">‏غلط SIM PIN کوڈ، آپ کے پاس <xliff:g id="NUMBER_1">%d</xliff:g> کوششیں بچی ہیں۔</item>
- <item quantity="one">‏غلط SIM PIN کوڈ، آپ کے پاس <xliff:g id="NUMBER_0">%d</xliff:g> کوشش بچی ہے، اس کے بعد آپ کو اپنا آلہ غیر مقفل کرنے کیلئے اپنے کیریئر سے رابطہ کرنا ہوگا۔</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{‏SIM کا غلط PIN کوڈ، اس سے پہلے کہ آپ اپنا آلہ غیر مقفل کرنے کیلئے لازمی طور پر اپنے کیریئر سے رابطہ کریں آپ کے پاس # کوشش بچی ہے۔}other{‏SIM کا غلط PIN کوڈ، آپ کے پاس # کوششیں بچی ہیں۔ }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"‏SIM ناقابل استعمال ہے۔ اپنے کیریئر سے رابطہ کریں۔"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">‏غلط SIM PUK کوڈ، آپ کے پاس <xliff:g id="NUMBER_1">%d</xliff:g> کوششیں بچی ہیں، اس کے بعد SIM مستقل طور پر ناقابل استعمال ہو جائے گا۔</item>
- <item quantity="one">‏غلط SIM PUK کوڈ، آپ کے پاس <xliff:g id="NUMBER_0">%d</xliff:g> کوشش بچی ہے، اس کے بعد SIM مستقل طور پر ناقابل استعمال ہو جائے گا۔</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{‏SIM کا غلط PUK کوڈ، SIM کے مستقل طور پر ناقابل استعمال ہونے سے پہلے آپ کے پاس # کوشش بچی ہے۔}other{‏SIM کا غلط PUK کوڈ، SIM کے مستقل طور پر ناقابل استعمال ہونے سے پہلے آپ کے پاس # کوششیں بچی ہیں۔}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"‏SIM PIN کی کارروائی ناکام ہوگئی!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"‏SIM PUK کارروائی ناکام ہو گئی!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"اندراج کا طریقہ سوئچ کریں"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"آلہ کو دستی طور پر مقفل کیا گیا تھا"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"تسلیم شدہ نہیں ہے"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"فیس اَنلاک استعمال کرنے کیلئے، ترتیبات میں کیمرا تک رسائی کو آن کریں"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">‏SIM کا PIN درج کریں، آپ کے پاس <xliff:g id="NUMBER_1">%d</xliff:g> کوششیں بچی ہیں۔</item>
- <item quantity="one">‏SIM کا PIN درج کریں، آپ کے پاس <xliff:g id="NUMBER_0">%d</xliff:g> کوشش بچی ہے، اس کے بعد آپ کو اپنا آلہ غیر مقفل کرنے کے لیے اپنے کیریئر سے رابطہ کرنا ہوگا۔</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">‏SIM اب غیر فعال ہے۔ جاری رکھنے کیلئے PUK کوڈ درج کریں۔ SIM کے مستقل طور پر ناقابل استعمال ہونے سے پہلے آپ کے پاس <xliff:g id="_NUMBER_1">%d</xliff:g> کوششیں بچی ہیں۔ تفصیلات کیلئے کیریئر سے رابطہ کریں۔</item>
- <item quantity="one">‏SIM اب غیر فعال ہے۔ جاری رکھنے کیلئے PUK کوڈ درج کریں۔ SIM کے مستقل طور پر ناقابل استعمال ہونے سے پہلے آپ کے پاس <xliff:g id="_NUMBER_0">%d</xliff:g> کوشش بچی ہے۔ تفصیلات کیلئے کیریئر سے رابطہ کریں۔</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{‏SIM کا PIN درج کریں۔ اس سے پہلے کہ آپ اپنا آلہ غیر مقفل کرنے کیلئے لازمی طور پر اپنے کیریئر سے رابطہ کریں آپ کے پاس # کوشش بچی ہے۔}other{‏SIM کا PIN درج کریں۔ آپ کے پاس # کوششیں بچی ہیں۔}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{‏SIM اب غیر فعال ہے۔ جاری رکھنے کیلئے PUK کوڈ درج کریں۔ SIM کے مستقل طور پر ناقابل استعمال ہونے سے پہلے آپ کے پاس # کوشش بچی ہے۔ تفصیلات کیلئے کیریئر سے رابطہ کریں۔}other{‏SIM اب غیر فعال ہے۔ جاری رکھنے کیلئے PUK کوڈ درج کریں۔ SIM کے مستقل طور پر ناقابل استعمال ہونے سے پہلے آپ کے پاس # کوششیں بچی ہیں۔ تفصیلات کیلئے کیریئر سے رابطہ کریں۔}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"ڈیفالٹ"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"بلبلہ"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"اینالاگ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml
index df537ab67442..dedf432425d1 100644
--- a/packages/SystemUI/res-keyguard/values-uz/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Parol <xliff:g id="NUMBER_0">%1$d</xliff:g> marta xato kiritildi. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> soniyadan keyin qaytadan urining."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Grafik kalit <xliff:g id="NUMBER_0">%1$d</xliff:g> marta xato kiritildi. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> soniyadan keyin qayta urining."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM kartaning PIN kodi xato. Qurilma qulfini ochish uchun operatoringizga murojaat qiling."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">SIM kartaning PIN kodi noto‘g‘ri. Sizda yana <xliff:g id="NUMBER_1">%d</xliff:g> ta urinish qoldi.</item>
- <item quantity="one">SIM karta PIN kodi noto‘g‘ri terildi, yana <xliff:g id="NUMBER_0">%d</xliff:g> marta uirinib ko‘rishingiz mumkin, urinishlar tugagandan keyin qurilmangizni qulfdan chiqarish uchun aloqa operatoringiz bilan bog‘lanishingiz kerak.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM karta PIN kodi xato kiritildi, yana # marta urinishingiz mumkin, urinishlar tugagandan keyin qurilmangizni qulfdan chiqarish uchun aloqa operatoringizga murojaat qilishingiz kerak.}other{SIM karta PIN kodi xato kiritildi, yana # marta urinishingiz mumkin. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM kartadan foydalanib bo‘lmaydi. Operatoringizga murojaat qiling."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">SIM kartaning PUK kodi noto‘g‘ri kiritildi. Yana <xliff:g id="NUMBER_1">%d</xliff:g> ta muvaffaqiyatsiz urinishdan keyin SIM karta butunlay ishdan chiqadi.</item>
- <item quantity="one">SIM karta PUK kodi noto‘g‘ri terildi, yana <xliff:g id="NUMBER_0">%d</xliff:g> marta urinib ko‘rganingizdan keyin SIM kartadan umuman foydalanib bo‘lmaydi.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{SIM karta PUK kodi xato kiritildi, yana # marta urinishdan keyin SIM kartadan umuman foydalanib boʻlmay qoladi.}other{SIM karta PUK kodi xato kiritildi, yana # marta urinishdan keyin SIM kartadan umuman foydalanib boʻlmay qoladi.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM kartani qulfdan chiqarib bo‘lmadi!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM kartani qulfdan chiqarib bo‘lmadi!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Matn kiritish usulini almashtirish"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Qurilma qo‘lda qulflangan"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Aniqlanmadi"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Yuz bilan ochish uchun kamera ruxsatini bering"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">SIM PIN kodini kiriting, sizda <xliff:g id="NUMBER_1">%d</xliff:g> ta urinish bor.</item>
- <item quantity="one">SIM PIN kodini kiriting, qurilmani qulfdan chiqarish uchun sizda <xliff:g id="NUMBER_0">%d</xliff:g> ta urinish bor.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM karta faolsizlantirildi. Davom etish uchun PUK kodni kiriting. Yana <xliff:g id="_NUMBER_1">%d</xliff:g> marta xato qilsangiz, SIM kartangiz butunlay qulflanadi. Batafsil axborot olish uchun tarmoq operatoriga murojaat qiling.</item>
- <item quantity="one">SIM karta faolsizlantirildi. Davom etish uchun PUK kodni kiriting. Yana <xliff:g id="_NUMBER_0">%d</xliff:g> marta xato qilsangiz, SIM kartangiz butunlay qulflanadi. Batafsil axborot olish uchun tarmoq operatoriga murojaat qiling.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{SIM PIN kodini kiriting, qurilmani qulfdan chiqarish uchun sizda # ta urinish bor.}other{SIM karta PIN kodini kiriting. Sizda # ta urinish qoldi.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM karta faolsizlantirildi. Davom etish uchun PUK kodni kiriting. Yana # marta xato qilsangiz, SIM kartangiz butunlay qulflanadi. Batafsil axborot olish uchun aloqa operatoriga murojaat qiling.}other{SIM karta faolsizlantirildi. Davom etish uchun PUK kodni kiriting. Yana # marta xato qilsangiz, SIM kartangiz butunlay qulflanadi. Batafsil axborot olish uchun aloqa operatoriga murojaat qiling.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Odatiy"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Pufaklar"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Analog"</string>
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index 5d6fc50bd237..9a0eb44bd0bc 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Bạn đã nhập sai mật khẩu <xliff:g id="NUMBER_0">%1$d</xliff:g> lần. \n\nHãy thử lại sau <xliff:g id="NUMBER_1">%2$d</xliff:g> giây."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Bạn đã vẽ không chính xác hình mở khóa <xliff:g id="NUMBER_0">%1$d</xliff:g> lần. \n\nHãy thử lại sau <xliff:g id="NUMBER_1">%2$d</xliff:g> giây."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Mã PIN của SIM không chính xác, bây giờ bạn phải liên hệ với nhà cung cấp dịch vụ để mở khóa thiết bị của mình."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">Mã PIN của SIM không chính xác, bạn còn <xliff:g id="NUMBER_1">%d</xliff:g> lần thử.</item>
- <item quantity="one">Mã PIN của SIM không chính xác, bạn còn <xliff:g id="NUMBER_0">%d</xliff:g> lần thử trước khi bạn phải liên hệ với nhà cung cấp dịch vụ để mở khóa thiết bị của mình.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Mã PIN của SIM không chính xác. Bạn còn # lần thử trước khi phải liên hệ với nhà cung cấp dịch vụ để mở khóa thiết bị của mình.}other{Mã PIN của SIM không chính xác, bạn còn # lần thử. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM không thể sử dụng được. Hãy liên hệ với nhà cung cấp dịch vụ của bạn."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">Mã PUK của SIM không chính xác, bạn còn <xliff:g id="NUMBER_1">%d</xliff:g> lần thử trước khi SIM vĩnh viễn không thể sử dụng được.</item>
- <item quantity="one">Mã PUK của SIM không chính xác, bạn còn <xliff:g id="NUMBER_0">%d</xliff:g> lần thử trước khi SIM vĩnh viễn không thể sử dụng được.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Mã PUK của SIM không chính xác, bạn còn # lần thử trước khi SIM vĩnh viễn không thể sử dụng được.}other{Mã PUK của SIM không chính xác, bạn còn # lần thử trước khi SIM vĩnh viễn không thể sử dụng được.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Thao tác mã PIN của SIM không thành công!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Thao tác mã PUK của SIM không thành công!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Chuyển phương thức nhập"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Thiết bị đã bị khóa theo cách thủ công"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Không nhận dạng được"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Cho phép truy cập máy ảnh để dùng Mở khóa bằng khuôn mặt"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Hãy nhập mã PIN của SIM. Bạn còn <xliff:g id="NUMBER_1">%d</xliff:g> lần thử.</item>
- <item quantity="one">Hãy nhập mã PIN của SIM. Bạn còn <xliff:g id="NUMBER_0">%d</xliff:g> lần thử trước khi phải liên hệ với nhà mạng để mở khóa thiết bị của mình.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM hiện đã bị tắt. Hãy nhập mã PUK để tiếp tục. Bạn còn <xliff:g id="_NUMBER_1">%d</xliff:g> lần thử trước khi SIM vĩnh viễn không sử dụng được. Hãy liên hệ với nhà cung cấp dịch vụ để biết chi tiết.</item>
- <item quantity="one">SIM hiện đã bị tắt. Hãy nhập mã PUK để tiếp tục. Bạn còn <xliff:g id="_NUMBER_0">%d</xliff:g> lần thử trước khi SIM vĩnh viễn không thể sử dụng được. Hãy liên hệ với nhà cung cấp dịch vụ để biết chi tiết.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Hãy nhập mã PIN của SIM. Bạn còn # lần thử trước khi phải liên hệ với nhà cung cấp dịch vụ để mở khóa thiết bị của mình.}other{Nhập mã PIN của SIM. Bạn còn # lần thử.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM hiện đã bị tắt. Hãy nhập mã PUK để tiếp tục. Bạn còn # lần thử trước khi SIM vĩnh viễn không thể sử dụng được. Hãy liên hệ với nhà cung cấp dịch vụ để biết thông tin chi tiết.}other{SIM hiện đã bị tắt. Hãy nhập mã PUK để tiếp tục. Bạn còn # lần thử trước khi SIM vĩnh viễn không thể sử dụng được. Hãy liên hệ với nhà cung cấp dịch vụ để biết thông tin chi tiết.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Mặc định"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Bong bóng"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"Đồng hồ kim"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
index e74fa75e7b44..26e3db7d4963 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rCN/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"您已经 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次输错密码。\n\n请在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒后重试。"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"您已经 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次画错解锁图案。\n\n请在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒后重试。"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM 卡 PIN 码不正确,您现在必须联系运营商为您解锁设备。"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">SIM 卡 PIN 码不正确,您还有 <xliff:g id="NUMBER_1">%d</xliff:g> 次尝试机会。</item>
- <item quantity="one">SIM 卡 PIN 码不正确,您还有 <xliff:g id="NUMBER_0">%d</xliff:g> 次尝试机会。如果仍不正确,则需要联系运营商帮您解锁设备。</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM 卡 PIN 码不正确,您还可以尝试 # 次,如果仍不正确,则必须联系运营商帮您解锁设备。}other{SIM 卡 PIN 码不正确,您还可以尝试 # 次。}}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM 卡无法使用,请与您的运营商联系。"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">SIM 卡 PUK 码不正确,您还有 <xliff:g id="NUMBER_1">%d</xliff:g> 次尝试机会。如果仍不正确,SIM 卡将永远无法使用。</item>
- <item quantity="one">SIM 卡 PUK 码不正确,您还有 <xliff:g id="NUMBER_0">%d</xliff:g> 次尝试机会。如果仍不正确,SIM 卡将永远无法使用。</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{SIM 卡 PUK 码不正确,您还可以尝试 # 次,如果仍不正确,SIM 卡将永远无法使用。}other{SIM 卡 PUK 码不正确,您还可以尝试 # 次,如果仍不正确,SIM 卡将永远无法使用。}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM 卡 PIN 码操作失败!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM 卡 PUK 码操作失败!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"切换输入法"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"此设备已手动锁定"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"无法识别"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"如需使用人脸解锁功能,请在“设置”中开启摄像头使用权限"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">请输入 SIM 卡 PIN 码,您还可以尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
- <item quantity="one">请输入 SIM 卡 PIN 码,您还可以尝试 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍不正确,则需要联系运营商帮您解锁设备。</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM 卡现已停用,请输入 PUK 码继续使用。您还可以尝试 <xliff:g id="_NUMBER_1">%d</xliff:g> 次。如果仍不正确,该 SIM 卡将永远无法使用。有关详情,请联系您的运营商。</item>
- <item quantity="one">SIM 卡现已停用,请输入 PUK 码继续使用。您还可以尝试 <xliff:g id="_NUMBER_0">%d</xliff:g> 次。如果仍不正确,该 SIM 卡将永远无法使用。有关详情,请联系您的运营商。</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{请输入 SIM 卡 PIN 码。您还可以尝试 # 次,如果仍不正确,则必须联系运营商帮您解锁设备。}other{请输入 SIM 卡 PIN 码。您还可以尝试 # 次。}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM 卡现已停用,请输入 PUK 码继续使用。您还可以尝试 # 次,如果仍不正确,SIM 卡将永远无法使用。有关详情,请联系运营商。}other{SIM 卡现已停用,请输入 PUK 码继续使用。您还可以尝试 # 次,如果仍不正确,SIM 卡将永远无法使用。有关详情,请联系运营商。}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"默认"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"泡泡"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"指针"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
index c22ecdc28fce..2bd210542ba2 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"您已輸入錯誤的密碼 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"您已畫錯解鎖圖案 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM 卡 PIN 碼不正確,您現在必須聯絡流動網絡供應商為您的裝置解鎖。"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">SIM 卡的 PIN 碼不正確,您還有 <xliff:g id="NUMBER_1">%d</xliff:g> 次輸入機會。</item>
- <item quantity="one">SIM 卡的 PIN 碼不正確,您還有 <xliff:g id="NUMBER_0">%d</xliff:g> 次輸入機會。如果仍然輸入錯誤,您必須聯絡流動網絡供應商為您的裝置解鎖。</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM 卡的 PIN 碼不正確,您還可以再試 # 次。如果仍然輸入錯誤,您必須聯絡流動網絡供應商解鎖您的裝置。}other{SIM 卡的 PIN 碼不正確,您還可以再試 # 次。}}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM 卡無法使用,請聯絡您的流動網絡供應商。"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">SIM 卡的 PUK 碼不正確,您還有 <xliff:g id="NUMBER_1">%d</xliff:g> 次輸入機會。如果仍然輸入錯誤,SIM 卡將永久無法使用。</item>
- <item quantity="one">SIM 卡的 PUK 碼不正確,您還有 <xliff:g id="NUMBER_0">%d</xliff:g> 次輸入機會。如果仍然輸入錯誤,SIM 卡將永久無法使用。</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{SIM 卡的 PUK 碼不正確,您還可以再試 # 次。如果仍然輪入錯誤,SIM 卡將永久無法使用。}other{SIM 卡的 PUK 碼不正確,您還可以再試 # 次。如果仍然輪入錯誤,SIM 卡將永久無法使用。}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"無法使用 SIM 卡 PIN 碼解鎖!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"無法使用 SIM 卡 PUK 碼解鎖!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"轉換輸入方法"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"使用者已手動將裝置上鎖"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"未能識別"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"如要使用「面孔解鎖」,請在「設定」開啟相機存取權"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">輸入 SIM 卡的 PIN,您還可以再試 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
- <item quantity="one">輸入 SIM 卡的 PIN,您還可以再試 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍然輸入錯誤,您必須聯絡流動網絡供應商解鎖您的裝置。</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM 卡已停用。請輸入 PUK 碼以繼續進行。您還可以再試 <xliff:g id="_NUMBER_1">%d</xliff:g> 次。如果仍然輸入錯誤,SIM 卡將永久無法使用。詳情請與流動網絡供應商聯絡。</item>
- <item quantity="one">SIM 卡已停用。請輸入 PUK 碼以繼續進行。您還可以再試 <xliff:g id="_NUMBER_0">%d</xliff:g> 次。如果仍然輸入錯誤,SIM 卡將永久無法使用。詳情請與流動網絡供應商聯絡。</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{輸入 SIM 卡的 PIN,您還可以再試 # 次。如果仍然輸入錯誤,您必須聯絡流動網絡供應商解鎖您的裝置。}other{輸入 SIM 卡的 PIN。您還可以再試 # 次。}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM 卡已停用。請輸入 PUK 碼以繼續進行。您還可以再試 # 次。如果仍然輸入錯誤,SIM 卡將永久無法使用。詳情請向流動網絡供應商查詢。}other{SIM 卡已停用。請輸入 PUK 碼以繼續進行。您還可以再試 # 次。如果仍然輸入錯誤,SIM 卡將永久無法使用。詳情請向流動網絡供應商查詢。}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"預設"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"泡泡"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"指針"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index 498f74834090..a6625537db2a 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"你已輸入錯誤的密碼 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"你已畫出錯誤的解鎖圖案 <xliff:g id="NUMBER_0">%1$d</xliff:g> 次。\n\n請在 <xliff:g id="NUMBER_1">%2$d</xliff:g> 秒後再試一次。"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM 卡的 PIN 碼輸入錯誤,你現在必須請電信業者為裝置解鎖。"</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="other">SIM 卡的 PIN 碼輸入錯誤,你還可以再試 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
- <item quantity="one">SIM 卡的 PIN 碼輸入錯誤,你還可以再試 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍然失敗,就必須請電信業者為裝置解鎖。</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM 卡 PIN 碼輸入錯誤,您還可以再試 # 次。如果仍然失敗,就必須聯絡電信業者為裝置解鎖。}other{SIM 卡 PIN 碼輸入錯誤,您還可以再試 # 次。}}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM 卡無法使用,請與你的電信業者聯絡。"</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="other">SIM 卡的 PUK 碼輸入錯誤,你還可以再試 <xliff:g id="NUMBER_1">%d</xliff:g> 次。如果仍然失敗,SIM 卡將永久無法使用。</item>
- <item quantity="one">SIM 卡的 PUK 碼輸入錯誤,你還可以再試 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍然失敗,SIM 卡將永久無法使用。</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{SIM 卡 PUK 碼輸入錯誤,您還可以再試 # 次,如果仍然失敗,SIM 卡將永久無法使用。}other{SIM 卡 PUK 碼輸入錯誤,您還可以再試 # 次,如果仍然失敗,SIM 卡將永久無法使用。}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"SIM 卡 PIN 碼解鎖失敗!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"SIM 卡 PUK 碼解鎖失敗!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"切換輸入法"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"裝置已手動鎖定"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"無法識別"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"如要使用人臉解鎖功能,請在「設定」中開啟相機存取權。"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">請輸入 SIM 卡的 PIN 碼,你還可以再試 <xliff:g id="NUMBER_1">%d</xliff:g> 次。</item>
- <item quantity="one">請輸入 SIM 卡的 PIN 碼,你還可以再試 <xliff:g id="NUMBER_0">%d</xliff:g> 次。如果仍然失敗,就必須請電信業者為裝置解鎖。</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="other">SIM 卡現在已遭停用。請輸入 PUK 碼以繼續進行。你還可以再試 <xliff:g id="_NUMBER_1">%d</xliff:g> 次,如果仍然失敗,SIM 卡將永久無法使用。詳情請與電信業者聯絡。</item>
- <item quantity="one">SIM 卡現在已遭停用。請輸入 PUK 碼以繼續進行。你還可以再試 <xliff:g id="_NUMBER_0">%d</xliff:g> 次,如果仍然失敗,SIM 卡將永久無法使用。詳情請與電信業者聯絡。</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{請輸入 SIM 卡 PIN 碼,您還可以再試 # 次,如果仍然失敗,就必須聯絡電信業者為裝置解鎖。}other{輸入 SIM 卡 PIN 碼您還可以再試 # 次}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{SIM 卡現在已遭停用。請輸入 PUK 碼以繼續。您還可以再試 # 次,如果仍然失敗,SIM 卡將永久無法使用。詳情請洽詢電信業者。}other{SIM 卡現在已遭停用。請輸入 PUK 碼以繼續。您還可以再試 # 次,如果仍然失敗,SIM 卡將永久無法使用。詳情請洽詢電信業者。}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"預設"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"泡泡"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"類比"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zu/strings.xml b/packages/SystemUI/res-keyguard/values-zu/strings.xml
index 70bed322cc53..8c7f83094c84 100644
--- a/packages/SystemUI/res-keyguard/values-zu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zu/strings.xml
@@ -68,15 +68,9 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Ubhale iphasiwedi yakho ngendlela engafanele <xliff:g id="NUMBER_0">%1$d</xliff:g> izikhathi. \n\nZama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%2$d</xliff:g>."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Udwebe iphathini yakho yokuvula ngendlela engafanele-<xliff:g id="NUMBER_0">%1$d</xliff:g>. \n\n Zama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%2$d</xliff:g>"</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Ikhodi yephinikhodi ye-SIM engalungile manje kumele uxhumane nenkampini yenethiwekhi yakho ukuvula idivayisi yakho."</string>
- <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
- <item quantity="one">Ikhodi engalungile yephinikhodi ye-SIM, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele.</item>
- <item quantity="other">Ikhodi engalungile yephinikhodi ye-SIM, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele.</item>
- </plurals>
+ <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Ikhodi engalungile Yephinikhodi ye-SIM, unomzamo ongu-# osele ngaphambi kokuba uxhumane nenkampani yakho yenethiwekhi ukuvula idivayisi yakho.}one{Ikhodi engalungile Yephinikhodi ye-SIM, unemizamo engu-# esele. }other{Ikhodi engalungile Yephinikhodi ye-SIM, unemizamo engu-# esele. }}"</string>
<string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"I-SIM ayisebenziseki. Xhumana nemkampini yenethiwekhi yakho."</string>
- <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="3937306685604862886">
- <item quantity="one">Ikhodi ye-PUK ye-SIM engalungile, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele ngaphambi kokuthi i-SIM ingasasebenziseki unaphakade.</item>
- <item quantity="other">Ikhodi ye-PUK ye-SIM engalungile, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele ngaphambi kokuthi i-SIM ingasasebenziseki unaphakade.</item>
- </plurals>
+ <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Ikhodi ye-PUK ye-SIM engalungile, unemizamo engu-# esele ngaphambi kokuba i-SIM ibe engasasebenziseki unomphela.}one{Ikhodi ye-PUK ye-SIM engalungile, unemizamo engu-# esele ngaphambi kokuthi i-SIM ibe engasasebenziseki unomphela.}other{Ikhodi ye-PUK ye-SIM engalungile, unemizamo engu-# esele ngaphambi kokuthi i-SIM ibe engasasebenziseki unomphela.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Umsebenzi wephinikhodi ye-SIM wehlulekile!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Umsebenzi we-PUK ye-SIM wehlulekile!"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Shintsha indlela yokufaka"</string>
@@ -91,14 +85,8 @@
<string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"Idivayisi ikhiywe ngokwenza"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Akwaziwa"</string>
<string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"Ukuze usebenzise Ukuvula Ngobuso, vula ukufinyelela kwekhamera Kumasethingi"</string>
- <plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="one">Faka i-PIN ye-SIM, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele.</item>
- <item quantity="other">Faka i-PIN ye-SIM, unemizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g> esele.</item>
- </plurals>
- <plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
- <item quantity="one">I-SIM manje ikhutshaziwe. Faka ikhodi ye-PUK ukuze uqhubeke. Unemizamo engu-<xliff:g id="_NUMBER_1">%d</xliff:g> esele ngaphambi kokuthi i-SIM ingasebenziseki unaphakade. Xhumana nenkampani yenethiwekhi ngemininingwane.</item>
- <item quantity="other">I-SIM manje ikhutshaziwe. Faka ikhodi ye-PUK ukuze uqhubeke. Unemizamo engu-<xliff:g id="_NUMBER_1">%d</xliff:g> esele ngaphambi kokuthi i-SIM ingasebenziseki unaphakade. Xhumana nenkampani yenethiwekhi ngemininingwane.</item>
- </plurals>
+ <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{Faka Iphinikhodi ye-SIM. Unemizamo engu-# esele ngaphambi kokuthi uxhumane nenkampani yakho yenethiwekhi ukuze uvule idivayisi yakho.}one{Faka Iphinikhodi ye-SIM Unemizamo engu-# esele.}other{Faka Iphinikhodi ye-SIM Unemizamo engu-# esele.}}"</string>
+ <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{I-SIM manje ikhutshaziwe. Faka ikhodi ye-PUK ukuze uqhubeke. Unomzamo ongu-# osele ngaphambi kokuthi i-SIM ingasasebenziseki unomphela. Xhumana nenkampani yenethiwekhi ngemininingwane.}one{I-SIM manje ikhutshaziwe. Faka ikhodi ye-PUK ukuze uqhubeke. Unemizamo engu-# esele ngaphambi kokuthi i-SIM ingasasebenziseki unomphela. Xhumana nenkampani yenethiwekhi ngemininingwane.}other{I-SIM manje ikhutshaziwe. Faka ikhodi ye-PUK ukuze uqhubeke. Unemizamo engu-# esele ngaphambi kokuthi i-SIM ingasasebenziseki unomphela. Xhumana nenkampani yenethiwekhi ngemininingwane.}}"</string>
<string name="clock_title_default" msgid="6342735240617459864">"Okuzenzekelayo"</string>
<string name="clock_title_bubble" msgid="2204559396790593213">"Ibhamuza"</string>
<string name="clock_title_analog" msgid="8409262532900918273">"I-Analog"</string>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 77f1803523a8..acf3e4dcf02a 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -102,12 +102,13 @@
screen. -->
<item name="half_opened_bouncer_height_ratio" type="dimen" format="float">0.0</item>
- <!-- The actual amount of translation that is applied to the bouncer when it animates from one
- side of the screen to the other in one-handed mode. Note that it will always translate from
- the side of the screen to the other (it will "jump" closer to the destination while the
- opacity is zero), but this controls how much motion will actually be applied to it while
- animating. Larger values will cause it to move "faster" while fading out/in. -->
- <dimen name="one_handed_bouncer_move_animation_translation">120dp</dimen>
+ <!-- The actual amount of translation that is applied to the security when it animates from one
+ side of the screen to the other in one-handed or user switcher mode. Note that it will
+ always translate from the side of the screen to the other (it will "jump" closer to the
+ destination while the opacity is zero), but this controls how much motion will actually be
+ applied to it while animating. Larger values will cause it to move "faster" while
+ fading out/in. -->
+ <dimen name="security_shift_animation_translation">120dp</dimen>
<dimen name="bouncer_user_switcher_header_text_size">20sp</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 1be2d997719d..babe924c4615 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -164,21 +164,21 @@
Displayed in a dialog box. -->
<string name="kg_password_wrong_pin_code_pukked">Incorrect SIM PIN code you must now contact your carrier to unlock your device.</string>
<!-- Instructions telling the user that they entered the wrong SIM PIN while trying
- to unlock the keyguard. Displayed in a dialog box. -->
- <plurals name="kg_password_wrong_pin_code">
- <item quantity="one">Incorrect SIM PIN code, you have <xliff:g id="number">%d</xliff:g> remaining attempt before you must contact your carrier to unlock your device.</item>
- <item quantity="other">Incorrect SIM PIN code, you have <xliff:g id="number">%d</xliff:g> remaining attempts.</item>
- </plurals>
+ to unlock the keyguard. Displayed in a dialog box. [CHAR LIMIT=NONE] -->
+ <string name="kg_password_wrong_pin_code"> {count, plural,
+ =1 {Incorrect SIM PIN code, you have # remaining attempt before you must contact your carrier to unlock your device.}
+ other {Incorrect SIM PIN code, you have # remaining attempts. }
+ }</string>
<!-- Instructions telling the user that they have exhausted SIM PUK retries and the SIM is now unusable.
Displayed in a dialog box. -->
<string name="kg_password_wrong_puk_code_dead">SIM is unusable. Contact your carrier.</string>
<!-- Instructions telling the user that they entered the wrong puk while trying
- to unlock the keyguard. Displayed in a dialog box. -->
- <plurals name="kg_password_wrong_puk_code">
- <item quantity="one">Incorrect SIM PUK code, you have <xliff:g id="number">%d</xliff:g> remaining attempt before SIM becomes permanently unusable.</item>
- <item quantity="other">Incorrect SIM PUK code, you have <xliff:g id="number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable.</item>
- </plurals>
+ to unlock the keyguard. Displayed in a dialog box. [CHAR LIMIT=NONE] -->
+ <string name="kg_password_wrong_puk_code">{count, plural,
+ =1 {Incorrect SIM PUK code, you have # remaining attempt before SIM becomes permanently unusable.}
+ other {Incorrect SIM PUK code, you have # remaining attempts before SIM becomes permanently unusable.}
+ }</string>
<!-- Instructions telling the user that the operation to unlock the keyguard
with SIM PIN failed. Displayed in one line in a large font. -->
<string name="kg_password_pin_failed">SIM PIN operation failed!</string>
@@ -223,21 +223,17 @@
<!-- Error message indicating that the camera privacy sensor has been turned on [CHAR LIMIT=53] -->
<string name="kg_face_sensor_privacy_enabled">To use Face Unlock, turn on camera access in Settings</string>
- <!-- Instructions telling the user remaining times when enter SIM PIN view. -->
- <plurals name="kg_password_default_pin_message">
- <item quantity="one">Enter SIM PIN. You have <xliff:g id="number">%d</xliff:g> remaining
-attempt before you must contact your carrier to unlock your device.</item>
- <item quantity="other">Enter SIM PIN. You have <xliff:g id="number">%d</xliff:g> remaining
-attempts.</item>
- </plurals>
-
- <!-- Instructions telling the user remaining times when enter SIM PUK view. -->
- <plurals name="kg_password_default_puk_message">
- <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="
-number">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact carrier for details.</item>
- <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="
-number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item>
- </plurals>
+ <!-- Instructions telling the user remaining times when enter SIM PIN view. [CHAR LIMIT=NONE] -->
+ <string name="kg_password_default_pin_message">{count, plural,
+ =1 {Enter SIM PIN. You have # remaining attempt before you must contact your carrier to unlock your device.}
+ other {Enter SIM PIN. You have # remaining attempts.}
+ }</string>
+
+ <!-- Instructions telling the user remaining times when enter SIM PUK view. [CHAR LIMIT=NONE] -->
+ <string name="kg_password_default_puk_message">{count, plural,
+ =1 {SIM is now disabled. Enter PUK code to continue. You have # remaining attempt before SIM becomes permanently unusable. Contact carrier for details.}
+ other {SIM is now disabled. Enter PUK code to continue. You have # remaining attempts before SIM becomes permanently unusable. Contact carrier for details.}
+ }</string>
<!-- Name of the "Default" clock face, which is the clock face that will be shown by default. [CHAR LIMIT=15]-->
<string name="clock_title_default">Default</string>
diff --git a/packages/SystemUI/res/drawable/broadcast_dialog_btn_bg.xml b/packages/SystemUI/res/drawable/broadcast_dialog_btn_bg.xml
new file mode 100644
index 000000000000..5fd7ee29d838
--- /dev/null
+++ b/packages/SystemUI/res/drawable/broadcast_dialog_btn_bg.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorAccentPrimary" />
+ <corners android:radius="@dimen/broadcast_dialog_btn_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml b/packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml
new file mode 100644
index 000000000000..1992c7733bd3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/dream_aqi_badge_bg.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/dream_overlay_aqi_unknown" />
+ <corners android:radius="@dimen/dream_aqi_badge_corner_radius" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/dream_overlay_camera_off.xml b/packages/SystemUI/res/drawable/dream_overlay_camera_off.xml
new file mode 100644
index 000000000000..159655e39d24
--- /dev/null
+++ b/packages/SystemUI/res/drawable/dream_overlay_camera_off.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="56dp"
+ android:height="24dp"
+ android:viewportWidth="56"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,0L44,0A12,12 0,0 1,56 12L56,12A12,12 0,0 1,44 24L12,24A12,12 0,0 1,0 12L0,12A12,12 0,0 1,12 0z"
+ android:fillColor="#5F6368"/>
+ <path
+ android:pathData="M21.872,5.873L20.926,6.813L21.492,7.38C21.392,7.566 21.332,7.773 21.332,8V16C21.332,16.733 21.932,17.333 22.666,17.333H30.666C30.892,17.333 31.099,17.273 31.286,17.173L33.186,19.073L34.126,18.133L31.999,16L21.872,5.873ZM31.999,10.986V8C31.999,7.266 31.399,6.666 30.666,6.666H24.552L25.886,8H30.666V12.78L31.999,14.113V13.013L34.666,15.666V8.333L31.999,10.986ZM22.666,8.553V16H30.112L22.666,8.553Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/dream_overlay_mic_and_camera_off.xml b/packages/SystemUI/res/drawable/dream_overlay_mic_and_camera_off.xml
new file mode 100644
index 000000000000..087dde78833f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/dream_overlay_mic_and_camera_off.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="56dp"
+ android:height="24dp"
+ android:viewportWidth="56"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,0L44,0A12,12 0,0 1,56 12L56,12A12,12 0,0 1,44 24L12,24A12,12 0,0 1,0 12L0,12A12,12 0,0 1,12 0z"
+ android:fillColor="#5F6368"/>
+ <path
+ android:pathData="M15.332,7.333C15.332,6.966 15.632,6.666 15.999,6.666C16.366,6.666 16.666,6.966 16.666,7.333V10.78L17.879,11.993C17.952,11.786 17.999,11.566 17.999,11.333V7.333C17.999,6.226 17.106,5.333 15.999,5.333C14.892,5.333 13.999,6.226 13.999,7.333V8.113L15.332,9.446V7.333ZM9.872,5.873L8.926,6.813L16.692,14.58C16.472,14.633 16.239,14.666 15.999,14.666C14.159,14.666 12.666,13.173 12.666,11.333H11.332C11.332,13.686 13.072,15.62 15.332,15.946V18H16.666V15.946C17.046,15.893 17.412,15.786 17.759,15.64L21.186,19.066L22.126,18.126L9.872,5.873ZM19.332,11.333H20.666C20.666,12.313 20.359,13.213 19.846,13.96L18.872,12.986C19.159,12.5 19.332,11.94 19.332,11.333Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M33.872,5.873L32.926,6.813L33.492,7.38C33.392,7.566 33.332,7.773 33.332,8V16C33.332,16.733 33.932,17.333 34.666,17.333H42.666C42.892,17.333 43.099,17.273 43.286,17.173L45.186,19.073L46.126,18.133L43.999,16L33.872,5.873ZM43.999,10.986V8C43.999,7.266 43.399,6.666 42.666,6.666H36.552L37.886,8H42.666V12.78L43.999,14.113V13.013L46.666,15.666V8.333L43.999,10.986ZM34.666,8.553V16H42.112L34.666,8.553Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/dream_overlay_mic_off.xml b/packages/SystemUI/res/drawable/dream_overlay_mic_off.xml
new file mode 100644
index 000000000000..693250d39f95
--- /dev/null
+++ b/packages/SystemUI/res/drawable/dream_overlay_mic_off.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="56dp"
+ android:height="24dp"
+ android:viewportWidth="56"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,0L44,0A12,12 0,0 1,56 12L56,12A12,12 0,0 1,44 24L12,24A12,12 0,0 1,0 12L0,12A12,12 0,0 1,12 0z"
+ android:fillColor="#5F6368"/>
+ <path
+ android:pathData="M27.807,7.133C27.807,6.767 28.107,6.467 28.473,6.467C28.84,6.467 29.14,6.767 29.14,7.133V10.58L30.353,11.793C30.427,11.587 30.473,11.367 30.473,11.133V7.133C30.473,6.027 29.58,5.133 28.473,5.133C27.367,5.133 26.473,6.027 26.473,7.133V7.913L27.807,9.247V7.133ZM22.347,5.673L21.4,6.613L29.167,14.38C28.947,14.433 28.713,14.467 28.473,14.467C26.633,14.467 25.14,12.973 25.14,11.133H23.807C23.807,13.487 25.547,15.42 27.807,15.747V17.8H29.14V15.747C29.52,15.693 29.887,15.587 30.233,15.44L33.66,18.867L34.6,17.927L22.347,5.673ZM31.807,11.133H33.14C33.14,12.113 32.833,13.013 32.32,13.76L31.347,12.787C31.633,12.3 31.807,11.74 31.807,11.133Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_media_pause_container.xml b/packages/SystemUI/res/drawable/ic_media_pause_container.xml
index b92e63575b95..ea9eb8cd5475 100644
--- a/packages/SystemUI/res/drawable/ic_media_pause_container.xml
+++ b/packages/SystemUI/res/drawable/ic_media_pause_container.xml
@@ -22,11 +22,6 @@
android:viewportHeight="48"
android:viewportWidth="48">
<group android:name="_R_G">
- <group android:name="_R_G_L_1_G"
- android:translateX="24"
- android:translateY="24"
- android:scaleX="0.5"
- android:scaleY="0.5"/>
<group android:name="_R_G_L_0_G"
android:translateX="24"
android:translateY="24"
@@ -46,7 +41,7 @@
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator android:propertyName="pathData"
- android:duration="500"
+ android:duration="250"
android:startOffset="0"
android:valueFrom="M48 -16 C48,-16 48,16 48,16 C48,33.67 33.67,48 16,48 C16,48 -16,48 -16,48 C-33.67,48 -48,33.67 -48,16 C-48,16 -48,-16 -48,-16 C-48,-33.67 -33.67,-48 -16,-48 C-16,-48 16,-48 16,-48 C33.67,-48 48,-33.67 48,-16c "
android:valueTo="M48 0.25 C48,0.25 48,0 48,0 C47.75,26 31.25,48 0,48 C0,48 0,48 0,48 C-30,48 -48,25.75 -48,-0.25 C-48,-0.25 -48,-0.25 -48,-0.25 C-47.75,-23.5 -32.25,-47.75 0.5,-48 C0.5,-48 0.5,-48 0.5,-48 C28,-47.75 47.75,-29.75 48,0.25c "
@@ -62,7 +57,7 @@
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator android:propertyName="translateX"
- android:duration="517"
+ android:duration="267"
android:startOffset="0"
android:valueFrom="0"
android:valueTo="1"
@@ -70,4 +65,4 @@
</set>
</aapt:attr>
</target>
-</animated-vector> \ No newline at end of file
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_media_play_container.xml b/packages/SystemUI/res/drawable/ic_media_play_container.xml
index 2fc9fc88a4fd..4cb011a89f35 100644
--- a/packages/SystemUI/res/drawable/ic_media_play_container.xml
+++ b/packages/SystemUI/res/drawable/ic_media_play_container.xml
@@ -41,7 +41,7 @@
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator android:propertyName="pathData"
- android:duration="500"
+ android:duration="250"
android:startOffset="0"
android:valueFrom="M48 0.25 C48,0.25 48,0 48,0 C47.75,26 31.25,48 0,48 C0,48 0,48 0,48 C-30,48 -48,25.75 -48,-0.25 C-48,-0.25 -48,-0.25 -48,-0.25 C-47.75,-23.5 -32.25,-47.75 0.5,-48 C0.5,-48 0.5,-48 0.5,-48 C28,-47.75 47.75,-29.75 48,0.25c "
android:valueTo="M48 -16 C48,-16 48,16 48,16 C48,33.67 33.67,48 16,48 C16,48 -16,48 -16,48 C-33.67,48 -48,33.67 -48,16 C-48,16 -48,-16 -48,-16 C-48,-33.67 -33.67,-48 -16,-48 C-16,-48 16,-48 16,-48 C33.67,-48 48,-33.67 48,-16c "
@@ -57,7 +57,7 @@
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator android:propertyName="translateX"
- android:duration="517"
+ android:duration="267"
android:startOffset="0"
android:valueFrom="0"
android:valueTo="1"
@@ -65,4 +65,4 @@
</set>
</aapt:attr>
</target>
-</animated-vector> \ No newline at end of file
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_screen_saver.xml b/packages/SystemUI/res/drawable/ic_qs_screen_saver.xml
new file mode 100644
index 000000000000..263a3d1c894c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_screen_saver.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+ <path android:fillColor="@android:color/white"
+ android:pathData="M5,15H19L14.5,9L11,13.5L8.5,10.5ZM6,21Q5.575,21 5.287,20.712Q5,20.425 5,20V19H3Q2.175,19 1.588,18.413Q1,17.825 1,17V6Q1,5.175 1.588,4.588Q2.175,4 3,4H21Q21.825,4 22.413,4.588Q23,5.175 23,6V17Q23,17.825 22.413,18.413Q21.825,19 21,19H19V20Q19,20.425 18.712,20.712Q18.425,21 18,21ZM3,17H21Q21,17 21,17Q21,17 21,17V6Q21,6 21,6Q21,6 21,6H3Q3,6 3,6Q3,6 3,6V17Q3,17 3,17Q3,17 3,17ZM3,17Q3,17 3,17Q3,17 3,17V6Q3,6 3,6Q3,6 3,6Q3,6 3,6Q3,6 3,6V17Q3,17 3,17Q3,17 3,17Z"/>
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_screen_saver_undocked.xml b/packages/SystemUI/res/drawable/ic_qs_screen_saver_undocked.xml
new file mode 100644
index 000000000000..1d1b4f507acf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_screen_saver_undocked.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M14.5,9l-3.5,4.51l-2.5,-3.01l-3.5,4.5l14,0z"
+ />
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M21,4H3C1.9,4 1,4.9 1,6V17C1,18.1 1.9,19 3,19H5H19H21C22.1,19 23,18.1 23,17V6C23,4.9 22.1,4 21,4ZM21,17H3V6H21V17Z"
+ />
+</vector>
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
index 3a08a7111d9a..41123c84ded1 100644
--- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
@@ -16,19 +16,13 @@
* limitations under the License.
*/
-->
-<ripple
+<shape
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:color="?android:attr/textColorPrimary">
- <item>
- <shape
- android:shape="rectangle">
- <solid android:color="?androidprv:attr/colorSurface"/>
- <size
- android:width="@dimen/keyguard_affordance_width"
- android:height="@dimen/keyguard_affordance_height"/>
- <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
- </shape>
- </item>
-</ripple>
-
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorSurface"/>
+ <size
+ android:width="@dimen/keyguard_affordance_width"
+ android:height="@dimen/keyguard_affordance_height"/>
+ <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml b/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml
new file mode 100644
index 000000000000..f239a8dd4291
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="183"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c M1.5 1.5 C1.5,1.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml b/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml
new file mode 100644
index 000000000000..d46fafa25816
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="667"
+ android:pathData="M 12.125,34.75C 12.104,30.958 12.021,15.792 12,12"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="517"
+ android:pathData="M 12,12C 12.021,8.312 12.104,-6.436999999999999 12.125,-10.125"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="683"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="12.125"
+ android:translateY="34.75">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M10 4 C10,4 10,2 10,2 C10,2 1.5,-3 1.5,-3 C1.5,-3 1.5,-8.5 1.5,-8.5 C1.5,-9.33 0.83,-10 0,-10 C-0.83,-10 -1.5,-9.33 -1.5,-8.5 C-1.5,-8.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5 C1.5,1.5 10,4 10,4c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c M1.5 1.5 C1.5,1.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_battery_saver_icon_off.xml b/packages/SystemUI/res/drawable/qs_battery_saver_icon_off.xml
new file mode 100644
index 000000000000..7b9f23d133ea
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_battery_saver_icon_off.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="183"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:pathData=" M3.67 -8 C3.67,-8 2,-8 2,-8 C2,-8 2,-10 2,-10 C2,-10 -2,-10 -2,-10 C-2,-10 -2,-8 -2,-8 C-2,-8 -3.67,-8 -3.67,-8 C-4.4,-8 -5,-7.4 -5,-6.67 C-5,-6.67 -5,8.66 -5,8.66 C-5,9.4 -4.4,10 -3.67,10 C-3.67,10 3.66,10 3.66,10 C4.4,10 5,9.4 5,8.67 C5,8.67 5,-6.67 5,-6.67 C5,-7.4 4.4,-8 3.67,-8c "
+ android:strokeAlpha="1"
+ android:strokeColor="#ffffff"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2" />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M2 -10 C2,-10 2,-7 2,-7 C2,-7 -2,-7 -2,-7 C-2,-7 -2,-10 -2,-10 C-2,-10 2,-10 2,-10c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_battery_saver_icon_on.xml b/packages/SystemUI/res/drawable/qs_battery_saver_icon_on.xml
new file mode 100644
index 000000000000..5e4af398e017
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_battery_saver_icon_on.xml
@@ -0,0 +1,636 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_4_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="317"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="fillAlpha"
+ android:startOffset="317"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_4_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:pathData="M 0,0.875C 0,-0.9690000000000001 0,-8.344000000000001 0,-10.188"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_4_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="112"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_3_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="fillAlpha"
+ android:startOffset="250"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="fillAlpha"
+ android:startOffset="333"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="fillAlpha"
+ android:startOffset="550"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_3_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:pathData="M -0.875,9C -0.854,6.156000000000001 -0.896,11.844 -0.875,9"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="550"
+ android:pathData="M -0.875,9C -0.854,6.156000000000001 -0.771,-5.218 -0.75,-8.062"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="83">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_3_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="67"
+ android:valueTo="67"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="550"
+ android:propertyName="rotation"
+ android:startOffset="83"
+ android:valueFrom="67"
+ android:valueTo="192"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="fillAlpha"
+ android:startOffset="417"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="267"
+ android:propertyName="fillAlpha"
+ android:startOffset="500"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="fillAlpha"
+ android:startOffset="767"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:pathData="M 2.125,9.375C 2.146,6.468999999999999 2.104,12.281 2.125,9.375"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="600"
+ android:pathData="M 2.125,9.375C 2.146,6.468999999999999 2.229,-5.155999999999999 2.25,-8.062"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="250">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="28"
+ android:valueTo="28"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="600"
+ android:propertyName="rotation"
+ android:startOffset="250"
+ android:valueFrom="28"
+ android:valueTo="153"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="533"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="fillAlpha"
+ android:startOffset="533"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="fillAlpha"
+ android:startOffset="617"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="fillAlpha"
+ android:startOffset="867"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:pathData="M -2,10C -2,6.99 -2,13.01 -2,10"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="600"
+ android:pathData="M -2,10C -2,6.99 -2,-5.052 -2,-8.062"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="350">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="28"
+ android:valueTo="28"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="600"
+ android:propertyName="rotation"
+ android:startOffset="350"
+ android:valueFrom="28"
+ android:valueTo="153"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="883"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="fillAlpha"
+ android:startOffset="883"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="667"
+ android:pathData="M 0,11.5C 0,9.729 0,13.271 0,11.5"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="0">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="667"
+ android:pathData="M 0,11.5C 0,9.729 0,2.646 0,0.875"
+ android:propertyName="translateXY"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:startOffset="667">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="667"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="-48"
+ android:valueTo="-48"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="667"
+ android:propertyName="rotation"
+ android:startOffset="667"
+ android:valueFrom="-48"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="1350"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_5_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_5_G_D_0_P_0"
+ android:pathData=" M3.67 -8 C3.67,-8 2,-8 2,-8 C2,-8 2,-10 2,-10 C2,-10 -2,-10 -2,-10 C-2,-10 -2,-8 -2,-8 C-2,-8 -3.67,-8 -3.67,-8 C-4.4,-8 -5,-7.4 -5,-6.67 C-5,-6.67 -5,8.66 -5,8.66 C-5,9.4 -4.4,10 -3.67,10 C-3.67,10 3.66,10 3.66,10 C4.4,10 5,9.4 5,8.67 C5,8.67 5,-6.67 5,-6.67 C5,-7.4 4.4,-8 3.67,-8c "
+ android:strokeAlpha="1"
+ android:strokeColor="#ffffff"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2" />
+ <path
+ android:name="_R_G_L_5_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M2 -10 C2,-10 2,-7 2,-7 C2,-7 -2,-7 -2,-7 C-2,-7 -2,-10 -2,-10 C-2,-10 2,-10 2,-10c " />
+ </group>
+ <group
+ android:name="_R_G_L_4_G_N_6_T_0"
+ android:translateX="12"
+ android:translateY="12">
+ <group
+ android:name="_R_G_L_4_G_T_1"
+ android:rotation="0"
+ android:translateX="0"
+ android:translateY="0.875">
+ <group
+ android:name="_R_G_L_4_G"
+ android:translateY="-0.875">
+ <path
+ android:name="_R_G_L_4_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c " />
+ </group>
+ </group>
+ </group>
+ <group
+ android:name="_R_G_L_3_G_N_6_T_0"
+ android:translateX="12"
+ android:translateY="12">
+ <group
+ android:name="_R_G_L_3_G_T_1"
+ android:rotation="67"
+ android:scaleX="0.7"
+ android:scaleY="0.7"
+ android:translateX="-0.875"
+ android:translateY="9">
+ <group
+ android:name="_R_G_L_3_G"
+ android:translateY="-0.875">
+ <path
+ android:name="_R_G_L_3_G_D_0_P_0"
+ android:fillAlpha="0"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c " />
+ </group>
+ </group>
+ </group>
+ <group
+ android:name="_R_G_L_2_G_N_6_T_0"
+ android:translateX="12"
+ android:translateY="12">
+ <group
+ android:name="_R_G_L_2_G_T_1"
+ android:rotation="28"
+ android:scaleX="0.85"
+ android:scaleY="0.85"
+ android:translateX="2.125"
+ android:translateY="9.375">
+ <group
+ android:name="_R_G_L_2_G"
+ android:translateY="-0.875">
+ <path
+ android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillAlpha="0"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c " />
+ </group>
+ </group>
+ </group>
+ <group
+ android:name="_R_G_L_1_G_N_6_T_0"
+ android:translateX="12"
+ android:translateY="12">
+ <group
+ android:name="_R_G_L_1_G_T_1"
+ android:rotation="28"
+ android:scaleX="0.55"
+ android:scaleY="0.55"
+ android:translateX="-2"
+ android:translateY="10">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateY="-0.875">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="0"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c " />
+ </group>
+ </group>
+ </group>
+ <group
+ android:name="_R_G_L_0_G_N_6_T_0"
+ android:translateX="12"
+ android:translateY="12">
+ <group
+ android:name="_R_G_L_0_G_T_1"
+ android:rotation="-48"
+ android:translateX="0"
+ android:translateY="11.5">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateY="-0.875">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="0"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c " />
+ </group>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_camera_access_icon_off.xml b/packages/SystemUI/res/drawable/qs_camera_access_icon_off.xml
new file mode 100644
index 000000000000..8f9ecbe482e3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_camera_access_icon_off.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M5 1.3 C5,1.3 8.35,4.63 8.35,4.63 C8.35,4.63 8.35,-4.58 8.35,-4.58 C8.35,-4.58 5,-1.25 5,-1.25 C5,-1.25 5,-5 5,-5 C5,-5.92 4.25,-6.67 3.33,-6.67 C3.33,-6.67 -6.84,-6.68 -6.84,-6.68 C-6.54,-6.37 -6.07,-5.56 -5.49,-5 C-5.49,-5 3.33,-5 3.33,-5 C3.33,-5 3.33,-1.91 3.33,-1.91 C3.33,-1.91 3.32,3.73 3.32,3.73 C3.32,3.73 5,5.23 5,5.23 C5,5.23 5,2.14 5,2.14 C5,2.14 5,1.3 5,1.3c M3.34 5 C3.34,5 -6.67,5 -6.67,5 C-6.67,5 -6.67,-5.01 -6.67,-5.01 C-6.67,-5.01 -5.5,-5 -5.5,-5 C-5.98,-5.57 -6.46,-6.23 -6.83,-6.68 C-7.84,-6.64 -8.34,-5.77 -8.34,-5.01 C-8.34,-5.01 -8.34,5 -8.34,5 C-8.34,5.91 -7.58,6.66 -6.67,6.66 C-6.67,6.66 3.34,6.66 3.34,6.66 C4.13,6.66 4.88,6.1 5,5.23 C4.73,4.96 3.73,4.13 3.34,3.73 C3.34,3.73 3.34,5 3.34,5c "
+ android:valueTo="M5 1.3 C5,1.3 8.35,4.63 8.35,4.63 C8.35,4.63 8.35,-4.58 8.35,-4.58 C8.35,-4.58 5,-1.25 5,-1.25 C5,-1.25 5,-5 5,-5 C5,-5.92 4.25,-6.67 3.33,-6.67 C3.33,-6.67 -4.28,-6.67 -4.28,-6.67 C-3.98,-6.36 -3.13,-5.56 -2.55,-4.99 C-2.55,-4.99 3.33,-5 3.33,-5 C3.33,-5 3.33,-1.91 3.33,-1.91 C3.33,-1.91 3.33,0.9 3.33,0.9 C3.33,0.9 5.01,2.59 5.01,2.59 C5.01,2.59 5,2.14 5,2.14 C5,2.14 5,1.3 5,1.3c M3.34 5 C3.34,5 -6.67,5 -6.67,5 C-6.67,5 -6.67,-5.01 -6.67,-5.01 C-6.67,-5.01 -5.5,-5 -5.5,-5 C-6.02,-5.5 -6.69,-6.17 -7.12,-6.61 C-7.82,-6.41 -8.34,-5.77 -8.34,-5.01 C-8.34,-5.01 -8.34,5 -8.34,5 C-8.34,5.91 -7.58,6.66 -6.67,6.66 C-6.67,6.66 3.34,6.66 3.34,6.66 C4.13,6.66 4.79,6.11 4.97,5.37 C4.7,5.1 3.73,4.13 3.34,3.73 C3.34,3.73 3.34,5 3.34,5c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-10.96 -7.79 C-10.96,-7.79 -10.96,-7.79 -10.96,-7.79 C-10.96,-7.79 -9.56,-9.19 -9.56,-9.19 C-9.56,-9.19 -9.56,-9.19 -9.56,-9.19 C-9.56,-9.19 -10.96,-7.79 -10.96,-7.79c "
+ android:valueTo="M7.44 10.61 C7.44,10.61 -10.96,-7.79 -10.96,-7.79 C-10.96,-7.79 -9.56,-9.19 -9.56,-9.19 C-9.56,-9.19 8.84,9.21 8.84,9.21 C8.84,9.21 7.44,10.61 7.44,10.61c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="183"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M5 1.3 C5,1.3 8.35,4.63 8.35,4.63 C8.35,4.63 8.35,-4.58 8.35,-4.58 C8.35,-4.58 5,-1.25 5,-1.25 C5,-1.25 5,-5 5,-5 C5,-5.92 4.25,-6.67 3.33,-6.67 C3.33,-6.67 -6.84,-6.68 -6.84,-6.68 C-6.54,-6.37 -6.07,-5.56 -5.49,-5 C-5.49,-5 3.33,-5 3.33,-5 C3.33,-5 3.33,-1.91 3.33,-1.91 C3.33,-1.91 3.32,3.73 3.32,3.73 C3.32,3.73 5,5.23 5,5.23 C5,5.23 5,2.14 5,2.14 C5,2.14 5,1.3 5,1.3c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="0"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-10.96 -7.79 C-10.96,-7.79 -10.96,-7.79 -10.96,-7.79 C-10.96,-7.79 -9.56,-9.19 -9.56,-9.19 C-9.56,-9.19 -9.56,-9.19 -9.56,-9.19 C-9.56,-9.19 -10.96,-7.79 -10.96,-7.79c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_camera_access_icon_on.xml b/packages/SystemUI/res/drawable/qs_camera_access_icon_on.xml
new file mode 100644
index 000000000000..beb8b72d29ad
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_camera_access_icon_on.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M7.44 10.61 C7.44,10.61 -10.96,-7.79 -10.96,-7.79 C-10.96,-7.79 -9.56,-9.19 -9.56,-9.19 C-9.56,-9.19 8.84,9.21 8.84,9.21 C8.84,9.21 7.44,10.61 7.44,10.61c "
+ android:valueTo="M7.44 10.62 C7.44,10.62 7.45,10.62 7.45,10.62 C7.45,10.62 8.85,9.22 8.85,9.22 C8.85,9.22 8.84,9.22 8.84,9.22 C8.84,9.22 7.44,10.62 7.44,10.62c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.55,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="333"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M5 1.3 C5,1.3 8.35,4.63 8.35,4.63 C8.35,4.63 8.35,-4.58 8.35,-4.58 C8.35,-4.58 5,-1.25 5,-1.25 C5,-1.25 5,-5 5,-5 C5,-5.92 4.25,-6.67 3.33,-6.67 C3.33,-6.67 -4.28,-6.67 -4.28,-6.67 C-3.98,-6.36 -3.13,-5.56 -2.55,-4.99 C-2.55,-4.99 3.33,-5 3.33,-5 C3.33,-5 3.33,-1.91 3.33,-1.91 C3.33,-1.91 3.33,0.9 3.33,0.9 C3.33,0.9 5.01,2.59 5.01,2.59 C5.01,2.59 5,2.14 5,2.14 C5,2.14 5,1.3 5,1.3c M3.34 5 C3.34,5 -6.67,5 -6.67,5 C-6.67,5 -6.67,-5.01 -6.67,-5.01 C-6.67,-5.01 -5.5,-5 -5.5,-5 C-6.02,-5.5 -6.69,-6.17 -7.12,-6.61 C-7.82,-6.41 -8.34,-5.77 -8.34,-5.01 C-8.34,-5.01 -8.34,5 -8.34,5 C-8.34,5.91 -7.58,6.66 -6.67,6.66 C-6.67,6.66 3.34,6.66 3.34,6.66 C4.13,6.66 4.79,6.11 4.97,5.37 C4.7,5.1 3.73,4.13 3.34,3.73 C3.34,3.73 3.34,5 3.34,5c "
+ android:valueTo="M5 1.3 C5,1.3 8.35,4.63 8.35,4.63 C8.35,4.63 8.35,-4.58 8.35,-4.58 C8.35,-4.58 5,-1.25 5,-1.25 C5,-1.25 5,-5 5,-5 C5,-5.92 4.25,-6.67 3.33,-6.67 C3.33,-6.67 -6.84,-6.68 -6.84,-6.68 C-6.54,-6.37 -6.07,-5.56 -5.49,-5 C-5.49,-5 3.33,-5 3.33,-5 C3.33,-5 3.33,-1.91 3.33,-1.91 C3.33,-1.91 3.32,3.73 3.32,3.73 C3.32,3.73 5,5.23 5,5.23 C5,5.23 5,2.14 5,2.14 C5,2.14 5,1.3 5,1.3c M3.34 5 C3.34,5 -6.67,5 -6.67,5 C-6.67,5 -6.67,-5.01 -6.67,-5.01 C-6.67,-5.01 -5.5,-5 -5.5,-5 C-5.98,-5.57 -6.46,-6.23 -6.83,-6.68 C-7.84,-6.64 -8.34,-5.77 -8.34,-5.01 C-8.34,-5.01 -8.34,5 -8.34,5 C-8.34,5.91 -7.58,6.66 -6.67,6.66 C-6.67,6.66 3.34,6.66 3.34,6.66 C4.13,6.66 4.88,6.1 5,5.23 C4.73,4.96 3.73,4.13 3.34,3.73 C3.34,3.73 3.34,5 3.34,5c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M7.44 10.61 C7.44,10.61 -10.96,-7.79 -10.96,-7.79 C-10.96,-7.79 -9.56,-9.19 -9.56,-9.19 C-9.56,-9.19 8.84,9.21 8.84,9.21 C8.84,9.21 7.44,10.61 7.44,10.61c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M5 1.3 C5,1.3 8.35,4.63 8.35,4.63 C8.35,4.63 8.35,-4.58 8.35,-4.58 C8.35,-4.58 5,-1.25 5,-1.25 C5,-1.25 5,-5 5,-5 C5,-5.92 4.25,-6.67 3.33,-6.67 C3.33,-6.67 -4.28,-6.67 -4.28,-6.67 C-3.98,-6.36 -3.13,-5.56 -2.55,-4.99 C-2.55,-4.99 3.33,-5 3.33,-5 C3.33,-5 3.33,-1.91 3.33,-1.91 C3.33,-1.91 3.33,0.9 3.33,0.9 C3.33,0.9 5.01,2.59 5.01,2.59 C5.01,2.59 5,2.14 5,2.14 C5,2.14 5,1.3 5,1.3c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_dnd_icon_off.xml b/packages/SystemUI/res/drawable/qs_dnd_icon_off.xml
new file mode 100644
index 000000000000..e42381a4d5a3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_dnd_icon_off.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="183"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:rotation="-225"
+ android:scaleX="0.85"
+ android:scaleY="0.85"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-2.83 -4.24 C-2.83,-4.24 4.24,2.83 4.24,2.83 C4.24,2.83 2.83,4.24 2.83,4.24 C2.83,4.24 -4.24,-2.83 -4.24,-2.83 C-4.24,-2.83 -2.83,-4.24 -2.83,-4.24c M7.07 7.09 C4.65,9.51 1.78,10.02 0,10.02 C-5.52,10.02 -10,5.54 -10,0.02 C-10,-1.76 -9.49,-4.52 -7.07,-7.07 C-7.07,-7.07 -5.66,-5.67 -5.66,-5.67 C-7.73,-3.44 -8,-1.2 -8,0.02 C-8,4.43 -4.41,8.02 0,8.02 C1.22,8.02 3.42,7.71 5.67,5.64 C5.67,5.64 7.07,7.09 7.07,7.09c M-7.06 -7.1 C-4.62,-9.54 -1.81,-9.94 -0.03,-9.94 C5.49,-9.94 9.97,-5.46 9.97,0.06 C9.97,1.84 9.49,4.63 7.07,7.05 C7.07,7.05 5.66,5.64 5.66,5.64 C7.67,3.51 7.97,1.28 7.97,0.06 C7.97,-4.35 4.38,-7.94 -0.03,-7.94 C-1.25,-7.94 -3.43,-7.88 -5.65,-5.66 C-5.65,-5.66 -7.06,-7.1 -7.06,-7.1c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_dnd_icon_on.xml b/packages/SystemUI/res/drawable/qs_dnd_icon_on.xml
new file mode 100644
index 000000000000..a63cb238f5a0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_dnd_icon_on.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="500"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="-225"
+ android:valueTo="-45"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="517"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:rotation="-225"
+ android:scaleX="0.85"
+ android:scaleY="0.85"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-2.83 -4.24 C-2.83,-4.24 4.24,2.83 4.24,2.83 C4.24,2.83 2.83,4.24 2.83,4.24 C2.83,4.24 -4.24,-2.83 -4.24,-2.83 C-4.24,-2.83 -2.83,-4.24 -2.83,-4.24c M7.07 7.09 C4.65,9.51 1.78,10.02 0,10.02 C-5.52,10.02 -10,5.54 -10,0.02 C-10,-1.76 -9.49,-4.52 -7.07,-7.07 C-7.07,-7.07 -5.66,-5.67 -5.66,-5.67 C-7.73,-3.44 -8,-1.2 -8,0.02 C-8,4.43 -4.41,8.02 0,8.02 C1.22,8.02 3.42,7.71 5.67,5.64 C5.67,5.64 7.07,7.09 7.07,7.09c M-7.06 -7.1 C-4.62,-9.54 -1.81,-9.94 -0.03,-9.94 C5.49,-9.94 9.97,-5.46 9.97,0.06 C9.97,1.84 9.49,4.63 7.07,7.05 C7.07,7.05 5.66,5.64 5.66,5.64 C7.67,3.51 7.97,1.28 7.97,0.06 C7.97,-4.35 4.38,-7.94 -0.03,-7.94 C-1.25,-7.94 -3.43,-7.88 -5.65,-5.66 C-5.65,-5.66 -7.06,-7.1 -7.06,-7.1c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_location_icon_off.xml b/packages/SystemUI/res/drawable/qs_location_icon_off.xml
new file mode 100644
index 000000000000..97eb91cd33b0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_location_icon_off.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="175"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M3.98 4.01 C3.98,4.01 2.56,2.54 2.56,2.54 C3.34,1.49 4.41,0.01 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -4.01,-6.61 -4.61,-4.61 C-4.61,-4.61 -6.24,-6.22 -6.24,-6.22 C-5.27,-8.11 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.86,-0.99 6.49,0.02 C6.12,1.04 4.57,3.23 3.98,4.01c "
+ android:valueTo="M4.62 3.11 C4.62,3.11 3.2,1.65 3.2,1.65 C3.77,0.85 4.43,-0.12 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -3.55,-6.78 -4.16,-5.65 C-4.16,-5.65 -5.67,-7.13 -5.67,-7.13 C-4.81,-8.35 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.85,-0.89 6.49,0.02 C6.13,0.94 5.16,2.35 4.62,3.11c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="75"
+ android:propertyName="pathData"
+ android:startOffset="175"
+ android:valueFrom="M4.62 3.11 C4.62,3.11 3.2,1.65 3.2,1.65 C3.77,0.85 4.43,-0.12 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -3.55,-6.78 -4.16,-5.65 C-4.16,-5.65 -5.67,-7.13 -5.67,-7.13 C-4.81,-8.35 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.85,-0.89 6.49,0.02 C6.13,0.94 5.16,2.35 4.62,3.11c "
+ android:valueTo="M5.18 2.33 C5.18,2.33 3.75,0.88 3.75,0.88 C4.13,0.29 4.45,-0.23 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -3.16,-6.94 -3.61,-6.46 C-3.61,-6.46 -5.07,-7.86 -5.07,-7.86 C-4.42,-8.54 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.84,-0.81 6.49,0.02 C6.14,0.86 5.68,1.58 5.18,2.33c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="125"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-0.96 -0.67 C0.37,-0.16 1.79,-0.77 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-2.08,-4.88 -2.84,-3.58 -2.35,-2.11 C-2.16,-1.55 -1.58,-0.91 -0.96,-0.67c "
+ android:valueTo="M-0.29 -0.52 C0.81,-0.39 1.86,-0.95 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.93,-4.94 -2.6,-3.89 -2.49,-2.82 C-2.18,-2.46 -0.67,-0.84 -0.29,-0.52c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="50"
+ android:propertyName="pathData"
+ android:startOffset="125"
+ android:valueFrom="M-0.29 -0.52 C0.81,-0.39 1.86,-0.95 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.93,-4.94 -2.6,-3.89 -2.49,-2.82 C-2.18,-2.46 -0.67,-0.84 -0.29,-0.52c "
+ android:valueTo="M0.87 -0.65 C1.56,-0.93 2.02,-1.3 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.63,-5.06 -2.14,-4.44 -2.33,-3.91 C-2.11,-3.68 0.66,-0.85 0.87,-0.65c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="75"
+ android:propertyName="pathData"
+ android:startOffset="175"
+ android:valueFrom="M0.87 -0.65 C1.56,-0.93 2.02,-1.3 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.63,-5.06 -2.14,-4.44 -2.33,-3.91 C-2.11,-3.68 0.66,-0.85 0.87,-0.65c "
+ android:valueTo="M1.8 -1.24 C2.03,-1.47 2.21,-1.76 2.33,-2.05 C2.44,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.25,-5.21 -1.57,-5.02 -1.77,-4.78 C-1.77,-4.78 1.8,-1.24 1.8,-1.24c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="125"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-9.89 -7.81 C-9.89,-7.81 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 -8.49,-9.21 -8.49,-9.21 C-8.49,-9.21 -9.89,-7.81 -9.89,-7.81c "
+ android:valueTo="M-9.89 -7.81 C-9.89,-7.81 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 -8.49,-9.21 -8.49,-9.21 C-8.49,-9.21 -9.89,-7.81 -9.89,-7.81c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="125"
+ android:propertyName="pathData"
+ android:startOffset="125"
+ android:valueFrom="M-9.89 -7.81 C-9.89,-7.81 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 -8.49,-9.21 -8.49,-9.21 C-8.49,-9.21 -9.89,-7.81 -9.89,-7.81c "
+ android:valueTo="M8.5 10.6 C8.5,10.6 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="267"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-5.05 -2.1 C-5.05,-1.06 -4.64,-0.4 -4.23,0.3 C-3.81,1 -3.28,1.75 -2.65,2.55 C-2.22,3.1 -1.78,3.71 -1.35,4.39 C-0.92,5.06 -0.39,5.6 0.01,6.43 C0.21,5.95 0.61,5.27 0.98,4.72 C1.44,4.01 2.13,3.09 2.55,2.54 C2.94,2.95 3.54,3.56 3.98,4 C3.47,4.64 2.65,5.82 2.1,6.77 C1.66,7.55 1.28,8.44 0.98,9.26 C0.88,9.55 0.77,9.67 0.59,9.81 C0.44,9.93 0.28,9.99 0,9.99 C-0.28,9.99 -0.48,9.89 -0.68,9.66 C-0.88,9.44 -1.03,9.18 -1.15,8.9 C-1.45,8.07 -1.68,7.43 -2.17,6.65 C-2.67,5.88 -3.27,4.87 -4.15,3.79 C-4.88,2.75 -5.67,1.6 -6.21,0.62 C-6.74,-0.36 -7,-1.76 -7,-3.01 C-7,-4.08 -6.77,-5.28 -6.23,-6.23 C-5.75,-5.73 -4.92,-4.92 -4.61,-4.61 C-4.87,-3.95 -5.06,-2.93 -5.05,-2.1c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M3.98 4.01 C3.98,4.01 2.56,2.54 2.56,2.54 C3.34,1.49 4.41,0.01 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -4.01,-6.61 -4.61,-4.61 C-4.61,-4.61 -6.24,-6.22 -6.24,-6.22 C-5.27,-8.11 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.86,-0.99 6.49,0.02 C6.12,1.04 4.57,3.23 3.98,4.01c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_1"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-0.96 -0.67 C0.37,-0.16 1.79,-0.77 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-2.08,-4.88 -2.84,-3.58 -2.35,-2.11 C-2.16,-1.55 -1.58,-0.91 -0.96,-0.67c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M-9.89 -7.81 C-9.89,-7.81 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 -8.49,-9.21 -8.49,-9.21 C-8.49,-9.21 -9.89,-7.81 -9.89,-7.81c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_location_icon_on.xml b/packages/SystemUI/res/drawable/qs_location_icon_on.xml
new file mode 100644
index 000000000000..c56b6508eb49
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_location_icon_on.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M5.18 2.33 C5.18,2.33 3.75,0.88 3.75,0.88 C4.13,0.29 4.45,-0.23 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -3.16,-6.94 -3.61,-6.46 C-3.61,-6.46 -5.07,-7.86 -5.07,-7.86 C-4.42,-8.54 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.84,-0.81 6.49,0.02 C6.14,0.86 5.68,1.58 5.18,2.33c "
+ android:valueTo="M4.62 3.11 C4.62,3.11 3.2,1.65 3.2,1.65 C3.77,0.85 4.43,-0.12 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -3.55,-6.78 -4.16,-5.65 C-4.16,-5.65 -5.67,-7.13 -5.67,-7.13 C-4.81,-8.35 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.85,-0.89 6.49,0.02 C6.13,0.94 5.16,2.35 4.62,3.11c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="233"
+ android:propertyName="pathData"
+ android:startOffset="100"
+ android:valueFrom="M4.62 3.11 C4.62,3.11 3.2,1.65 3.2,1.65 C3.77,0.85 4.43,-0.12 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -3.55,-6.78 -4.16,-5.65 C-4.16,-5.65 -5.67,-7.13 -5.67,-7.13 C-4.81,-8.35 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.85,-0.89 6.49,0.02 C6.13,0.94 5.16,2.35 4.62,3.11c "
+ android:valueTo="M3.98 4.01 C3.98,4.01 2.56,2.54 2.56,2.54 C3.34,1.49 4.41,0.01 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -4.01,-6.61 -4.61,-4.61 C-4.61,-4.61 -6.24,-6.22 -6.24,-6.22 C-5.27,-8.11 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.86,-0.99 6.49,0.02 C6.12,1.04 4.57,3.23 3.98,4.01c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M1.8 -1.24 C2.03,-1.47 2.21,-1.76 2.33,-2.05 C2.44,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.25,-5.21 -1.57,-5.02 -1.77,-4.78 C-1.77,-4.78 1.8,-1.24 1.8,-1.24c "
+ android:valueTo="M0.87 -0.65 C1.56,-0.93 2.02,-1.3 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.63,-5.06 -2.14,-4.44 -2.33,-3.91 C-2.11,-3.68 0.66,-0.85 0.87,-0.65c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="100"
+ android:valueFrom="M0.87 -0.65 C1.56,-0.93 2.02,-1.3 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.63,-5.06 -2.14,-4.44 -2.33,-3.91 C-2.11,-3.68 0.66,-0.85 0.87,-0.65c "
+ android:valueTo="M-0.29 -0.52 C0.81,-0.39 1.86,-0.95 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.93,-4.94 -2.6,-3.89 -2.49,-2.82 C-2.18,-2.46 -0.67,-0.84 -0.29,-0.52c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="pathData"
+ android:startOffset="167"
+ android:valueFrom="M-0.29 -0.52 C0.81,-0.39 1.86,-0.95 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.93,-4.94 -2.6,-3.89 -2.49,-2.82 C-2.18,-2.46 -0.67,-0.84 -0.29,-0.52c "
+ android:valueTo="M-0.96 -0.67 C0.37,-0.16 1.79,-0.77 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-2.08,-4.88 -2.84,-3.58 -2.35,-2.11 C-2.16,-1.55 -1.58,-0.91 -0.96,-0.67c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M8.5 10.6 C8.5,10.6 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c "
+ android:valueTo="M8.5 10.6 C8.5,10.6 8.51,10.6 8.51,10.6 C8.51,10.6 9.91,9.2 9.91,9.2 C9.91,9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-5.05 -2.1 C-5.05,-1.06 -4.64,-0.4 -4.23,0.3 C-3.81,1 -3.28,1.75 -2.65,2.55 C-2.22,3.1 -1.78,3.71 -1.35,4.39 C-0.92,5.06 -0.39,5.6 0.01,6.43 C0.21,5.95 0.61,5.27 0.98,4.72 C1.44,4.01 2.13,3.09 2.55,2.54 C2.94,2.95 3.54,3.56 3.98,4 C3.47,4.64 2.65,5.82 2.1,6.77 C1.66,7.55 1.28,8.44 0.98,9.26 C0.88,9.55 0.77,9.67 0.59,9.81 C0.44,9.93 0.28,9.99 0,9.99 C-0.28,9.99 -0.48,9.89 -0.68,9.66 C-0.88,9.44 -1.03,9.18 -1.15,8.9 C-1.45,8.07 -1.68,7.43 -2.17,6.65 C-2.67,5.88 -3.27,4.87 -4.15,3.79 C-4.88,2.75 -5.67,1.6 -6.21,0.62 C-6.74,-0.36 -7,-1.76 -7,-3.01 C-7,-4.08 -6.77,-5.28 -6.23,-6.23 C-5.75,-5.73 -4.92,-4.92 -4.61,-4.61 C-4.87,-3.95 -5.06,-2.93 -5.05,-2.1c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M5.18 2.33 C5.18,2.33 3.75,0.88 3.75,0.88 C4.13,0.29 4.45,-0.23 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -3.16,-6.94 -3.61,-6.46 C-3.61,-6.46 -5.07,-7.86 -5.07,-7.86 C-4.42,-8.54 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.84,-0.81 6.49,0.02 C6.14,0.86 5.68,1.58 5.18,2.33c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_1"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M1.8 -1.24 C2.03,-1.47 2.21,-1.76 2.33,-2.05 C2.44,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.25,-5.21 -1.57,-5.02 -1.77,-4.78 C-1.77,-4.78 1.8,-1.24 1.8,-1.24c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M8.5 10.6 C8.5,10.6 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_mic_access_off.xml b/packages/SystemUI/res/drawable/qs_mic_access_off.xml
new file mode 100644
index 000000000000..63a94443c255
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_mic_access_off.xml
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M5.05 4.43 C3.98,5.25 3.08,5.53 2.53,5.73 C2.26,5.81 1.98,5.87 1.7,5.9 C1.7,5.9 1.7,9 1.7,9 C1.7,9 -0.3,9 -0.3,9 C-0.3,9 -0.3,5.9 -0.3,5.9 C-2.02,5.65 -3.44,4.88 -4.59,3.59 C-5.73,2.3 -6.3,0.77 -6.3,-1 C-6.3,-1 -4.3,-1 -4.3,-1 C-4.3,0.38 -3.81,1.56 -2.84,2.54 C-1.86,3.51 -0.68,4 0.7,4 C0.88,4 1.06,3.99 1.23,3.96 C1.39,3.94 2.58,3.77 3.61,2.99 C3.61,2.99 5.05,4.43 5.05,4.43c "
+ android:valueTo="M5.02 4.5 C3.59,5.47 2.79,5.64 2.53,5.73 C2.26,5.81 1.98,5.87 1.7,5.9 C1.7,5.9 1.7,9 1.7,9 C1.7,9 -0.3,9 -0.3,9 C-0.3,9 -0.3,5.9 -0.3,5.9 C-2.02,5.65 -3.44,4.88 -4.59,3.59 C-5.73,2.3 -6.3,0.77 -6.3,-1 C-6.3,-1 -4.3,-1 -4.3,-1 C-4.3,0.38 -3.81,1.56 -2.84,2.54 C-1.86,3.51 -0.68,4 0.7,4 C0.88,4 1.06,3.99 1.23,3.96 C1.39,3.94 2.53,3.83 3.58,3.07 C3.58,3.07 5.02,4.5 5.02,4.5c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="44"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M2.31 1.86 C2.31,1.86 1.01,0.55 0.75,0.31 C1.7,0.27 1.7,-0.7 1.7,-0.7 C1.7,-0.7 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-0.66 -0.29,-0.66 C-0.29,-0.66 -2.31,-2.67 -2.31,-2.67 C-2.31,-2.67 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.7,-0.33 3.64,-0.03 C3.51,0.52 3.23,1.19 2.31,1.86c "
+ android:valueTo="M2.86 1.48 C2.86,1.48 1.58,0.21 1.39,0.03 C1.77,-0.26 1.7,-1.03 1.7,-1.03 C1.7,-1.03 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-1.51 -0.29,-1.51 C-0.29,-1.51 -2.31,-3.5 -2.31,-3.5 C-2.31,-3.5 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.68,-0.44 3.65,-0.17 C3.61,0.19 3.36,1.05 2.86,1.48c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="39"
+ android:propertyName="pathData"
+ android:startOffset="44"
+ android:valueFrom="M2.86 1.48 C2.86,1.48 1.58,0.21 1.39,0.03 C1.77,-0.26 1.7,-1.03 1.7,-1.03 C1.7,-1.03 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-1.51 -0.29,-1.51 C-0.29,-1.51 -2.31,-3.5 -2.31,-3.5 C-2.31,-3.5 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.68,-0.44 3.65,-0.17 C3.61,0.19 3.36,1.05 2.86,1.48c "
+ android:valueTo="M3.12 0.99 C3.12,0.99 1.83,-0.26 1.71,-0.38 C1.72,-0.81 1.7,-1.31 1.7,-1.31 C1.7,-1.31 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-2.26 -0.29,-2.26 C-0.29,-2.26 -2.31,-4.23 -2.31,-4.23 C-2.31,-4.23 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.67,-0.54 3.66,-0.3 C3.63,0.12 3.38,0.7 3.12,0.99c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="pathData"
+ android:startOffset="83"
+ android:valueFrom="M3.12 0.99 C3.12,0.99 1.83,-0.26 1.71,-0.38 C1.72,-0.81 1.7,-1.31 1.7,-1.31 C1.7,-1.31 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-2.26 -0.29,-2.26 C-0.29,-2.26 -2.31,-4.23 -2.31,-4.23 C-2.31,-4.23 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.67,-0.54 3.66,-0.3 C3.63,0.12 3.38,0.7 3.12,0.99c "
+ android:valueTo="M3.5 -0.05 C3.5,-0.05 1.7,-1.85 1.7,-1.85 C1.7,-1.85 1.7,-1.92 1.7,-1.92 C1.7,-1.92 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.3,-3.85 -0.3,-3.85 C-0.3,-3.85 -2.3,-5.85 -2.3,-5.85 C-2.3,-5.85 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.68,-0.65 3.64,-0.5 C3.6,-0.35 3.55,-0.2 3.5,-0.05c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M5.04 4.45 C5.04,4.45 3.59,3.02 3.59,3.02 C4.82,1.94 5.31,0.96 5.53,0.3 C5.64,-0.12 5.7,-0.55 5.7,-1 C5.7,-1 7.7,-1 7.7,-1 C7.7,-0.27 7.59,0.43 7.38,1.09 C7.16,1.75 6.51,3.17 5.04,4.45c "
+ android:valueTo="M6.45 2.95 C6.45,2.95 5,1.5 5,1.5 C5.23,1.12 5.41,0.72 5.53,0.3 C5.64,-0.12 5.7,-0.55 5.7,-1 C5.7,-1 7.7,-1 7.7,-1 C7.7,-0.27 7.59,0.43 7.38,1.09 C7.16,1.75 6.85,2.37 6.45,2.95c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C-0.4,2.17 -0.94,1.89 -1.41,1.42 C-1.89,0.94 -2.18,0.37 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c "
+ android:valueTo="M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C-0.4,2.17 -0.94,1.89 -1.41,1.42 C-1.89,0.94 -2.18,0.37 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="pathData"
+ android:startOffset="67"
+ android:valueFrom="M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C-0.4,2.17 -0.94,1.89 -1.41,1.42 C-1.89,0.94 -2.18,0.37 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c "
+ android:valueTo="M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C0.09,2.2 -0.55,1.45 -1.02,0.98 C-1.5,0.5 -2.23,-0.14 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_4_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_4_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-9.89 -7.81 C-9.89,-7.81 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 -8.49,-9.21 -8.49,-9.21 C-8.49,-9.21 -9.89,-7.81 -9.89,-7.81c "
+ android:valueTo="M8.5 10.6 C8.5,10.6 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="267"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M5.05 4.43 C3.98,5.25 3.08,5.53 2.53,5.73 C2.26,5.81 1.98,5.87 1.7,5.9 C1.7,5.9 1.7,9 1.7,9 C1.7,9 -0.3,9 -0.3,9 C-0.3,9 -0.3,5.9 -0.3,5.9 C-2.02,5.65 -3.44,4.88 -4.59,3.59 C-5.73,2.3 -6.3,0.77 -6.3,-1 C-6.3,-1 -4.3,-1 -4.3,-1 C-4.3,0.38 -3.81,1.56 -2.84,2.54 C-1.86,3.51 -0.68,4 0.7,4 C0.88,4 1.06,3.99 1.23,3.96 C1.39,3.94 2.58,3.77 3.61,2.99 C3.61,2.99 5.05,4.43 5.05,4.43c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M2.31 1.86 C2.31,1.86 1.01,0.55 0.75,0.31 C1.7,0.27 1.7,-0.7 1.7,-0.7 C1.7,-0.7 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-0.66 -0.29,-0.66 C-0.29,-0.66 -2.31,-2.67 -2.31,-2.67 C-2.31,-2.67 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.7,-0.33 3.64,-0.03 C3.51,0.52 3.23,1.19 2.31,1.86c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M5.04 4.45 C5.04,4.45 3.59,3.02 3.59,3.02 C4.82,1.94 5.31,0.96 5.53,0.3 C5.64,-0.12 5.7,-0.55 5.7,-1 C5.7,-1 7.7,-1 7.7,-1 C7.7,-0.27 7.59,0.43 7.38,1.09 C7.16,1.75 6.51,3.17 5.04,4.45c " />
+ <path
+ android:name="_R_G_L_0_G_D_3_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C-0.4,2.17 -0.94,1.89 -1.41,1.42 C-1.89,0.94 -2.18,0.37 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c " />
+ <path
+ android:name="_R_G_L_0_G_D_4_P_0"
+ android:fillAlpha="0"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M-9.89 -7.81 C-9.89,-7.81 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 -8.49,-9.21 -8.49,-9.21 C-8.49,-9.21 -9.89,-7.81 -9.89,-7.81c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_mic_access_on.xml b/packages/SystemUI/res/drawable/qs_mic_access_on.xml
new file mode 100644
index 000000000000..b485f06d1bf2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_mic_access_on.xml
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M5.02 4.5 C3.59,5.47 2.79,5.64 2.53,5.73 C2.26,5.81 1.98,5.87 1.7,5.9 C1.7,5.9 1.7,9 1.7,9 C1.7,9 -0.3,9 -0.3,9 C-0.3,9 -0.3,5.9 -0.3,5.9 C-2.02,5.65 -3.44,4.88 -4.59,3.59 C-5.73,2.3 -6.3,0.77 -6.3,-1 C-6.3,-1 -4.3,-1 -4.3,-1 C-4.3,0.38 -3.81,1.56 -2.84,2.54 C-1.86,3.51 -0.68,4 0.7,4 C0.88,4 1.06,3.99 1.23,3.96 C1.39,3.94 2.53,3.83 3.58,3.07 C3.58,3.07 5.02,4.5 5.02,4.5c "
+ android:valueTo="M5.05 4.43 C3.98,5.25 3.08,5.53 2.53,5.73 C2.26,5.81 1.98,5.87 1.7,5.9 C1.7,5.9 1.7,9 1.7,9 C1.7,9 -0.3,9 -0.3,9 C-0.3,9 -0.3,5.9 -0.3,5.9 C-2.02,5.65 -3.44,4.88 -4.59,3.59 C-5.73,2.3 -6.3,0.77 -6.3,-1 C-6.3,-1 -4.3,-1 -4.3,-1 C-4.3,0.38 -3.81,1.56 -2.84,2.54 C-1.86,3.51 -0.68,4 0.7,4 C0.88,4 1.06,3.99 1.23,3.96 C1.39,3.94 2.58,3.77 3.61,2.99 C3.61,2.99 5.05,4.43 5.05,4.43c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="125"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M3.5 -0.05 C3.5,-0.05 1.7,-1.85 1.7,-1.85 C1.7,-1.85 1.7,-1.92 1.7,-1.92 C1.7,-1.92 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.3,-3.85 -0.3,-3.85 C-0.3,-3.85 -2.3,-5.85 -2.3,-5.85 C-2.3,-5.85 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.68,-0.65 3.64,-0.5 C3.6,-0.35 3.55,-0.2 3.5,-0.05c "
+ android:valueTo="M3.12 0.99 C3.12,0.99 1.83,-0.26 1.71,-0.38 C1.72,-0.81 1.7,-1.31 1.7,-1.31 C1.7,-1.31 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-2.26 -0.29,-2.26 C-0.29,-2.26 -2.31,-4.23 -2.31,-4.23 C-2.31,-4.23 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.67,-0.54 3.66,-0.3 C3.63,0.12 3.38,0.7 3.12,0.99c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="58"
+ android:propertyName="pathData"
+ android:startOffset="125"
+ android:valueFrom="M3.12 0.99 C3.12,0.99 1.83,-0.26 1.71,-0.38 C1.72,-0.81 1.7,-1.31 1.7,-1.31 C1.7,-1.31 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-2.26 -0.29,-2.26 C-0.29,-2.26 -2.31,-4.23 -2.31,-4.23 C-2.31,-4.23 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.67,-0.54 3.66,-0.3 C3.63,0.12 3.38,0.7 3.12,0.99c "
+ android:valueTo="M2.86 1.48 C2.86,1.48 1.58,0.21 1.39,0.03 C1.77,-0.26 1.7,-1.03 1.7,-1.03 C1.7,-1.03 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-1.51 -0.29,-1.51 C-0.29,-1.51 -2.31,-3.5 -2.31,-3.5 C-2.31,-3.5 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.68,-0.44 3.65,-0.17 C3.61,0.19 3.36,1.05 2.86,1.48c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="183"
+ android:valueFrom="M2.86 1.48 C2.86,1.48 1.58,0.21 1.39,0.03 C1.77,-0.26 1.7,-1.03 1.7,-1.03 C1.7,-1.03 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-1.51 -0.29,-1.51 C-0.29,-1.51 -2.31,-3.5 -2.31,-3.5 C-2.31,-3.5 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.68,-0.44 3.65,-0.17 C3.61,0.19 3.36,1.05 2.86,1.48c "
+ android:valueTo="M2.42 1.81 C2.42,1.81 1.16,0.52 0.9,0.28 C1.7,0.14 1.7,-0.7 1.7,-0.7 C1.7,-0.7 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-0.66 -0.29,-0.66 C-0.29,-0.66 -2.31,-2.67 -2.31,-2.67 C-2.31,-2.67 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.7,-0.33 3.64,-0.03 C3.58,0.27 3.22,1.2 2.42,1.81c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M6.45 2.95 C6.45,2.95 5,1.5 5,1.5 C5.23,1.12 5.41,0.72 5.53,0.3 C5.64,-0.12 5.7,-0.55 5.7,-1 C5.7,-1 7.7,-1 7.7,-1 C7.7,-0.27 7.59,0.43 7.38,1.09 C7.16,1.75 6.85,2.37 6.45,2.95c "
+ android:valueTo="M5.04 4.43 C5.04,4.43 3.59,2.98 3.59,2.98 C4.61,2.09 5.41,0.72 5.53,0.3 C5.64,-0.12 5.7,-0.55 5.7,-1 C5.7,-1 7.7,-1 7.7,-1 C7.7,-0.27 7.59,0.43 7.38,1.09 C7.16,1.75 6.19,3.44 5.04,4.43c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_3_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C0.09,2.2 -0.55,1.45 -1.02,0.98 C-1.5,0.5 -2.23,-0.14 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c "
+ android:valueTo="M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C-0.4,2.17 -0.94,1.89 -1.41,1.42 C-1.89,0.94 -2.18,0.37 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_4_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="400"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="fillAlpha"
+ android:startOffset="400"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_4_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M8.5 10.6 C8.5,10.6 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c "
+ android:valueTo="M8.5 10.6 C8.5,10.6 8.51,10.6 8.51,10.6 C8.51,10.6 9.91,9.2 9.91,9.2 C9.91,9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="433"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M5.02 4.5 C3.59,5.47 2.79,5.64 2.53,5.73 C2.26,5.81 1.98,5.87 1.7,5.9 C1.7,5.9 1.7,9 1.7,9 C1.7,9 -0.3,9 -0.3,9 C-0.3,9 -0.3,5.9 -0.3,5.9 C-2.02,5.65 -3.44,4.88 -4.59,3.59 C-5.73,2.3 -6.3,0.77 -6.3,-1 C-6.3,-1 -4.3,-1 -4.3,-1 C-4.3,0.38 -3.81,1.56 -2.84,2.54 C-1.86,3.51 -0.68,4 0.7,4 C0.88,4 1.06,3.99 1.23,3.96 C1.39,3.94 2.53,3.83 3.58,3.07 C3.58,3.07 5.02,4.5 5.02,4.5c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M3.5 -0.05 C3.5,-0.05 1.7,-1.85 1.7,-1.85 C1.7,-1.85 1.7,-1.92 1.7,-1.92 C1.7,-1.92 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.3,-3.85 -0.3,-3.85 C-0.3,-3.85 -2.3,-5.85 -2.3,-5.85 C-2.3,-5.85 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.68,-0.65 3.64,-0.5 C3.6,-0.35 3.55,-0.2 3.5,-0.05c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M6.45 2.95 C6.45,2.95 5,1.5 5,1.5 C5.23,1.12 5.41,0.72 5.53,0.3 C5.64,-0.12 5.7,-0.55 5.7,-1 C5.7,-1 7.7,-1 7.7,-1 C7.7,-0.27 7.59,0.43 7.38,1.09 C7.16,1.75 6.85,2.37 6.45,2.95c " />
+ <path
+ android:name="_R_G_L_0_G_D_3_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C0.09,2.2 -0.55,1.45 -1.02,0.98 C-1.5,0.5 -2.23,-0.14 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c " />
+ <path
+ android:name="_R_G_L_0_G_D_4_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M8.5 10.6 C8.5,10.6 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/screenshot_edit_background.xml b/packages/SystemUI/res/drawable/screenshot_edit_background.xml
index ff5c62e1600b..a1185a2d5479 100644
--- a/packages/SystemUI/res/drawable/screenshot_edit_background.xml
+++ b/packages/SystemUI/res/drawable/screenshot_edit_background.xml
@@ -17,7 +17,7 @@
<!-- Long screenshot edit FAB background -->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:color="?android:textColorPrimary">
+ android:color="@color/overlay_button_ripple">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<solid android:color="?androidprv:attr/colorAccentPrimary"/>
diff --git a/packages/SystemUI/res/drawable/settings_input_antenna.xml b/packages/SystemUI/res/drawable/settings_input_antenna.xml
new file mode 100644
index 000000000000..f2adcaf069ef
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settings_input_antenna.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp"
+ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"
+ android:tint="?android:attr/textColorSecondary">
+ <path android:fillColor="#FF000000"
+ android:pathData="M9,22.4 L7.6,21 11,17.6V14.3Q10.325,14 9.913,13.375Q9.5,12.75 9.5,12Q9.5,10.95 10.225,10.225Q10.95,9.5 12,9.5Q13.05,9.5 13.775,10.225Q14.5,10.95 14.5,12Q14.5,12.75 14.088,13.375Q13.675,14 13,14.3V17.6L16.4,21L15,22.4L12,19.4ZM5,12Q5,9.05 7.05,7.025Q9.1,5 12,5Q14.9,5 16.95,7.025Q19,9.05 19,12H17Q17,9.925 15.538,8.462Q14.075,7 12,7Q9.925,7 8.463,8.462Q7,9.925 7,12ZM1,12Q1,9.7 1.863,7.7Q2.725,5.7 4.225,4.212Q5.725,2.725 7.725,1.862Q9.725,1 12,1Q14.275,1 16.275,1.862Q18.275,2.725 19.775,4.212Q21.275,5.7 22.138,7.7Q23,9.7 23,12H21Q21,10.125 20.288,8.487Q19.575,6.85 18.35,5.625Q17.125,4.4 15.488,3.7Q13.85,3 12,3Q10.15,3 8.512,3.7Q6.875,4.4 5.65,5.625Q4.425,6.85 3.712,8.487Q3,10.125 3,12Z"/>
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_container_view.xml b/packages/SystemUI/res/layout/auth_container_view.xml
index 3db01a4e7f3a..2bd2e640127e 100644
--- a/packages/SystemUI/res/layout/auth_container_view.xml
+++ b/packages/SystemUI/res/layout/auth_container_view.xml
@@ -23,7 +23,6 @@
android:id="@+id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/biometric_dialog_dim_color"
android:contentDescription="@string/biometric_dialog_empty_space_description"/>
<View
diff --git a/packages/SystemUI/res/layout/brightness_mirror_container.xml b/packages/SystemUI/res/layout/brightness_mirror_container.xml
index ac90db3e5e19..1bf45aad8906 100644
--- a/packages/SystemUI/res/layout/brightness_mirror_container.xml
+++ b/packages/SystemUI/res/layout/brightness_mirror_container.xml
@@ -23,7 +23,6 @@
android:background="@drawable/brightness_mirror_background"
android:layout_gravity="center_vertical"
android:layout_margin="8dp"
- android:padding="@dimen/rounded_slider_background_padding"
android:gravity="center"
android:visibility="invisible">
diff --git a/packages/SystemUI/res/layout/broadcast_dialog.xml b/packages/SystemUI/res/layout/broadcast_dialog.xml
new file mode 100644
index 000000000000..5ba2afe5b172
--- /dev/null
+++ b/packages/SystemUI/res/layout/broadcast_dialog.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/dialog_bg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/broadcast_dialog_margin"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/dialog_icon"
+ android:layout_width="@dimen/broadcast_dialog_icon_size"
+ android:layout_height="@dimen/broadcast_dialog_icon_size"
+ android:layout_marginTop="@dimen/broadcast_dialog_icon_margin_top"
+ android:layout_marginBottom="@dimen/broadcast_dialog_title_img_margin_top"
+ android:layout_gravity="center"
+ android:src="@drawable/settings_input_antenna"/>
+
+ <TextView
+ style="@style/BroadcastDialogTitleStyle"
+ android:id="@+id/dialog_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:layout_gravity="center"/>
+
+ <TextView
+ style="@style/BroadcastDialogBodyStyle"
+ android:id="@+id/dialog_subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:layout_gravity="center"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/broadcast_dialog_margin"
+ android:layout_marginBottom="@dimen/broadcast_dialog_margin"
+ android:orientation="vertical">
+
+ <Button
+ android:layout_marginBottom="@dimen/broadcast_dialog_btn_margin_bottom"
+ android:id="@+id/switch_broadcast"
+ style="@style/BroadcastDialogButtonStyle"/>
+
+ <Button
+ android:layout_marginBottom="@dimen/broadcast_dialog_btn_margin_bottom"
+ android:id="@+id/change_output"
+ android:text="@string/bt_le_audio_broadcast_dialog_different_output"
+ style="@style/BroadcastDialogButtonStyle"/>
+
+ <Button
+ android:id="@+id/cancel"
+ android:text="@android:string/cancel"
+ style="@style/BroadcastDialogButtonStyle"/>
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 99a5a2e904f6..1a1fc75a41a1 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -115,6 +115,7 @@
android:autoSizeMinTextSize="@dimen/clipboard_overlay_min_font"
android:autoSizeMaxTextSize="@dimen/clipboard_overlay_max_font"
android:textColor="?attr/overlayButtonTextColor"
+ android:textColorLink="?attr/overlayButtonTextColor"
android:background="?androidprv:attr/colorAccentSecondary"
android:layout_width="@dimen/clipboard_preview_size"
android:layout_height="@dimen/clipboard_preview_size"/>
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index ec82ccf2022e..5dc34b9db594 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<androidx.constraintlayout.motion.widget.MotionLayout
+<com.android.systemui.util.NoRemeasureMotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/split_shade_status_bar"
@@ -32,10 +32,37 @@
<androidx.constraintlayout.widget.Guideline
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:id="@+id/center"
- app:layout_constraintGuide_percent="0.5"
+ android:id="@+id/begin_guide"
+ android:orientation="vertical"
+ app:layout_constraintGuide_begin="0dp"/>
+
+ <androidx.constraintlayout.widget.Guideline
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/end_guide"
+ android:orientation="vertical"
+ app:layout_constraintGuide_end="0dp"
+ />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/center_left"
android:orientation="vertical" />
+ <androidx.constraintlayout.widget.Guideline
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/center_right"
+ android:orientation="vertical" />
+
+ <androidx.constraintlayout.widget.Barrier
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/barrier"
+ app:barrierDirection="start"
+ app:constraint_referenced_ids="statusIcons,privacy_container" />
+
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:layout_width="wrap_content"
@@ -44,18 +71,25 @@
android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
android:singleLine="true"
+ android:textDirection="locale"
android:textAppearance="@style/TextAppearance.QS.Status"
+ android:transformPivotX="0sp"
+ android:transformPivotY="20sp"
+ android:scaleX="1"
+ android:scaleY="1"
/>
- <com.android.systemui.statusbar.policy.DateView
+ <com.android.systemui.statusbar.policy.VariableDateView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="start|center_vertical"
android:gravity="center_vertical"
android:singleLine="true"
+ android:textDirection="locale"
android:textAppearance="@style/TextAppearance.QS.Status"
- app:datePattern="@string/abbrev_wday_month_day_no_year_alarm"
+ app:longDatePattern="@string/abbrev_wday_month_day_no_year_alarm"
+ app:shortDatePattern="@string/abbrev_month_day_no_year"
/>
<include
@@ -81,7 +115,7 @@
app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height"
android:paddingEnd="@dimen/signal_cluster_battery_padding"
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
app:layout_constraintStart_toEndOf="@id/carrier_group"
app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
app:layout_constraintTop_toTopOf="@id/clock"
@@ -92,8 +126,9 @@
<com.android.systemui.battery.BatteryMeterView
android:id="@+id/batteryRemainingIcon"
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height"
+ app:layout_constrainedWidth="true"
app:textAppearance="@style/TextAppearance.QS.Status"
app:layout_constraintStart_toEndOf="@id/statusIcons"
app:layout_constraintEnd_toEndOf="parent"
@@ -104,13 +139,18 @@
<FrameLayout
android:id="@+id/privacy_container"
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
android:gravity="center"
- app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintEnd_toEndOf="@id/end_guide"
app:layout_constraintTop_toTopOf="@id/date"
app:layout_constraintBottom_toBottomOf="@id/date"
>
<include layout="@layout/ongoing_privacy_chip"/>
</FrameLayout>
-</androidx.constraintlayout.motion.widget.MotionLayout> \ No newline at end of file
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:id="@+id/space"
+ />
+</com.android.systemui.util.NoRemeasureMotionLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_aqi.xml b/packages/SystemUI/res/layout/dream_overlay_complication_aqi.xml
new file mode 100644
index 000000000000..fcebb8d3f6c6
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_aqi.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/aqi_view"
+ style="@style/clock_subtitle"
+ android:visibility="gone"
+ android:background="@drawable/dream_aqi_badge_bg"
+ android:paddingHorizontal="@dimen/dream_aqi_badge_padding_horizontal"
+ android:paddingVertical="@dimen/dream_aqi_badge_padding_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml
index 3f56bafef134..efbdd1af3644 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml
@@ -20,5 +20,5 @@
style="@style/clock_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:format12Hour="EEE, MMM d"
- android:format24Hour="EEE, MMM d"/>
+ android:format12Hour="@string/dream_date_complication_date_format"
+ android:format24Hour="@string/dream_date_complication_date_format"/>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
index e066d38e446f..7a57293f58bd 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
@@ -22,8 +22,8 @@
android:fontFamily="@font/clock"
android:includeFontPadding="false"
android:textColor="@android:color/white"
- android:format12Hour="h:mm"
- android:format24Hour="kk:mm"
+ android:format12Hour="@string/dream_time_complication_12_hr_time_format"
+ android:format24Hour="@string/dream_time_complication_24_hr_time_format"
android:shadowColor="@color/keyguard_shadow_color"
android:shadowRadius="?attr/shadowRadius"
android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"/>
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
index d0f4903a3421..70a770912c7f 100644
--- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
@@ -70,15 +70,32 @@
android:visibility="gone"
android:contentDescription="@string/dream_overlay_status_bar_wifi_off" />
- <com.android.systemui.dreams.DreamOverlayDotImageView
+ <ImageView
+ android:id="@+id/dream_overlay_mic_off"
+ android:layout_width="@dimen/dream_overlay_grey_chip_width"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:src="@drawable/dream_overlay_mic_off"
+ android:visibility="gone"
+ android:contentDescription="@string/dream_overlay_status_bar_mic_off" />
+
+ <ImageView
+ android:id="@+id/dream_overlay_camera_off"
+ android:layout_width="@dimen/dream_overlay_grey_chip_width"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:src="@drawable/dream_overlay_camera_off"
+ android:visibility="gone"
+ android:contentDescription="@string/dream_overlay_status_bar_camera_off" />
+
+ <ImageView
android:id="@+id/dream_overlay_camera_mic_off"
- android:layout_width="@dimen/dream_overlay_camera_mic_off_indicator_size"
- android:layout_height="@dimen/dream_overlay_camera_mic_off_indicator_size"
- android:layout_gravity="center_vertical"
+ android:layout_width="@dimen/dream_overlay_grey_chip_width"
+ android:layout_height="match_parent"
android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:src="@drawable/dream_overlay_mic_and_camera_off"
android:visibility="gone"
- android:contentDescription="@string/dream_overlay_status_bar_camera_mic_off"
- app:dotColor="@color/dream_overlay_camera_mic_off_dot_color" />
+ android:contentDescription="@string/dream_overlay_status_bar_camera_mic_off" />
</LinearLayout>
</com.android.systemui.dreams.DreamOverlayStatusBarView>
diff --git a/packages/SystemUI/res/layout/hybrid_conversation_notification.xml b/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
index 43b16618d615..a313833e2a66 100644
--- a/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
+++ b/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
@@ -57,7 +57,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
- style="?attr/hybridNotificationTextStyle"
+ android:paddingEnd="4dp"
+ style="@*android:style/Widget.DeviceDefault.Notification.Text"
/>
<TextView
@@ -65,6 +66,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
- style="?attr/hybridNotificationTextStyle"
+ android:paddingEnd="4dp"
+ style="@*android:style/Widget.DeviceDefault.Notification.Text"
/>
</com.android.systemui.statusbar.notification.row.HybridConversationNotificationView>
diff --git a/packages/SystemUI/res/layout/hybrid_notification.xml b/packages/SystemUI/res/layout/hybrid_notification.xml
index e8d77511e53c..9ea7be50adec 100644
--- a/packages/SystemUI/res/layout/hybrid_notification.xml
+++ b/packages/SystemUI/res/layout/hybrid_notification.xml
@@ -20,19 +20,22 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom|start"
- style="?attr/hybridNotificationStyle">
+ android:paddingStart="@*android:dimen/notification_content_margin_start"
+ android:paddingEnd="12dp">
<TextView
android:id="@+id/notification_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
- style="?attr/hybridNotificationTitleStyle"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
+ android:paddingEnd="4dp"
/>
<TextView
android:id="@+id/notification_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
- style="?attr/hybridNotificationTextStyle"
+ android:paddingEnd="4dp"
+ style="@*android:style/Widget.DeviceDefault.Notification.Text"
/>
</com.android.systemui.statusbar.notification.row.HybridNotificationView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 8f8993f3c8d9..12dfa1042dd7 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -59,26 +59,6 @@
</LinearLayout>
- <com.android.systemui.statusbar.KeyguardAffordanceView
- android:id="@+id/camera_button"
- android:layout_height="@dimen/keyguard_affordance_height"
- android:layout_width="@dimen/keyguard_affordance_width"
- android:layout_gravity="bottom|end"
- android:src="@drawable/ic_camera_alt_24dp"
- android:scaleType="center"
- android:contentDescription="@string/accessibility_camera_button"
- android:tint="?attr/wallpaperTextColor" />
-
- <com.android.systemui.statusbar.KeyguardAffordanceView
- android:id="@+id/left_button"
- android:layout_height="@dimen/keyguard_affordance_height"
- android:layout_width="@dimen/keyguard_affordance_width"
- android:layout_gravity="bottom|start"
- android:src="@*android:drawable/ic_phone"
- android:scaleType="center"
- android:contentDescription="@string/accessibility_phone_button"
- android:tint="?attr/wallpaperTextColor" />
-
<ImageView
android:id="@+id/wallet_button"
android:layout_height="@dimen/keyguard_affordance_fixed_height"
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index e47eed9ea04a..d27fa192e741 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -60,9 +60,8 @@
</com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer>
<FrameLayout android:id="@+id/system_icons_container"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_weight="1"
android:layout_marginEnd="@dimen/status_bar_padding_end"
android:gravity="center_vertical|end">
<include layout="@layout/system_icons" />
diff --git a/packages/SystemUI/res/layout/large_screen_shade_header.xml b/packages/SystemUI/res/layout/large_screen_shade_header.xml
index 250eabd42d50..3029a2777fd9 100644
--- a/packages/SystemUI/res/layout/large_screen_shade_header.xml
+++ b/packages/SystemUI/res/layout/large_screen_shade_header.xml
@@ -22,7 +22,7 @@
android:minHeight="@dimen/large_screen_shade_header_min_height"
android:clickable="false"
android:focusable="true"
- android:paddingLeft="@dimen/qs_panel_padding"
+ android:paddingLeft="@dimen/large_screen_shade_header_left_padding"
android:paddingRight="@dimen/qs_panel_padding"
android:visibility="gone"
android:theme="@style/Theme.SystemUI.QuickSettings.Header">
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index 1efb4796b5b7..93c16e4e119d 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -42,12 +42,35 @@
android:layout_height="wrap_content"
android:paddingStart="12dp"
android:orientation="vertical">
- <ImageView
- android:id="@+id/app_source_icon"
- android:layout_width="20dp"
- android:layout_height="20dp"
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
android:gravity="center_vertical"
- android:importantForAccessibility="no"/>
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/app_source_icon"
+ android:layout_width="20dp"
+ android:layout_height="20dp"
+ android:gravity="center_vertical"
+ android:importantForAccessibility="no"/>
+
+ <Space
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
+
+ <ImageView
+ android:id="@+id/broadcast_icon"
+ android:src="@drawable/settings_input_antenna"
+ android:contentDescription="@string/broadcasting_description_is_broadcasting"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:padding="12dp"
+ android:gravity="center_vertical"
+ android:clickable="true"
+ android:focusable="true"
+ android:visibility="gone"/>
+ </LinearLayout>
<TextView
android:id="@+id/header_title"
android:layout_width="wrap_content"
@@ -89,8 +112,7 @@
android:id="@+id/list_result"
android:scrollbars="vertical"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:overScrollMode="never"/>
+ android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index 0e20fa3f46b5..c526d9cc8dd3 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -42,7 +42,6 @@
android:adjustViewBounds="true"
android:clipToOutline="true"
android:background="@drawable/qs_media_outline_album_bg"
- android:foreground="@drawable/qs_media_scrim"
/>
<!-- Guideline for output switcher -->
@@ -151,7 +150,7 @@
<!-- See comment in media_session_collapsed.xml for how these barriers are used -->
<androidx.constraintlayout.widget.Barrier
- android:id="@+id/media_action_barrier"
+ android:id="@+id/media_action_barrier_start"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
@@ -173,6 +172,7 @@
app:layout_constraintStart_toStartOf="parent"
/>
+ <!-- This barrier is used in expanded view to constrain the bottom row of actions -->
<androidx.constraintlayout.widget.Barrier
android:id="@+id/media_action_barrier_top"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/media_ttt_chip.xml b/packages/SystemUI/res/layout/media_ttt_chip.xml
index 4d24140abbf4..d88680669fe0 100644
--- a/packages/SystemUI/res/layout/media_ttt_chip.xml
+++ b/packages/SystemUI/res/layout/media_ttt_chip.xml
@@ -31,6 +31,8 @@
android:padding="@dimen/media_ttt_chip_outer_padding"
android:background="@drawable/media_ttt_chip_background"
android:layout_marginTop="20dp"
+ android:layout_marginStart="@dimen/notification_side_paddings"
+ android:layout_marginEnd="@dimen/notification_side_paddings"
android:clipToPadding="false"
android:gravity="center_vertical"
android:alpha="0.0"
@@ -46,8 +48,9 @@
<TextView
android:id="@+id/text"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:layout_weight="1"
android:textSize="@dimen/media_ttt_text_size"
android:textColor="?android:attr/textColorPrimary"
android:alpha="0.0"
diff --git a/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml b/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml
index 5e8b892018eb..e079fd3c5e8f 100644
--- a/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml
+++ b/packages/SystemUI/res/layout/media_ttt_chip_receiver.xml
@@ -14,20 +14,25 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<!-- TODO(b/203800646): layout_marginTop doesn't seem to work on some large screens. -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/media_ttt_receiver_chip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@drawable/media_ttt_chip_background_receiver"
>
+ <com.android.systemui.media.taptotransfer.receiver.ReceiverChipRippleView
+ android:id="@+id/ripple"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
<com.android.internal.widget.CachingIconView
android:id="@+id/app_icon"
android:layout_width="@dimen/media_ttt_icon_size_receiver"
android:layout_height="@dimen/media_ttt_icon_size_receiver"
- android:layout_gravity="center"
+ android:layout_gravity="center|bottom"
+ android:alpha="0.0"
/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/notification_icon_area.xml b/packages/SystemUI/res/layout/notification_icon_area.xml
index fa696cc1f54c..aadfae8c5aed 100644
--- a/packages/SystemUI/res/layout/notification_icon_area.xml
+++ b/packages/SystemUI/res/layout/notification_icon_area.xml
@@ -14,18 +14,9 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.keyguard.AlphaOptimizedLinearLayout
+<com.android.systemui.statusbar.phone.NotificationIconContainer
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/notification_icon_area_inner"
- android:layout_width="match_parent"
+ android:id="@+id/notificationIcons"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:clipChildren="false">
- <com.android.systemui.statusbar.phone.NotificationIconContainer
- android:id="@+id/notificationIcons"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignParentStart="true"
- android:gravity="center_vertical"
- android:orientation="horizontal"
- android:clipChildren="false"/>
-</com.android.keyguard.AlphaOptimizedLinearLayout> \ No newline at end of file
+ android:clipChildren="false"/> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_activity.xml b/packages/SystemUI/res/layout/people_space_activity.xml
index 7102375a89bf..f45cc7c464d5 100644
--- a/packages/SystemUI/res/layout/people_space_activity.xml
+++ b/packages/SystemUI/res/layout/people_space_activity.xml
@@ -13,103 +13,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<LinearLayout
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/top_level"
+ android:id="@+id/container"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:padding="8dp">
- <TextView
- android:id="@+id/select_conversation_title"
- android:text="@string/select_conversation_title"
- android:gravity="center"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="24sp"/>
-
- <TextView
- android:id="@+id/select_conversation"
- android:text="@string/select_conversation_text"
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="16sp"
- android:paddingVertical="24dp"
- android:paddingHorizontal="48dp"/>
-
- <androidx.core.widget.NestedScrollView
- android:id="@+id/scroll_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout
- android:id="@+id/scroll_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:orientation="vertical">
-
- <LinearLayout
- android:id="@+id/priority"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="35dp">
- <TextView
- android:id="@+id/priority_header"
- android:text="@string/priority_conversations"
- android:layout_width="wrap_content"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
- android:textColor="?androidprv:attr/colorAccentPrimaryVariant"
- android:textSize="14sp"
- android:paddingStart="16dp"
- android:layout_height="wrap_content"/>
-
- <LinearLayout
- android:id="@+id/priority_tiles"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:orientation="vertical"
- android:background="@drawable/rounded_bg_full_large_radius"
- android:clipToOutline="true">
- </LinearLayout>
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/recent"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView
- android:id="@+id/recent_header"
- android:gravity="start"
- android:text="@string/recent_conversations"
- android:layout_width="wrap_content"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
- android:textColor="?androidprv:attr/colorAccentPrimaryVariant"
- android:textSize="14sp"
- android:paddingStart="16dp"
- android:layout_height="wrap_content"/>
-
- <LinearLayout
- android:id="@+id/recent_tiles"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:orientation="vertical"
- android:background="@drawable/rounded_bg_full_large_radius"
- android:clipToOutline="true">
- </LinearLayout>
- </LinearLayout>
- </LinearLayout>
- </androidx.core.widget.NestedScrollView>
-</LinearLayout> \ No newline at end of file
+ android:layout_height="match_parent">
+ <!-- The content of people_space_activity_(no|with)_conversations.xml will be added here at
+ runtime depending on the number of conversations to show. -->
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml b/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml
index 2e9ff07caed9..e929169cfe3d 100644
--- a/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml
+++ b/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml
@@ -16,7 +16,7 @@
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/top_level"
+ android:id="@+id/top_level_no_conversations"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp"
diff --git a/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml b/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml
new file mode 100644
index 000000000000..2384963c44db
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml
@@ -0,0 +1,115 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/top_level_with_conversations"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="8dp">
+ <TextView
+ android:id="@+id/select_conversation_title"
+ android:text="@string/select_conversation_title"
+ android:gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="24sp"/>
+
+ <TextView
+ android:id="@+id/select_conversation"
+ android:text="@string/select_conversation_text"
+ android:gravity="center"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp"
+ android:paddingVertical="24dp"
+ android:paddingHorizontal="48dp"/>
+
+ <androidx.core.widget.NestedScrollView
+ android:id="@+id/scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/scroll_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/priority"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="35dp">
+ <TextView
+ android:id="@+id/priority_header"
+ android:text="@string/priority_conversations"
+ android:layout_width="wrap_content"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
+ android:textColor="?androidprv:attr/colorAccentPrimaryVariant"
+ android:textSize="14sp"
+ android:paddingStart="16dp"
+ android:layout_height="wrap_content"/>
+
+ <LinearLayout
+ android:id="@+id/priority_tiles"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:orientation="vertical"
+ android:background="@drawable/rounded_bg_full_large_radius"
+ android:clipToOutline="true">
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/recent"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/recent_header"
+ android:gravity="start"
+ android:text="@string/recent_conversations"
+ android:layout_width="wrap_content"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
+ android:textColor="?androidprv:attr/colorAccentPrimaryVariant"
+ android:textSize="14sp"
+ android:paddingStart="16dp"
+ android:layout_height="wrap_content"/>
+
+ <LinearLayout
+ android:id="@+id/recent_tiles"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:orientation="vertical"
+ android:background="@drawable/rounded_bg_full_large_radius"
+ android:clipToOutline="true">
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+ </androidx.core.widget.NestedScrollView>
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_tile_view.xml b/packages/SystemUI/res/layout/people_space_tile_view.xml
index 2a2c35dde841..b0599caae6df 100644
--- a/packages/SystemUI/res/layout/people_space_tile_view.xml
+++ b/packages/SystemUI/res/layout/people_space_tile_view.xml
@@ -37,8 +37,8 @@
<ImageView
android:id="@+id/tile_view_person_icon"
- android:layout_width="52dp"
- android:layout_height="52dp" />
+ android:layout_width="@dimen/avatar_size_for_medium"
+ android:layout_height="@dimen/avatar_size_for_medium" />
<LinearLayout
android:orientation="horizontal"
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 77523ec9229f..c124aea01afc 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -28,7 +28,7 @@
android:importantForAccessibility="no"
android:layout_gravity="center_vertical | start">
- <com.android.systemui.util.SafeMarqueeTextView
+ <com.android.systemui.util.DelayableMarqueeTextView
android:id="@+id/tile_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -41,7 +41,7 @@
android:importantForAccessibility="no"
android:textAppearance="@style/TextAppearance.QS.TileLabel"/>
- <com.android.systemui.util.SafeMarqueeTextView
+ <com.android.systemui.util.DelayableMarqueeTextView
android:id="@+id/app_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index deab1ebd6507..e281511140c7 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -47,52 +47,63 @@
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingEnd="@dimen/status_bar_padding_end"
android:paddingTop="@dimen/status_bar_padding_top"
- android:orientation="horizontal"
- >
+ android:orientation="horizontal">
+
+ <!-- Container for the entire start half of the status bar. It will always use the same
+ width, independent of the number of visible children and sub-children. -->
<FrameLayout
+ android:id="@+id/status_bar_start_side_container"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1">
- <include layout="@layout/heads_up_status_bar_layout" />
+ <!-- Container that is wrapped around the views on the start half of the status bar.
+ Its width will change with the number of visible children and sub-children.
+ It is useful when we want to know the visible bounds of the content. -->
+ <FrameLayout
+ android:id="@+id/status_bar_start_side_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipChildren="false">
- <!-- The alpha of the left side is controlled by PhoneStatusBarTransitions, and the
- individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK and
- DISABLE_NOTIFICATION_ICONS, respectively -->
- <LinearLayout
- android:id="@+id/status_bar_left_side"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:clipChildren="false"
- >
- <ViewStub
- android:id="@+id/operator_name"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout="@layout/operator_name" />
+ <include layout="@layout/heads_up_status_bar_layout" />
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- android:singleLine="true"
- android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
- android:gravity="center_vertical|start"
- />
-
- <include layout="@layout/ongoing_call_chip" />
-
- <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
- android:id="@+id/notification_icon_area"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="horizontal"
- android:clipChildren="false"/>
+ <!-- The alpha of the start side is controlled by PhoneStatusBarTransitions, and the
+ individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK
+ and DISABLE_NOTIFICATION_ICONS, respectively -->
+ <LinearLayout
+ android:id="@+id/status_bar_start_side_except_heads_up"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:clipChildren="false">
+ <ViewStub
+ android:id="@+id/operator_name"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout="@layout/operator_name" />
+
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ android:singleLine="true"
+ android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+ android:gravity="center_vertical|start"
+ />
+
+ <include layout="@layout/ongoing_call_chip" />
- </LinearLayout>
+ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+ android:id="@+id/notification_icon_area"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:clipChildren="false"/>
+
+ </LinearLayout>
+ </FrameLayout>
</FrameLayout>
<!-- Space should cover the notch (if it exists) and let other views lay out around it -->
@@ -103,42 +114,57 @@
android:gravity="center_horizontal|center_vertical"
/>
- <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
+ <!-- Container for the entire end half of the status bar. It will always use the same
+ width, independent of the number of visible children and sub-children. -->
+ <FrameLayout
+ android:id="@+id/status_bar_end_side_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
- android:orientation="horizontal"
- android:gravity="center_vertical|end"
- >
-
- <com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer
- android:id="@+id/user_switcher_container"
+ android:clipChildren="false">
+
+ <!-- Container that is wrapped around the views on the end half of the
+ status bar. Its width will change with the number of visible children and
+ sub-children.
+ It is useful when we want know the visible bounds of the content.-->
+ <com.android.keyguard.AlphaOptimizedLinearLayout
+ android:id="@+id/status_bar_end_side_content"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center"
+ android:layout_height="match_parent"
+ android:layout_gravity="end"
android:orientation="horizontal"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:layout_marginEnd="16dp"
- android:background="@drawable/status_bar_user_chip_bg"
- android:visibility="visible" >
- <ImageView android:id="@+id/current_user_avatar"
- android:layout_width="@dimen/multi_user_avatar_keyguard_size"
- android:layout_height="@dimen/multi_user_avatar_keyguard_size"
- android:scaleType="centerInside"
- android:paddingEnd="4dp" />
-
- <TextView android:id="@+id/current_user_name"
+ android:gravity="center_vertical|end"
+ android:clipChildren="false">
+
+ <com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer
+ android:id="@+id/user_switcher_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock"
- />
- </com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer>
-
- <include layout="@layout/system_icons" />
- </com.android.keyguard.AlphaOptimizedLinearLayout>
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:layout_marginEnd="16dp"
+ android:background="@drawable/status_bar_user_chip_bg"
+ android:visibility="visible" >
+ <ImageView android:id="@+id/current_user_avatar"
+ android:layout_width="@dimen/multi_user_avatar_keyguard_size"
+ android:layout_height="@dimen/multi_user_avatar_keyguard_size"
+ android:scaleType="centerInside"
+ android:paddingEnd="4dp" />
+
+ <TextView android:id="@+id/current_user_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ />
+ </com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer>
+
+ <include layout="@layout/system_icons" />
+ </com.android.keyguard.AlphaOptimizedLinearLayout>
+ </FrameLayout>
</LinearLayout>
<ViewStub
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 4d5bf53eb64a..6423a50fc107 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -18,7 +18,7 @@
-->
-<com.android.systemui.statusbar.phone.NotificationPanelView
+<com.android.systemui.shade.NotificationPanelView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/notification_panel"
@@ -67,7 +67,7 @@
</com.android.keyguard.LockIconView>
- <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
+ <com.android.systemui.shade.NotificationsQuickSettingsContainer
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="@integer/notification_panel_layout_gravity"
@@ -150,11 +150,11 @@
android:text="@string/tap_again"
android:visibility="gone"
/>
- </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
+ </com.android.systemui.shade.NotificationsQuickSettingsContainer>
<FrameLayout
android:id="@+id/preview_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
-</com.android.systemui.statusbar.phone.NotificationPanelView>
+</com.android.systemui.shade.NotificationPanelView>
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 60860bad9c64..86f8ce26ccab 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -18,7 +18,7 @@
-->
<!-- This is the notification shade window. -->
-<com.android.systemui.statusbar.phone.NotificationShadeWindowView
+<com.android.systemui.shade.NotificationShadeWindowView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
@@ -114,4 +114,4 @@
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
-</com.android.systemui.statusbar.phone.NotificationShadeWindowView>
+</com.android.systemui.shade.NotificationShadeWindowView>
diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
index 0f2d372f7158..c2c79cb0f34b 100644
--- a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
+++ b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<androidx.constraintlayout.widget.ConstraintLayout
+<com.android.systemui.user.UserSwitcherRootView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
@@ -68,4 +68,4 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_min="48dp" />
-</androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.systemui.user.UserSwitcherRootView>
diff --git a/packages/SystemUI/res/layout/wireless_charging_layout.xml b/packages/SystemUI/res/layout/wireless_charging_layout.xml
index 1312b413f106..887e3e715369 100644
--- a/packages/SystemUI/res/layout/wireless_charging_layout.xml
+++ b/packages/SystemUI/res/layout/wireless_charging_layout.xml
@@ -22,7 +22,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <com.android.systemui.statusbar.charging.ChargingRippleView
+ <com.android.systemui.ripple.RippleView
android:id="@+id/wireless_charging_ripple"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 3eb73e09688b..3d27dfd95760 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Toestel is gesluit"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skandeer tans gesig"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Stuur"</string>
- <string name="phone_label" msgid="5715229948920451352">"maak foon oop"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"maak stembystand oop"</string>
- <string name="camera_label" msgid="8253821920931143699">"maak kamera oop"</string>
<string name="cancel" msgid="1089011503403416730">"Kanselleer"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Bevestig"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Probeer weer"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensors Af is aktief"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Verwyder alle kennisgewings."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">nog <xliff:g id="NUMBER_1">%s</xliff:g> kennisgewings binne.</item>
- <item quantity="one">nog <xliff:g id="NUMBER_0">%s</xliff:g> kennisgewing binne.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{nog # kennisgewing binne.}other{nog # kennisgewings binne.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Skerm is in landskapsoriëntasie gesluit."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Skerm is in portretoriëntasie gesluit."</string>
<string name="dessert_case" msgid="9104973640704357717">"Nageregkas"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Outo-draai"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Outodraai skerm"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ligging"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Sluimerskerm"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameratoegang"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofoontoegang"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Beskikbaar"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Warmkol"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Skakel tans aan …"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Databespaarder is aan"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d toestelle</item>
- <item quantity="one">%d toestel</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# toestel}other{# toestelle}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Flitslig"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera in gebruik"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobiele data"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Tik weer"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swiep op om oop te maak"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Druk die onsluitikoon om oop te maak"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Ontsluit met gesig. Druk die ontsluitikoon om oop te maak."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Ontsluit met gesig. Druk om oop te maak."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Gesig is herken. Druk om oop te maak."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Wiil jy jou sessie voortsit?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Begin van voor af"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, gaan voort"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Gasmodus"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Jy is in gasmodus"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"As ’n nuwe gebruiker bygevoeg word, sal gasmodus verlaat word en sal alle programme en data in die huidige gastesessie uitgevee word."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Gebruikerlimiet is bereik"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Jy kan tot <xliff:g id="COUNT">%d</xliff:g> gebruikers byvoeg.</item>
- <item quantity="one">Net een gebruiker kan geskep word.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Jy kan net een gebruiker skep.}other{Jy kan tot # gebruikers byvoeg.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Verwyder gebruiker?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Alle programme en data van hierdie gebruiker sal uitgevee word."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Verwyder"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Herinner my"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Ontdoen"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Sluimer vir <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d uur</item>
- <item quantity="one">%d uur</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minute</item>
- <item quantity="one">%d minuut</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# uur}=2{# uur}other{# uur}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuut}other{# minute}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Batterybespaarder"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Knoppie <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Opletberigte"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Battery"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Skermkiekies"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Algemene boodskappe"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Kitsprogramme"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Opstelling"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Berging"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Wenke"</string>
<string name="instant_apps" msgid="8337185853050247304">"Kitsprogramme"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"wissel"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Toestelkontroles"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Kies program om kontroles by te voeg"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> kontroles bygevoeg.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> kontrole bygevoeg.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontrole bygevoeg.}other{# kontroles bygevoeg.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Verwyder"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"As gunsteling gemerk"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"As gunsteling gemerk; posisie <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Voeg teël by"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Moenie teël byvoeg nie"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kies gebruiker"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> programme is aktief</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> program is aktief</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app is aktief}other{# apps is aktief}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nuwe inligting"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktiewe programme"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Hierdie programme is aktief en werk, selfs wanneer jy hulle nie gebruik nie. Dit verbeter hul funksies, maar beïnvloed dalk ook batterylewe."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Wekker gestel"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera en mikrofoon is af"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# kennisgewing}other{# kennisgewings}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Uitsaai"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hou op om <xliff:g id="APP_NAME">%1$s</xliff:g> uit te saai?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"As jy <xliff:g id="SWITCHAPP">%1$s</xliff:g> uitsaai of die uitvoer verander, sal jou huidige uitsending stop"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Saai <xliff:g id="SWITCHAPP">%1$s</xliff:g> uit"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Verander uitvoer"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Onbekend"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EE. d MMM."</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml
index 93d26e8f1f83..e60f23329212 100644
--- a/packages/SystemUI/res/values-af/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Af"</item>
<item msgid="460891964396502657">"Aan"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Onbeskikbaar"</item>
+ <item msgid="8014986104355098744">"Af"</item>
+ <item msgid="5966994759929723339">"Aan"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 7fa8604dc2e5..78873e529f13 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"መሣሪያ ተቆልፏል"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"የቅኝት ፊት"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ላክ"</string>
- <string name="phone_label" msgid="5715229948920451352">"ስልክ ክፈት"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"የድምጽ ረዳትን ክፈት"</string>
- <string name="camera_label" msgid="8253821920931143699">"ካሜራ ክፈት"</string>
<string name="cancel" msgid="1089011503403416730">"ይቅር"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"አረጋግጥ"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"እንደገና ይሞክሩ"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"ዳሳሾች ጠፍተዋል ገቢር"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"ሁሉንም ማሳወቂያዎች አጽዳ"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">ከውስጥ ተጨማሪ <xliff:g id="NUMBER_1">%s</xliff:g> ማሳወቂያዎች።</item>
- <item quantity="other">ከውስጥ ተጨማሪ <xliff:g id="NUMBER_1">%s</xliff:g> ማሳወቂያዎች።</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# ተጨማሪ ማሳወቂያ ከውስጥ አለ።}one{# ተጨማሪ ማሳወቂያ ከውስጥ አለ።}other{# ተጨማሪ ማሳወቂያዎች ከውስጥ አሉ።}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"ማያ ገጽ በወርድ ገፅ አቀማመጥ ተቆልፏል።"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"ማያ ገጽ በቁም ገፅ አቀማመጥ ተቆልፏል።"</string>
<string name="dessert_case" msgid="9104973640704357717">"የማወራረጃ ምግቦች መያዣ"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"በራስ ሰር አሽከርክር"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ማያ ገጽን በራስ-አሽከርክር"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"አካባቢ"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"የማያ ገጽ ማቆያ"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"የካሜራ መዳረሻ"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"የማይክሮፎን መዳረሻ"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ይገኛል"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"መገናኛ ነጥብ"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"በማብራት ላይ..."</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"ውሂብ ቆጣቢ በርቷል"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d መሣሪያዎች</item>
- <item quantity="other">%d መሣሪያዎች</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# መሣሪያ}one{# መሣሪያዎች}other{# መሣሪያዎች}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"የባትሪ ብርሃን"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"ካሜራ ስራ ላይ ነው"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"የተንቀሳቃሽ ስልክ ውሂብ"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"እንደገና መታ ያድርጉ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ለመክፈት በጣት ወደ ላይ ጠረግ ያድርጉ"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"ለመክፈት የመክፈቻ አዶውን ይጫኑ"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"በመልክ ተከፍቷል። ለመክፈት የመክፈቻ አዶውን ይጫኑ።"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"በመልክ ተከፍቷል። ለመክፈት ይጫኑ።"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"መልክ ተለይቶ ታውቋል። ለመክፈት ይጫኑ።"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"ክፍለ-ጊዜዎን መቀጠል ይፈልጋሉ?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"እንደገና ጀምር"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"አዎ፣ ቀጥል"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"የእንግዳ ሁነታ"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"በእንግዳ ሁኔታ ውስጥ ነዎት"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"አዲስ ተጠቃሚ ማከል ከእንግዳ ሁነታ ወጥቶ ሁሉንም መተግበሪያዎች እና ውሂብ አሁን ካለው የእንግዳ ክፍለ ጊዜ ይሰርዛል።"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"የተጠቃሚ ገደብ ላይ ተደርሷል"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one"><xliff:g id="COUNT">%d</xliff:g> ተጠቃሚዎች ብቻ ናቸው ሊፈጠሩ የሚችሉት።</item>
- <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> ተጠቃሚዎች ብቻ ናቸው ሊፈጠሩ የሚችሉት።</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{ሊፈጠር የሚችለው አንድ ተጠቃሚ ብቻ ነው።}one{እስከ # ተጠቃሚ ድረስ ማከል ይችላሉ።}other{እስከ # ተጠቃሚዎች ድረስ ማከል ይችላሉ።}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"ተጠቃሚ ይወገድ?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"ሁሉም የዚህ ተጠቃሚ መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"አስወግድ"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"አስታውሰኝ"</string>
<string name="snooze_undo" msgid="2738844148845992103">"ቀልብስ"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"ለ<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> አሸልቧል"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one"> %d ሰዓቶች</item>
- <item quantity="other"> %d ሰዓቶች</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one"> %d ደቂቃዎች</item>
- <item quantity="other"> %d ደቂቃዎች</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ሰዓት}=2{# ሰዓታት}one{# ሰዓት}other{# ሰዓታት}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# ደቂቃ}one{# ደቂቃ}other{# ደቂቃዎች}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"ባትሪ ቆጣቢ"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"አዝራር <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"መነሻ"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"ማንቂያዎች"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"ባትሪ"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"ቅጽበታዊ ገጽ እይታዎች"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"አጠቃላይ መልዕክቶች"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"ቅጽበታዊ መተግበሪያዎች"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"ውቅረት"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"ማከማቻ"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"ፍንጮች"</string>
<string name="instant_apps" msgid="8337185853050247304">"የቅጽበት መተግበሪያዎች"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ቀያይር"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"የመሣሪያ መቆጣጠሪያዎች"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"መቆጣጠሪያዎችን ለማከል መተግበሪያ ይምረጡ"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> ቁጥጥሮች ታክለዋል።</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> ቁጥጥሮች ታክለዋል።</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# ቁጥጥር ታክሏል።}one{# ቁጥጥር ታክሏል።}other{# ቁጥጥሮች ታክለዋል።}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"ተወግዷል"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"ተወዳጅ የተደረገ"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"ተወዳጅ ተደርጓል፣ አቋም <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ሰቅ አክል"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ሰቅ አታክል"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ተጠቃሚን ይምረጡ"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> መተግበሪያዎች ንቁ ናቸው</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> መተግበሪያዎች ንቁ ናቸው</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# መተግበሪያ ገቢር ሆኗል}one{# መተግበሪያ ገቢር ሆኗል}other{# መተግበሪያዎች ገቢር ሆነዋል}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"አዲስ መረጃ"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ገቢር መተግበሪያዎች"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"እነዚህ መተግበሪያዎች ንቁ እና እያሄዱ ናቸው፣ እርስዎ እየተጠቀሙባቸው ባይሆንም እንኳ። ይህ ተግባራዊነታቸውን ቢያሻሽልም በባትሪ ዕድሜያቸው ላይ ተጽዕኖ ሊኖረው ይችላል።"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ማንቂያ ተቀናብሯል"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ካሜራ እና ማይክሮፎን ጠፍተዋል"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ማሳወቂያ}one{# ማሳወቂያዎች}other{# ማሳወቂያዎች}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"በማሰራጨት ላይ"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g>ን ማሰራጨት ይቁም?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g>ን ካሰራጩ ወይም ውፅዓትን ከቀየሩ የአሁኑ ስርጭትዎ ይቆማል"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ያሰራጩ"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ውፅዓትን ይቀይሩ"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ያልታወቀ"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE፣ MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml
index 12be1ae2fd22..bbf2d2385f05 100644
--- a/packages/SystemUI/res/values-am/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"አጥፋ"</item>
<item msgid="460891964396502657">"አብራ"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"አይገኝም"</item>
+ <item msgid="8014986104355098744">"ጠፍቷል"</item>
+ <item msgid="5966994759929723339">"በርቷል"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index fdb892712046..98ab7a4b8836 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"الجهاز مُقفل."</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"مسح الوجه"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"إرسال"</string>
- <string name="phone_label" msgid="5715229948920451352">"فتح الهاتف"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"فتح المساعد الصوتي"</string>
- <string name="camera_label" msgid="8253821920931143699">"فتح الكاميرا"</string>
<string name="cancel" msgid="1089011503403416730">"إلغاء"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"تأكيد"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"إعادة المحاولة"</string>
@@ -205,14 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"أجهزة الاستشعار غير مفعّلة"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"محو جميع الإشعارات."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="zero"><xliff:g id="NUMBER_1">%s</xliff:g> إشعار آخر بداخل المجموعة.</item>
- <item quantity="two">إشعاران (<xliff:g id="NUMBER_1">%s</xliff:g>) آخران بداخل المجموعة.</item>
- <item quantity="few"><xliff:g id="NUMBER_1">%s</xliff:g> إشعارات أخرى بداخل المجموعة.</item>
- <item quantity="many"><xliff:g id="NUMBER_1">%s</xliff:g> إشعارًا آخر بداخل المجموعة.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> إشعار آخر بداخل المجموعة.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> إشعار آخر بداخل المجموعة.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{إشعار واحد آخر بداخل المجموعة.}zero{# إشعار آخر بداخل المجموعة.}two{إشعاران آخران بداخل المجموعة.}few{# إشعارات أخرى بداخل المجموعة.}many{# إشعارًا آخر بداخل المجموعة.}other{# إشعار آخر بداخل المجموعة.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"تم قفل الشاشة في الاتجاه الأفقي."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"تم قفل الشاشة في الاتجاه العمودي."</string>
<string name="dessert_case" msgid="9104973640704357717">"حالة الحلويات"</string>
@@ -230,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"التدوير التلقائي"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"التدوير التلقائي للشاشة"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"الموقع الجغرافي"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"شاشة الاستراحة"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"الوصول إلى الكاميرا"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"الوصول إلى الميكروفون"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"متاح"</string>
@@ -259,14 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"نقطة اتصال"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"جارٍ التفعيل…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"توفير البيانات مفعّل"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="zero">‏%d جهاز</item>
- <item quantity="two">‏جهازان (%d)</item>
- <item quantity="few">‏%d أجهزة</item>
- <item quantity="many">‏%d جهازًا</item>
- <item quantity="other">‏%d جهاز</item>
- <item quantity="one">‏جهاز واحد (%d)</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{جهاز واحد}zero{# جهاز}two{جهازان}few{# أجهزة}many{# جهازًا}other{# جهاز}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"الفلاش"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"الكاميرا قيد الاستخدام"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"بيانات الجوّال"</string>
@@ -323,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"انقر مرة أخرى"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"يمكنك الفتح بالتمرير سريعًا لأعلى."</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"اضغط على رمز فتح القفل لفتح قفل الشاشة."</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"تم فتح القفل بالتعرّف على وجهك. لفتح الجهاز، اضغط على رمز فتح القفل."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"تم فتح قفل جهازك عند تقريبه من وجهك. اضغط لفتح الجهاز."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"تم التعرّف على الوجه. اضغط لفتح الجهاز."</string>
@@ -359,15 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"هل تريد متابعة جلستك؟"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"البدء من جديد"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"نعم، متابعة"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"وضع الضيف"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"أنت تستخدِم وضع الضيف."</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ستؤدي إضافة مُستخدِم جديد إلى الخروج من وضع الضيف وحذف كل التطبيقات والبيانات من جلسة الضيف الحالية."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"تم الوصول إلى أقصى عدد للمستخدمين"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="zero">يمكنك إضافة ما يصل إلى <xliff:g id="COUNT">%d</xliff:g> مستخدم.</item>
- <item quantity="two">يمكنك إضافة ما يصل إلى مستخدمينِ (<xliff:g id="COUNT">%d</xliff:g>).</item>
- <item quantity="few">يمكنك إضافة ما يصل إلى <xliff:g id="COUNT">%d</xliff:g> مستخدمين.</item>
- <item quantity="many">يمكنك إضافة ما يصل إلى <xliff:g id="COUNT">%d</xliff:g> مستخدمًا.</item>
- <item quantity="other">يمكنك إضافة ما يصل إلى <xliff:g id="COUNT">%d</xliff:g> مستخدم.</item>
- <item quantity="one">يمكن إنشاء مستخدم واحد فقط.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{يمكن إنشاء مستخدم واحد فقط.}zero{يمكنك إضافة ما يصل إلى # مستخدم}two{يمكنك إضافة ما يصل إلى مستخدمَين}few{يمكنك إضافة ما يصل إلى # مستخدمِين}many{يمكنك إضافة ما يصل إلى # مستخدمًا}other{يمكنك إضافة ما يصل إلى # مستخدم}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"هل تريد إزالة المستخدم؟"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"سيتم حذف جميع تطبيقات وبيانات هذا المستخدم."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"إزالة"</string>
@@ -553,22 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"تذكيري"</string>
<string name="snooze_undo" msgid="2738844148845992103">"تراجع"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"تم تأجيل الإشعار لمدة <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="zero">‏%d ساعة</item>
- <item quantity="two">‏ساعتان (%d)</item>
- <item quantity="few">‏%d ساعات</item>
- <item quantity="many">‏%d ساعة</item>
- <item quantity="other">‏%d ساعة</item>
- <item quantity="one">ساعة واحدة</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="zero">‏%d دقيقة</item>
- <item quantity="two">‏دقيقتان (%d)</item>
- <item quantity="few">‏%d دقائق</item>
- <item quantity="many">‏%d دقيقة</item>
- <item quantity="other">‏%d دقيقة</item>
- <item quantity="one">دقيقة واحدة</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{ساعة واحدة}=2{ساعتان}zero{# ساعة}few{# ساعات}many{# ساعةً}other{# ساعة}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{دقيقة واحدة}zero{# دقيقة}two{دقيقتان}few{# دقائق}many{# دقيقةً}other{# دقيقة}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"توفير شحن البطارية"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"الزر <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -717,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"التنبيهات"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"البطارية"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"لقطات الشاشة"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"رسائل عامة"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"التطبيقات الفورية"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"عملية الإعداد"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"مساحة التخزين"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"تلميحات"</string>
<string name="instant_apps" msgid="8337185853050247304">"التطبيقات الفورية"</string>
@@ -791,14 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"إيقاف/تفعيل"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"التحكم بالجهاز"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"اختيار تطبيق لإضافة عناصر التحكّم"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="zero">تمت إضافة <xliff:g id="NUMBER_1">%s</xliff:g> عنصر تحكّم.</item>
- <item quantity="two">تمت إضافة عنصرَي تحكّم (<xliff:g id="NUMBER_1">%s</xliff:g>).</item>
- <item quantity="few">تمت إضافة <xliff:g id="NUMBER_1">%s</xliff:g> عناصر تحكّم.</item>
- <item quantity="many">تمت إضافة <xliff:g id="NUMBER_1">%s</xliff:g> عنصر تحكّم.</item>
- <item quantity="other">تمت إضافة <xliff:g id="NUMBER_1">%s</xliff:g> عنصر تحكّم.</item>
- <item quantity="one">تمت إضافة عنصر تحكّم واحد (<xliff:g id="NUMBER_0">%s</xliff:g>).</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{تمت إضافة عنصر تحكّم واحد.}zero{تمت إضافة # عنصر تحكّم.}two{تمت إضافة عنصرَي تحكّم.}few{تمت إضافة # عناصر تحكّم.}many{تمت إضافة # عنصر تحكّم.}other{تمت إضافة # عنصر تحكّم.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"تمت الإزالة"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"تمت الإضافة إلى المفضّلة"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"تمت الإضافة إلى المفضّلة، الموضع <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -955,14 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"إضافة المربّع"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"عدم إضافة المربّع"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"اختيار المستخدم"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="zero"><xliff:g id="COUNT_1">%s</xliff:g> تطبيق نشط</item>
- <item quantity="two">تطبيقَان نشطَان (<xliff:g id="COUNT_1">%s</xliff:g>)</item>
- <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> تطبيقات نشطة</item>
- <item quantity="many"><xliff:g id="COUNT_1">%s</xliff:g> تطبيقًا نشطًا</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> تطبيق نشط</item>
- <item quantity="one">تطبيق واحد نشط (<xliff:g id="COUNT_0">%s</xliff:g>)</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{تطبيق واحد نشط}zero{# تطبيق نشط}two{تطبيقَان نشطَان}few{# تطبيقات نشطة}many{# تطبيقًا نشطًا}other{# تطبيق نشط}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"معلومات جديدة"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"التطبيقات النشطة"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"هذه التطبيقات نشطة وقيد التشغيل، حتى في حال عدم استخدامها. يؤدي ذلك إلى تحسين وظائفها، ولكنه قد يؤثّر أيضًا على عمر البطارية."</string>
@@ -991,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"تم ضبط المنبه."</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"الكاميرا والميكروفون غير مفعّلين."</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{إشعار واحد}zero{# إشعار}two{إشعاران}few{# إشعارات}many{# إشعارًا}other{# إشعار}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"البث"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"هل تريد إيقاف بث تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>؟"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"إذا أجريت بث تطبيق <xliff:g id="SWITCHAPP">%1$s</xliff:g> أو غيَّرت جهاز الإخراج، سيتوقَف البث الحالي."</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"بث تطبيق <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"تغيير جهاز الإخراج"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"غير معروف"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"‏EEE،‏ d‏ MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
index b4fb760f5e65..44b58f964ce9 100644
--- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"غير مفعّل"</item>
<item msgid="460891964396502657">"مفعّل"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"غير متوفّرة"</item>
+ <item msgid="8014986104355098744">"غير مفعّلة"</item>
+ <item msgid="5966994759929723339">"مفعَّلة"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 73166e9606ab..564fd1b11ff1 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"ডিভাইচটো লক হৈ আছে"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"চেহেৰা স্কেন কৰি থকা হৈছে"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"পঠিয়াওক"</string>
- <string name="phone_label" msgid="5715229948920451352">"ফ\'ন খোলক"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"কণ্ঠধ্বনিৰে সহায় খোলক"</string>
- <string name="camera_label" msgid="8253821920931143699">"কেমেৰা খোলক"</string>
<string name="cancel" msgid="1089011503403416730">"বাতিল কৰক"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"নিশ্চিত কৰক"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"আকৌ চেষ্টা কৰক"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"ছেন্সৰ অফ সক্ৰিয় কৰা আছে"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"আটাইবোৰ জাননী মচক৷"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one"> ভিতৰত আৰু <xliff:g id="NUMBER_1">%s</xliff:g>টা জাননী আছে।</item>
- <item quantity="other"> ভিতৰত আৰু <xliff:g id="NUMBER_1">%s</xliff:g>টা জাননী আছে।</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{ভিতৰত আৰু # টা জাননী আছে।}one{ভিতৰত আৰু # টা জাননী আছে।}other{ভিতৰত আৰু # টা জাননী আছে।}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"স্ক্ৰীন লেণ্ডস্কে\'প দিশত লক কৰা হ’ল।"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"স্ক্ৰীন প\'ৰ্ট্ৰেইট দিশত লক কৰা হ’ল।"</string>
<string name="dessert_case" msgid="9104973640704357717">"মিষ্টান্ন ভাণ্ডাৰ"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"স্বয়ং-ঘূৰ্ণন"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"স্বয়ং-ঘূৰ্ণন স্ক্ৰীন"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"অৱস্থান"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"স্ক্ৰীন ছেভাৰ"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"কেমেৰাৰ এক্সেছ"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"মাইকৰ এক্সেছ"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"উপলব্ধ"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"হটস্পট"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"অন কৰি থকা হৈছে…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"ডেটা সঞ্চয়কাৰী অন হৈ আছে"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d ডিভাইচ</item>
- <item quantity="other">%d ডিভাইচ</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# টা ডিভাইচ}one{# টা ডিভাইচ}other{# টা ডিভাইচ}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"ফ্লাশ্বলাইট"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"কেমেৰা ব্যৱহাৰ হৈ আছে"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"ম’বাইল ডেটা"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"পুনৰ টিপক"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"খুলিবলৈ ওপৰলৈ ছোৱাইপ কৰক"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"খুলিবলৈ আনলক কৰক চিহ্নটোত টিপক"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"মুখাৱয়বৰ জৰিয়তে আনলক কৰা হৈছে। খুলিবলৈ আনলক কৰক চিহ্নটোত টিপক।"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"মুখাৱয়বৰ জৰিয়তে আনলক কৰা হৈছে। খুলিবলৈ টিপক।"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"মুখাৱয়ব চিনাক্ত কৰা হৈছে। খুলিবলৈ টিপক।"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"আপুনি আপোনাৰ ছেশ্বন অব্যাহত ৰাখিব বিচাৰেনে?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আকৌ আৰম্ভ কৰক"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"হয়, অব্যাহত ৰাখক"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"অতিথি ম’ড"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"আপুনি অতিথি ম’ডত আছে"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"এগৰাকী নতুন ব্যৱহাৰকাৰীক যোগ দিয়াটোৱে অতিথি ম’ডৰ পৰা বাহিৰ কৰিব আৰু বৰ্তমানৰ অতিথিৰ ছেশ্বনটোৰ পৰা আটাইবোৰ এপ্ আৰু ডেটা মচিব।"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"অধিকতম ব্যৱহাৰকাৰী সৃষ্টি কৰা হ’ল"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">আপুনি <xliff:g id="COUNT">%d</xliff:g> জনলৈকে ব্যৱহাৰকাৰী যোগ কৰিব পাৰে।</item>
- <item quantity="other">আপুনি <xliff:g id="COUNT">%d</xliff:g> জনলৈকে ব্যৱহাৰকাৰী যোগ কৰিব পাৰে।</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{মাত্ৰ এগৰাকী ব্যৱহাৰকাৰী সৃষ্টি কৰিব পাৰি।}one{আপুনি # গৰাকী পৰ্যন্ত ব্যৱহাৰকাৰী যোগ দিব পাৰে।}other{আপুনি # গৰাকী পৰ্যন্ত ব্যৱহাৰকাৰী যোগ দিব পাৰে।}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"ব্যৱহাৰকাৰীক আঁতৰাবনে?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"এই ব্যৱহাৰকাৰীৰ আটাইবোৰ এপ্ আৰু ডেটা মচা হ\'ব।"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"আঁতৰাওক"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"মোক মনত পেলাই দিব"</string>
<string name="snooze_undo" msgid="2738844148845992103">"আনডু কৰক"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g>ৰ বাবে স্নুজ কৰক"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one"> %d ঘণ্টা</item>
- <item quantity="other"> %d ঘণ্টা</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one"> %d মিনিট</item>
- <item quantity="other"> %d মিনিট</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ঘণ্টা}=2{# ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# মিনিট}one{# মিনিট}other{# মিনিট}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"বেটাৰী সঞ্চয়কাৰী"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> বুটাম"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"হ\'ম"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"সতৰ্কবার্তাসমূহ"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"বেটাৰী"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"স্ক্ৰীণশ্বটসমূহ"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"সাধাৰণ বার্তাসমূহ"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"তাৎক্ষণিক এপ্"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"ছেটআপ"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"ষ্ট\'ৰেজ"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"ইংগিতবোৰ"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ট’গল কৰক"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ডিভাইচৰ নিয়ন্ত্ৰণসমূহ"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"নিয়ন্ত্ৰণসমূহ যোগ কৰিবলৈ এপ্‌ বাছনি কৰক"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> টা নিয়ন্ত্ৰণ যোগ কৰা হ’ল।</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> টা নিয়ন্ত্ৰণ যোগ কৰা হ’ল।</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# টা নিয়ন্ত্ৰণ যোগ দিয়া হৈছে।}one{# টা নিয়ন্ত্ৰণ যোগ দিয়া হৈছে।}other{# টা নিয়ন্ত্ৰণ যোগ দিয়া হৈছে।}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"আঁতৰোৱা হ’ল"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"প্ৰিয় হিচাপে চিহ্নিত কৰা হ’ল"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"প্ৰিয় হিচাপে চিহ্নিত কৰা হ’ল, স্থান <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"টাইল যোগ দিয়ক"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"টাইল যোগ নিদিব"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ব্যৱহাৰকাৰী বাছনি কৰক"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> টা এপ্‌ সক্ৰিয় হৈ আছে</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> টা এপ্‌ সক্ৰিয় হৈ আছে</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# টা এপ্‌ সক্ৰিয় হৈ আছে}one{# টা এপ্‌ সক্ৰিয় হৈ আছে}other{# টা এপ্‌ সক্ৰিয় হৈ আছে}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"নতুন তথ্য"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"সক্ৰিয় এপ্‌"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"এই এপ্‌সমূহ সক্ৰিয় আৰু আনকি আপুনি এইসমূহ ব্যৱহাৰ নকৰাৰ সময়তো চলি থাকে। ই সেইসমূহৰ কাৰ্য্যক্ষমতা উন্নত কৰে, কিন্তু ই বেটাৰীৰ জীৱনকালতো প্ৰভাৱ পেলাব পাৰে।"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"এলাৰ্ম ছেট কৰা হ’ল"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"কেমেৰা আৰু মাইক অফ হৈ আছে"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# টা জাননী}one{# টা জাননী}other{# টা জাননী}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"সম্প্ৰচাৰণ"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ সম্প্ৰচাৰ কৰা বন্ধ কৰিবনে?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"যদি আপুনি <xliff:g id="SWITCHAPP">%1$s</xliff:g>ৰ সম্প্ৰচাৰ কৰে অথবা আউটপুট সলনি কৰে, তেন্তে, আপোনাৰ বৰ্তমানৰ সম্প্ৰচাৰ বন্ধ হৈ যাব"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> সম্প্ৰচাৰ কৰক"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"আউটপুট সলনি কৰক"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"অজ্ঞাত"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml
index 7767cfc6943a..3145341cabe4 100644
--- a/packages/SystemUI/res/values-as/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"অফ"</item>
<item msgid="460891964396502657">"অন"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"উপলব্ধ নহয়"</item>
+ <item msgid="8014986104355098744">"অফ আছে"</item>
+ <item msgid="5966994759929723339">"অন আছে"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 5bc28954230e..4ce451c86ed1 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Cihaz kilidlənib"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Üzün skan edilməsi"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Göndərin"</string>
- <string name="phone_label" msgid="5715229948920451352">"telefonu açın"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"səs yardımçısını açın"</string>
- <string name="camera_label" msgid="8253821920931143699">"kemaranı açın"</string>
<string name="cancel" msgid="1089011503403416730">"Ləğv edin"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Təsdiq"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Yenidən cəhd edin"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"\"Deaktiv sensorlar\" aktivdir"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Bütün bildirişləri sil."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">Daxilində daha <xliff:g id="NUMBER_1">%s</xliff:g> bildiriş.</item>
- <item quantity="one">Daxilində daha <xliff:g id="NUMBER_0">%s</xliff:g> bildiriş.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Daha # bildiriş daxildir.}other{Daha # bildiriş daxildir.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Ekran landşaft orientasiyasında kilidlənib."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Ekran portret orientasiyasında kilidlənib."</string>
<string name="dessert_case" msgid="9104973640704357717">"Desert Qabı"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Avtodönüş"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranın avtomatik dönməsi"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Məkan"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Ekran qoruyucu"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameraya giriş"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofona giriş"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Əlçatan"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Aktiv edilir..."</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Trafikə qənaət edilir"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d cihaz</item>
- <item quantity="one">%d cihaz</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# cihaz}other{# cihaz}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Fənər"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera istifadə olunur"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobil data"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Yenidən toxunun"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Açmaq üçün yuxarı sürüşdürün"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"\"Kilidi aç\" ikonasına basıb açın"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Üzlə kilidi açılıb. \"Kilidi aç\" ikonasına basıb açın."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Üz ilə kiliddən çıxarılıb. Açmaq üçün basın."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Üz tanınıb. Açmaq üçün basın."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Sessiya davam etsin?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Yenidən başlayın"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Bəli, davam edin"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Qonaq rejimi"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Qonaq rejimindəsiniz"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Yeni istifadəçi əlavə edildikdə qonaq rejimindən çıxılacaq və cari qonaq sessiyasındakı bütün tətbiqlər və data silinəcək."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"İstifadəçi limitinə çatmısınız"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Maksimum <xliff:g id="COUNT">%d</xliff:g> istifadəçi əlavə edə bilərsiniz.</item>
- <item quantity="one">Yalnız bir istifadəçi yaradıla bilər.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Yalnız bir istifadəçi yaradıla bilər.}other{Maksimum # istifadəçi əlavə edə bilərsiniz.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"İstifadəçi silinsin?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Bu istifadəçinin bütün tətbiqləri və datası silinəcək."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Silin"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Mənə xatırladın"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Geri qaytarın"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> üçün təxirə salınıb"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other"> %d saat</item>
- <item quantity="one">%d saat</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d dəqiqə</item>
- <item quantity="one">%d dəqiqə</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# saat}=2{# saat}other{# saat}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# dəqiqə}other{# dəqiqə}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Enerjiyə qənaət"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Düymə <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Əsas səhifə"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Xəbərdarlıqlar"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Batareya"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Skrinşotlar"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Ümumi Mesajlar"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Ani Tətbiqlər"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Ayarlama"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Yaddaş"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Məsləhətlər"</string>
<string name="instant_apps" msgid="8337185853050247304">"Ani Tətbiqlər"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"keçirin"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Cihaz kontrolları"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Kontrol əlavə etmək üçün tətbiq seçin"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> nizamlayıcı əlavə edilib.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> nizamlayıcı əlavə edilib.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# nizamlayıcı əlavə edilib.}other{# nizamlayıcı əlavə edilib.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Silinib"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Sevimlilərə əlavə edilib"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Sevimlilərə əlavə edilib, sıra: <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Mozaik əlavə edin"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Mozaik əlavə etməyin"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"İstifadəçi seçin"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> tətbiq aktivdir</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> tətbiq aktivdir</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# tətbiq aktivdir}other{# tətbiq aktivdir}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Yeni məlumat"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktiv tətbiqlər"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"İstifadə etmədiyiniz zaman belə bu tətbiqlər aktiv olur və işləyir. Bu, onların funksionallığını yaxşılaşdırır, lakin bu, batareyanın ömrünə də təsir edə bilər."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Siqnal ayarlanıb"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera və mikrofon deaktivdir"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# bildiriş}other{# bildiriş}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Yayım"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqinin yayımlanması dayandırılsın?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> tətbiqini yayımlasanız və ya nəticəni dəyişsəniz, cari yayımınız dayandırılacaq"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> tətbiqini yayımlayın"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Nəticəni dəyişdirin"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Naməlum"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"HHH, AAA g"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"s:dd"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"ss:dd"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml
index 0311794de3cd..fb745b251bc9 100644
--- a/packages/SystemUI/res/values-az/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Deaktiv"</item>
<item msgid="460891964396502657">"Aktiv"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Əlçatmazdır"</item>
+ <item msgid="8014986104355098744">"Deaktiv"</item>
+ <item msgid="5966994759929723339">"Aktiv"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 9437fb137cfc..e87cc5effbcf 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Uređaj je zaključan"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skeniranje lica"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Pošalji"</string>
- <string name="phone_label" msgid="5715229948920451352">"otvori telefon"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"otvori glasovnu pomoć"</string>
- <string name="camera_label" msgid="8253821920931143699">"otvori kameru"</string>
<string name="cancel" msgid="1089011503403416730">"Otkaži"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potvrdi"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Probaj ponovo"</string>
@@ -205,11 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Senzori su isključeni"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Obriši sva obaveštenja."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"i još <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">Još <xliff:g id="NUMBER_1">%s</xliff:g> obaveštenje u grupi.</item>
- <item quantity="few">Još <xliff:g id="NUMBER_1">%s</xliff:g> obaveštenja u grupi.</item>
- <item quantity="other">Još <xliff:g id="NUMBER_1">%s</xliff:g> obaveštenja u grupi.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Unutra je još # obaveštenje.}one{Unutra je još # obaveštenje.}few{Unutra su još # obaveštenja.}other{Unutra je još # obaveštenja.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Ekran je zaključan u horizontalnom položaju."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Ekran je zaključan u vertikalnom položaju."</string>
<string name="dessert_case" msgid="9104973640704357717">"Vitrina sa poslasticama"</string>
@@ -227,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatska rotacija"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko rotiranje ekrana"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Čuvar ekrana"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Pristup kameri"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Pristup mikrofonu"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Dostupno"</string>
@@ -256,11 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Uključuje se..."</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Ušteda podataka je uključena"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d uređaj</item>
- <item quantity="few">%d uređaja</item>
- <item quantity="other">%d uređaja</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# uređaj}one{# uređaj}few{# uređaja}other{# uređaja}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lampa"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Koristi se kamera"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobilni podaci"</string>
@@ -317,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Dodirnite ponovo"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Prevucite nagore da biste otvorili"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Pritisnite ikonu otključavanja da biste otvorili."</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Otključano je licem. Pritisnite ikonu otključavanja da biste otvorili."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Otključano je licem. Pritisnite da biste otvorili."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Lice je prepoznato. Pritisnite da biste otvorili."</string>
@@ -353,12 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li da nastavite sesiju?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni iz početka"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, nastavi"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Režim gosta"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Koristite režim gosta"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Dodavanjem novog korisnika izaći ćete iz režima gosta i izbrisaćete sve aplikacije i podatke iz aktuelne sesije gosta."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Dostignut maksimalni broj korisnika"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Možete da dodate najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
- <item quantity="few">Možete da dodate najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
- <item quantity="other">Možete da dodate najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Možete da napravite samo jednog korisnika.}one{Možete da dodate najviše # korisnika.}few{Možete da dodate najviše # korisnika.}other{Možete da dodate najviše # korisnika.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Želite li da uklonite korisnika?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Sve aplikacije i podaci ovog korisnika će biti izbrisani."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Ukloni"</string>
@@ -544,16 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Podseti me"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Opozovi"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Odloženo je za <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d sat</item>
- <item quantity="few">%d sata</item>
- <item quantity="other">%d sati</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d minut</item>
- <item quantity="few">%d minuta</item>
- <item quantity="other">%d minuta</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# sat}=2{# sata}one{# sat}few{# sata}other{# sati}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minut}one{# minut}few{# minuta}other{# minuta}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Ušteda baterije"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Dugme <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Taster Početna"</string>
@@ -702,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Obaveštenja"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Baterija"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Snimci ekrana"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Opšte poruke"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant aplikacije"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Podešavanje"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Memorijski prostor"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Saveti"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant aplikacije"</string>
@@ -776,11 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"uključite/isključite"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Odaberite aplikaciju za dodavanje kontrola"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> kontrola je dodata.</item>
- <item quantity="few"><xliff:g id="NUMBER_1">%s</xliff:g> kontrole su dodate.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> kontrola je dodato.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontrola je dodata.}one{# kontrola je dodata.}few{# kontrole su dodate.}other{# kontrola je dodato.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Uklonjeno"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Označeno je kao omiljeno"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Označeno je kao omiljeno, <xliff:g id="NUMBER">%d</xliff:g>. pozicija"</string>
@@ -937,11 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj pločicu"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne dodaj pločicu"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Izaberite korisnika"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> aplikacija je aktivna</item>
- <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> aplikacije su aktivne</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplikacija je aktivno</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikacija je aktivna}one{# aplikacija je aktivna}few{# aplikacije su aktivne}other{# aplikacija je aktivno}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nove informacije"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivne aplikacije"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ove aplikacije su aktivne i rade čak i kada ih ne koristite. To im poboljšava funkcionalnost, ali može da utiče i na trajanje baterije."</string>
@@ -970,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm je podešen"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera i mikrofon su isključeni"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# obaveštenje}one{# obaveštenje}few{# obaveštenja}other{# obaveštenja}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitovanje"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Želite da zaustavite emitovanje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ako emitujete aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g> ili promenite izlaz, aktuelno emitovanje će se zaustaviti"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitujte aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Promenite izlaz"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nepoznato"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"DDD, d. MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"s:min"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"č:min"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
index a057c48bbec9..b69b06419281 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Isključeno"</item>
<item msgid="460891964396502657">"Uključeno"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Nedostupno"</item>
+ <item msgid="8014986104355098744">"Isključeno"</item>
+ <item msgid="5966994759929723339">"Uključeno"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index d1596937c7bf..5779cb3ace7e 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Прылада заблакіравана"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Сканіраванне твару"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Адправіць"</string>
- <string name="phone_label" msgid="5715229948920451352">"адкрыць тэлефон"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"адкрыць галасавую дапамогу"</string>
- <string name="camera_label" msgid="8253821920931143699">"адкрыць камеру"</string>
<string name="cancel" msgid="1089011503403416730">"Скасаваць"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Пацвердзіць"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Паўтарыць спробу"</string>
@@ -205,12 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Датчыкі выключаны"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Выдалiць усе апавяшчэннi."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">Яшчэ <xliff:g id="NUMBER_1">%s</xliff:g> апавяшчэнне ўнутры.</item>
- <item quantity="few">Яшчэ <xliff:g id="NUMBER_1">%s</xliff:g> апавяшчэнні ўнутры.</item>
- <item quantity="many">Яшчэ <xliff:g id="NUMBER_1">%s</xliff:g> апавяшчэнняў унутры.</item>
- <item quantity="other">Яшчэ <xliff:g id="NUMBER_1">%s</xliff:g> апавяшчэння ўнутры.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Яшчэ # апавяшчэнне ўнутры.}one{Яшчэ # апавяшчэнне ўнутры.}few{Яшчэ # апавяшчэнні ўнутры.}many{Яшчэ # апавяшчэнняў унутры.}other{Яшчэ # апавяшчэння ўнутры.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Экран заблакiраваны ў альбомнай арыентацыі."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Экран заблакiраваны ў партрэтнай арыентацыі."</string>
<string name="dessert_case" msgid="9104973640704357717">"Вітрына з дэсертамі"</string>
@@ -228,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Аўтапаварот"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Аўтаматычны паварот экрана"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Месцазнаходжанне"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Экранная застаўка"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Доступ да камеры"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Доступ да мікрафона"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Доступ дазволены"</string>
@@ -257,12 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Хот-спот"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Уключэнне…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Эканомія трафіка ўкл"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d прылада</item>
- <item quantity="few">%d прылады</item>
- <item quantity="many">%d прылад</item>
- <item quantity="other">%d прылады</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# прылада}one{# прылада}few{# прылады}many{# прылад}other{# прылады}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Ліхтарык"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Камера выкарыстоўваецца"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Мабільная перадача даных"</string>
@@ -319,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Націсніце яшчэ раз"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Каб адкрыць, прагарніце ўверх"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Каб адкрыць, націсніце значок разблакіроўкі"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Твар распазнаны. Для адкрыцця націсніце значок разблакіроўкі"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Разблакіравана распазнаваннем твару. Націсніце, каб адкрыць."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Твар распазнаны. Націсніце, каб адкрыць."</string>
@@ -355,13 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Хочаце працягнуць сеанс?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Пачаць зноў"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Так, працягнуць"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Гасцявы рэжым"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Вы знаходзіцеся ў гасцявым рэжыме"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Дадаванне новага карыстальніка закрые гасцявы рэжым. Будуць выдалены ўсе праграмы і даныя бягучага гасцявога сеанса."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Дасягнуты ліміт карыстальнікаў"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Можна дадаць <xliff:g id="COUNT">%d</xliff:g> карыстальніка.</item>
- <item quantity="few">Можна дадаць <xliff:g id="COUNT">%d</xliff:g> карыстальнікаў.</item>
- <item quantity="many">Можна дадаць <xliff:g id="COUNT">%d</xliff:g> карыстальнікаў.</item>
- <item quantity="other">Можна дадаць <xliff:g id="COUNT">%d</xliff:g> карыстальніка.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Можна стварыць толькі аднаго карыстальніка.}one{Вы можаце дадаць толькі # карыстальніка.}few{Вы можаце дадаць толькі # карыстальнікаў.}many{Вы можаце дадаць толькі # карыстальнікаў.}other{Вы можаце дадаць толькі # карыстальніка.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Выдаліць карыстальніка?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Усе праграмы і даныя гэтага карыстальніка будуць выдалены."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Выдаліць"</string>
@@ -547,18 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Нагадаць"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Адрабіць"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Адкладзена на <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d гадзіна</item>
- <item quantity="few">%d гадзіны</item>
- <item quantity="many">%d гадзін</item>
- <item quantity="other">%d гадзіны</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d хвіліна</item>
- <item quantity="few">%d хвіліны</item>
- <item quantity="many">%d хвілін</item>
- <item quantity="other">%d хвіліны</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# гадз}=2{# гадзіны}one{# гадзіна}few{# гадзіны}many{# гадзін}other{# гадзіны}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# хвіліна}one{# хвіліна}few{# хвіліны}many{# хвілін}other{# хвіліны}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Рэжым энергазберажэння"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -707,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Абвесткі"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Акумулятар"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Здымкі экрана"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Агульныя паведамленні"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Імгненныя праграмы"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Наладжванне"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Захоўванне"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Падказкі"</string>
<string name="instant_apps" msgid="8337185853050247304">"Імгненныя праграмы"</string>
@@ -781,12 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"уключыць/выключыць"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Элементы кіравання прыладай"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Выберыце праграму для дадавання элементаў кіравання"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one">Дададзены <xliff:g id="NUMBER_1">%s</xliff:g> элемент кіравання.</item>
- <item quantity="few">Дададзена <xliff:g id="NUMBER_1">%s</xliff:g> элементы кіравання.</item>
- <item quantity="many">Дададзена <xliff:g id="NUMBER_1">%s</xliff:g> элементаў кіравання.</item>
- <item quantity="other">Дададзена <xliff:g id="NUMBER_1">%s</xliff:g> элемента кіравання.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Дададзены # элемент кіравання.}one{Дададзена # элемента кіравання.}few{Дададзена # элементы кіравання.}many{Дададзена # элементаў кіравання.}other{Дададзена # элемента кіравання.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Выдалена"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Дададзена ў абранае"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Дададзена ў абранае, пазіцыя <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -943,12 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Дадаць плітку"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не дадаваць плітку"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Выбар карыстальніка"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> праграма актыўная</item>
- <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> праграмы актыўныя</item>
- <item quantity="many"><xliff:g id="COUNT_1">%s</xliff:g> праграм актыўныя</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> праграмы актыўныя</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# праграма актыўная}one{# праграма актыўныя}few{# праграмы актыўныя}many{# праграм актыўныя}other{# праграмы актыўныя}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Новая інфармацыя"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Актыўныя праграмы"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Гэтыя праграмы працуюць, нават калі вы іх не выкарыстоўваеце. У выніку павышаецца іх функцыянальнасць, аднак можа знізіцца час працы ад акумулятара."</string>
@@ -977,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Будзільнік зададзены"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камера і мікрафон выключаны"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# апавяшчэнне}one{# апавяшчэнне}few{# апавяшчэнні}many{# апавяшчэнняў}other{# апавяшчэння}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Перадача даных"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Спыніць трансляцыю праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Пры пераключэнні на праграму \"<xliff:g id="SWITCHAPP">%1$s</xliff:g>\" ці змяненні вываду бягучая трансляцыя спыняецца"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Трансляцыя праграмы \"<xliff:g id="SWITCHAPP">%1$s</xliff:g>\""</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Змяненне вываду"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Невядома"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml
index d2588113f37b..8fb2da26edc2 100644
--- a/packages/SystemUI/res/values-be/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Выключана"</item>
<item msgid="460891964396502657">"Уключана"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Недаступна"</item>
+ <item msgid="8014986104355098744">"Выключана"</item>
+ <item msgid="5966994759929723339">"Уключана"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 29f361f8b8b1..1784939b8f2d 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Устройството е заключено"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Извършва се сканиране на лице"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Изпращане"</string>
- <string name="phone_label" msgid="5715229948920451352">"отваряне на телефона"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"отваряне на гласовата помощ"</string>
- <string name="camera_label" msgid="8253821920931143699">"отваряне на камерата"</string>
<string name="cancel" msgid="1089011503403416730">"Отказ"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Потвърждаване"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Нов опит"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Сензорите са изключени"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Изчистване на всички известия."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">Съдържа още <xliff:g id="NUMBER_1">%s</xliff:g> известия.</item>
- <item quantity="one">Съдържа още <xliff:g id="NUMBER_0">%s</xliff:g> известие.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Съдържа още # известие.}other{Съдържа още # известия.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Екранът е заключен в хоризонтална ориентация."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Екранът е заключен във вертикална ориентация."</string>
<string name="dessert_case" msgid="9104973640704357717">"Витрина с десерти"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматична ориентация"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматично завъртане на екрана"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Местоположение"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Скрийнсейвър"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Достъп до камерата"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Достъп до микрофона"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Налице"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Точка за достъп"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Включва се..."</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Икономия на данни е вкл."</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d устройства</item>
- <item quantity="one">%d устройство</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# устройство}other{# устройства}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Фенерче"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Камерата се използва"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Мобилни данни"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Докоснете отново"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Прекарайте пръст нагоре, за да отключите"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Натиснете иконата за отключване, за да отворите"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Отключено с лице. Натиснете иконата за отключване, за да отворите."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Отключено с лице. Натиснете за отваряне."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Лицето бе разпознато. Натиснете за отваряне."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Искате ли да продължите сесията си?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Започване отначало"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Да, продължавам"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Режим на гост"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Вие сте в режим на гост"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"С добавянето на нов потребител ще излезете от режима на гост и ще изтриете всички приложения и данни от текущата сесия като гост."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Достигнахте огранич. за потребители"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Можете да добавите до <xliff:g id="COUNT">%d</xliff:g> потребители.</item>
- <item quantity="one">Може да бъде създаден само един потребител.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Може да бъде създаден само един потребител.}other{Можете да добавите до # потребители.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Да се премахне ли потребителят?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Всички приложения и данни на този потребител ще бъдат изтрити."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Премахване"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Напомняне"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Отмяна"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Отложено за <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d часа</item>
- <item quantity="one">%d час</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d минути</item>
- <item quantity="one">%d минута</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# час}=2{# часа}other{# часа}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# минута}other{# минути}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Режим за запазване на батерията"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Бутон „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Начало"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Сигнали"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Батерия"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Екранни снимки"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Общи съобщения"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Мигновени приложения"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Настройване"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Хранилище"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Съвети"</string>
<string name="instant_apps" msgid="8337185853050247304">"Мигновени приложения"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"превключване"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Контроли за устройството"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Изберете приложение, за да добавите контроли"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">Добавени са <xliff:g id="NUMBER_1">%s</xliff:g> контроли.</item>
- <item quantity="one">Добавена е <xliff:g id="NUMBER_0">%s</xliff:g> контрола.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Добавена е # контрола.}other{Добавени са # контроли.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Премахнато"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Означено като любимо"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Означено като любимо – позиция <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Добавяне на панел"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Отмяна на добавянето"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Избор на потребител"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> приложения са активни</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> приложение е активно</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# приложение е активно}other{# приложения са активни}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Нова информация"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активни приложения"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Дори когато не ги използвате, тези приложения са активни и работят. Това подобрява функционалността им, но може да окаже и влияние върху живота на батерията."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Будилникът е зададен"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камерата и микрофонът са изключени"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# известие}other{# известия}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Излъчване"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Да се спре ли предаването на <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ако предавате <xliff:g id="SWITCHAPP">%1$s</xliff:g> или промените изхода, текущото ви предаване ще бъде прекратено"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Предаване на <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Промяна на изхода"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Неизвестно"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
index da5c325aaa1c..b85133b6bd43 100644
--- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Изкл."</item>
<item msgid="460891964396502657">"Вкл."</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Не е налице"</item>
+ <item msgid="8014986104355098744">"Изкл."</item>
+ <item msgid="5966994759929723339">"Вкл."</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index cc54762f8736..dfc8a2b714ad 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"ডিভাইস লক করা আছে"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ফেস স্ক্যান করা হচ্ছে"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"পাঠান"</string>
- <string name="phone_label" msgid="5715229948920451352">"ফোন খুলুন"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"ভয়েস সহায়তা খুলুন"</string>
- <string name="camera_label" msgid="8253821920931143699">"ক্যামেরা খুলুন"</string>
<string name="cancel" msgid="1089011503403416730">"বাতিল করুন"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"কনফার্ম করুন"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"আবার চেষ্টা করুন"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"সেন্সর অফ অ্যাক্টিভ"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"সমস্ত বিজ্ঞপ্তি সাফ করুন৷"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>টি"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">ভিতরে আরও <xliff:g id="NUMBER_1">%s</xliff:g>টি বিজ্ঞপ্তি আছে।</item>
- <item quantity="other">ভিতরে আরও <xliff:g id="NUMBER_1">%s</xliff:g>টি বিজ্ঞপ্তি আছে।</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{ভিতরে আরও #টি বিজ্ঞপ্তি আছে।}one{ভিতরে আরও #টি বিজ্ঞপ্তি আছে।}other{ভিতরে আরও #টি বিজ্ঞপ্তি আছে।}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"ল্যান্ডস্কেপ সজ্জাতে স্ক্রিন লক করা আছে৷"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"পোর্ট্রেট অবস্থায় স্ক্রিন লক করা আছে৷"</string>
<string name="dessert_case" msgid="9104973640704357717">"ডেজার্ট কেস"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"নিজে থেকে ঘুরবে"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"অটো-রোটেট স্ক্রিন"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"লোকেশন"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"স্ক্রিন সেভার"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"ক্যামেরা অ্যাক্সেস"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"মাইক্রোফোন অ্যাক্সেস"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"উপলভ্য"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"হটস্পট"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"চালু করা হচ্ছে…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"ডেটা সেভার চালু আছে"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%dটি ডিভাইস</item>
- <item quantity="other">%dটি ডিভাইস</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{#টি ডিভাইস}one{#টি ডিভাইস}other{#টি ডিভাইস}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"ফ্ল্যাশলাইট"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"ক্যামেরা ব্যবহার করা হচ্ছে"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"মোবাইল ডেটা"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"আবার ট্যাপ করুন"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"খোলার জন্য উপরে সোয়াইপ করুন"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"খোলার জন্য আনলক আইকন প্রেস করুন"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ফেসের সাহায্যে আনলক করা হয়েছে। খোলার জন্য আনলক আইকন প্রেস করুন।"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ফেসের সাহায্যে আনলক করা হয়েছে। খোলার জন্য প্রেস করুন।"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ফেস শনাক্ত করা হয়েছে। খোলার জন্য প্রেস করুন।"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"আপনি কি আপনার সেশনটি চালিয়ে যেতে চান?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আবার শুরু করুন"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"হ্যাঁ, চালিয়ে যান"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"অতিথি মোড"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"আপনি \'অতিথি মোড\' ব্যবহার করছেন"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"নতুন ব্যবহারকারী যোগ করার মাধ্যমে \'অতিথি মোড\' ছেড়ে বেরিয়ে আসতে পারবেন এবং বর্তমান অতিথি সেশন থেকে সব অ্যাপ ও ডেটা মুছে যাবে।"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"আর কোনও প্রোফাইল যোগ করা যাবে না"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">আপনি <xliff:g id="COUNT">%d</xliff:g> জন পর্যন্ত ব্যবহারকারীর প্রোফাইল যোগ করতে পারেন।</item>
- <item quantity="other">আপনি <xliff:g id="COUNT">%d</xliff:g> জন পর্যন্ত ব্যবহারকারীর প্রোফাইল যোগ করতে পারেন।</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{শুধুমাত্র একজন ব্যবহারকারী ডিভাইস ব্যবহার করতে পারবেন।}one{আপনি # জন পর্যন্ত ব্যবহারকারীকে যোগ করতে পারবেন।}other{আপনি # জন পর্যন্ত ব্যবহারকারীকে যোগ করতে পারবেন।}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"ব্যবহারকারী সরাবেন?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"এই ব্যবহারকারীর সমস্ত অ্যাপ্লিকেশান ও ডেটা মুছে ফেলা হবে।"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"সরান"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"আমাকে মনে করিয়ে দিও"</string>
<string name="snooze_undo" msgid="2738844148845992103">"আগের অবস্থায় ফিরুন"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> পরে আবার মনে করানো হবে"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d ঘণ্টা</item>
- <item quantity="other">%d ঘণ্টা</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d মিনিট</item>
- <item quantity="other">%d মিনিট</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ঘণ্টা}=2{# ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# মিনিট}one{# মিনিট}other{# মিনিট}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"ব্যাটারি সেভার"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> বোতাম"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"হোম"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"সতর্কতা"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"ব্যাটারি"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"স্ক্রীনশটস"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"সাধারণ বার্তাগুলি"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"সেট-আপ"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"স্টোরেজ"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"হিন্ট"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"টগল করুন"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ডিভাইস কন্ট্রোল"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"কন্ট্রোল যোগ করতে অ্যাপ বেছে নিন"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g>টি কন্ট্রোল যোগ করা হয়েছে।</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g>টি কন্ট্রোল যোগ করা হয়েছে।</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{#টি কন্ট্রোল যোগ করা হয়েছে।}one{#টি কন্ট্রোল যোগ করা হয়েছে।}other{#টি কন্ট্রোল যোগ করা হয়েছে।}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"সরানো হয়েছে"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"পছন্দসই হিসেবে চিহ্নিত করেছেন"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"পছন্দসই হিসেবে চিহ্নিত করেছেন, অবস্থান <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"টাইল যোগ করুন"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"টাইল যোগ করবেন না"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ব্যবহারকারী বেছে নিন"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g>টি অ্যাপ চালু আছে</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g>টি অ্যাপ চালু আছে</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{#টি অ্যাপ চালু আছে}one{#টি অ্যাপ চালু আছে}other{#টি অ্যাপ চালু আছে}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"নতুন তথ্য"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"অ্যাক্টিভ অ্যাপ"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"আপনি এমনকি ব্যবহার না করলেও, এইসব অ্যাপ অ্যাক্টিভ থাকে ও চলে। এগুলির কার্যকারিতা এটি উন্নত করে, তবে ব্যাটারির আয়ুর উপর প্রভাব ফেলতেও পারে।"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"অ্যালার্ম সেট করা হয়েছে"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ক্যামেরা ও মাইক্রোফোন বন্ধ আছে"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{#টি বিজ্ঞপ্তি}one{#টি বিজ্ঞপ্তি}other{#টি বিজ্ঞপ্তি}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ব্রডকাস্ট করা হচ্ছে"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> সম্প্রচার বন্ধ করবেন?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"আপনি <xliff:g id="SWITCHAPP">%1$s</xliff:g> সম্প্রচার করলে বা আউটপুট পরিবর্তন করলে, আপনার বর্তমান সম্প্রচার বন্ধ হয়ে যাবে"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> সম্প্রচার করুন"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"আউটপুট পরিবর্তন করুন"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"অজানা"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
index 784d974e2eba..d70afc0f7f4f 100644
--- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"বন্ধ আছে"</item>
<item msgid="460891964396502657">"চালু আছে"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"অনুপলভ্য"</item>
+ <item msgid="8014986104355098744">"বন্ধ আছে"</item>
+ <item msgid="5966994759929723339">"চালু আছে"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index b6f6f3928985..1ae04fedbf03 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Uređaj je zaključan"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skeniranje lica"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Pošalji"</string>
- <string name="phone_label" msgid="5715229948920451352">"otvori telefon"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"otvori glasovnu pomoć"</string>
- <string name="camera_label" msgid="8253821920931143699">"otvori kameru"</string>
<string name="cancel" msgid="1089011503403416730">"Otkaži"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potvrdite"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Pokušaj ponovo"</string>
@@ -205,11 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Senzori su isključeni"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Uklanjanje svih obavještenja."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">Još <xliff:g id="NUMBER_1">%s</xliff:g> obavještenje unutra.</item>
- <item quantity="few">Još <xliff:g id="NUMBER_1">%s</xliff:g> obavještenja unutra.</item>
- <item quantity="other">Još <xliff:g id="NUMBER_1">%s</xliff:g> obavještenja unutra.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Još # obavještenje unutra.}one{Još # obavještenje unutra.}few{Još # obavještenja unutra.}other{Još # obavještenja unutra.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Ekran je zaključan u vodoravnom prikazu."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Ekran je zaključan u uspravnom prikazu."</string>
<string name="dessert_case" msgid="9104973640704357717">"Slika sa desertima"</string>
@@ -227,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatsko rotiranje"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko rotiranje ekrana"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Čuvar ekrana"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Pristup kameri"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Pristup mikrofonu"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Dostupno"</string>
@@ -256,11 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Pristupna tačka"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Uključivanje…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Ušteda podataka uklj."</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d uređaj</item>
- <item quantity="few">%d uređaja</item>
- <item quantity="other">%d uređaja</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# uređaj}one{# uređaj}few{# uređaja}other{# uređaja}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Svjetiljka"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera u upotrebi"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Prijenos podataka na mobilnoj mreži"</string>
@@ -317,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Ponovo dodirnite"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Prevucite da otvorite"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Pritisnite ikonu za otključavanje da otvorite."</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Otključano licem. Pritisnite ikonu za otklj. da otvorite."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Otključano licem. Pritisnite da otvorite."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Lice prepoznato. Pritisnite da otvorite."</string>
@@ -353,12 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, nastavi"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Način rada za gosta"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Nalazite se u načinu rada za gosta"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Dodavanjem novog korisnika napustit ćete način rada za gosta i izbrisati sve aplikacije i podatke iz trenutne sesije gosta."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Dostignut limit za broj korisnika"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
- <item quantity="few">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
- <item quantity="other">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Moguće je kreirati samo jednog korisnika.}one{Možete dodati najviše # korisnika}few{Možete dodati najviše # korisnika}other{Možete dodati najviše # korisnika}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Zaista želite ukloniti korisnika?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Sve aplikacije i podaci ovog korisnika bit će izbrisani."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Ukloni"</string>
@@ -544,16 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Podsjeti me"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Poništi"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Odgođeno za <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d sat</item>
- <item quantity="few">%d sata</item>
- <item quantity="other">%d sati</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d minuta</item>
- <item quantity="few">%d minute</item>
- <item quantity="other">%d minuta</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# h}=2{# h}one{# h}few{# h}other{# h}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# min}one{# min}few{# min}other{# min}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Ušteda baterije"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Dugme <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Tipka za početak"</string>
@@ -702,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Obavještenja"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Baterija"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Snimci ekrana"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Opće poruke"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant aplikacije"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Postavljanje"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Pohrana"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Savjeti"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant aplikacije"</string>
@@ -776,11 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"aktiviranje/deaktiviranje"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Odaberite aplikaciju da dodate kontrole"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one">Dodana je <xliff:g id="NUMBER_1">%s</xliff:g> kontrola.</item>
- <item quantity="few">Dodane su <xliff:g id="NUMBER_1">%s</xliff:g> kontrole.</item>
- <item quantity="other">Dodano je <xliff:g id="NUMBER_1">%s</xliff:g> kontrola.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Dodana je # kontrola.}one{Dodana je # kontrola.}few{Dodane su # kontrole.}other{Dodano je # kontrola.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Uklonjeno"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Dodano u omiljeno"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Dodano u omiljeno, pozicija <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -937,11 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj karticu"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nemoj dodati karticu"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Odaberite korisnika"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> aplikacija je aktivna</item>
- <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> aplikacije su aktivne</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplikacija je aktivno</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikacija je aktivna}one{# aplikacija je aktivna}few{# aplikacije su aktivne}other{# aplikacija je aktivno}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nove informacije"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivne aplikacije"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ove aplikacije su aktivne i pokrenute, čak i kada ih ne koristite. Ovim se poboljšava njihova funkcionalnost, ali može uticati i na vijek trajanja baterije."</string>
@@ -970,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm je postavljen"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera i mikrofon su isključeni"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# obavještenje}one{# obavještenje}few{# obavještenja}other{# obavještenja}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitiranje"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zaustaviti emitiranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ako emitirate aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g> ili promijenite izlaz, trenutno emitiranje će se zaustaviti"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitiraj aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Promijeni izlaz"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nepoznato"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"DDD, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
index a057c48bbec9..b69b06419281 100644
--- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Isključeno"</item>
<item msgid="460891964396502657">"Uključeno"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Nedostupno"</item>
+ <item msgid="8014986104355098744">"Isključeno"</item>
+ <item msgid="5966994759929723339">"Uključeno"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 497c2012e5e0..0899d882edec 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositiu bloquejat"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"S\'està escanejant la cara"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Envia"</string>
- <string name="phone_label" msgid="5715229948920451352">"obre el telèfon"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"obre l\'assistència per veu"</string>
- <string name="camera_label" msgid="8253821920931143699">"obre la càmera"</string>
<string name="cancel" msgid="1089011503403416730">"Cancel·la"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirma"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Torna-ho a provar"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensors desactivats"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Esborra totes les notificacions."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> notificacions més a l\'interior.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> notificació més a l\'interior.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# notificació més a l\'interior.}other{# notificacions més a l\'interior.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"La pantalla està bloquejada en orientació horitzontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"La pantalla està bloquejada en orientació vertical."</string>
<string name="dessert_case" msgid="9104973640704357717">"Capsa de postres"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Gira automàticament"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Gira la pantalla automàticament"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicació"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Estalvi de pantalla"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Accés a la càmera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Accés al micròfon"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponible"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Punt d\'accés Wi-Fi"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"S\'està activant…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Economitzador activat"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d dispositius</item>
- <item quantity="one">%d dispositiu</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# dispositiu}other{# dispositius}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Llanterna"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Càmera en ús"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Dades mòbils"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Torna a tocar"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Llisca cap amunt per obrir"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Prem la icona de desbloqueig per obrir"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"S\'ha desbloquejat amb la cara. Prem la icona per obrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"S\'ha desbloquejat amb la cara. Prem per obrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"S\'ha reconegut la cara. Prem per obrir."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Vols continuar amb la sessió?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Torna a començar"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sí, continua"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Mode de convidat"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Estàs en mode de convidat"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"En afegir un usuari nou, se sortirà del mode de convidat i se suprimiran totes les aplicacions i dades de la sessió de convidat actual."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"S\'ha assolit el límit d\'usuaris"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Pots afegir fins a <xliff:g id="COUNT">%d</xliff:g> usuaris.</item>
- <item quantity="one">Només es pot crear un usuari.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Només es pot crear 1 usuari.}other{Pots afegir fins a # usuaris.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Vols suprimir l\'usuari?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Totes les aplicacions i les dades d\'aquest usuari se suprimiran."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Suprimeix"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Recorda-m\'ho"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Desfés"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"S\'ha posposat <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d hores</item>
- <item quantity="one">%d hora</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minuts</item>
- <item quantity="one">%d minut</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hora}=2{# hores}other{# hores}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minut}other{# minuts}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Estalvi de bateria"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Botó <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Inici"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alertes"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Captures de pantalla"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Missatges generals"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Aplicacions instantànies"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Configuració"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Emmagatzematge"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Suggeriments"</string>
<string name="instant_apps" msgid="8337185853050247304">"Aplicacions instantànies"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"commuta"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controls de dispositius"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Selecciona l\'aplicació per afegir controls"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">S\'han afegit <xliff:g id="NUMBER_1">%s</xliff:g> controls.</item>
- <item quantity="one">S\'ha afegit <xliff:g id="NUMBER_0">%s</xliff:g> control.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{S\'ha afegit # control.}other{S\'han afegit # controls.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Suprimit"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Afegit als preferits"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Afegit als preferits, posició <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Afegeix la icona"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"No afegeixis la icona"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecciona un usuari"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplicacions estan actives</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplicació està activa</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplicació està activa}other{# aplicacions estan actives}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Informació nova"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplicacions actives"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Aquestes aplicacions estan actives i executant-se, fins i tot quan no les utilitzes. Això en millora la funcionalitat, però també pot afectar la durada de la bateria."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarma definida"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Càmera i micròfon desactivats"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificació}other{# notificacions}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"S\'està emetent"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vols deixar d\'emetre <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si emets <xliff:g id="SWITCHAPP">%1$s</xliff:g> o canvies la sortida, l\'emissió actual s\'aturarà"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emet <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Canvia la sortida"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconeguda"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"hh:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
index 2d13e568d5f3..aaf19c7c0cc6 100644
--- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Desactivat"</item>
<item msgid="460891964396502657">"Activat"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"No disponible"</item>
+ <item msgid="8014986104355098744">"Desactivat"</item>
+ <item msgid="5966994759929723339">"Activat"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index e9f3a64d9fae..db653160c64d 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Zařízení uzamčeno"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skenování obličeje"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Odeslat"</string>
- <string name="phone_label" msgid="5715229948920451352">"otevřít telefon"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"otevřít hlasovou asistenci"</string>
- <string name="camera_label" msgid="8253821920931143699">"spustit fotoaparát"</string>
<string name="cancel" msgid="1089011503403416730">"Zrušit"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potvrdit"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Zkusit znovu"</string>
@@ -205,12 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Vypnutí senzorů je aktivní"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Vymazat všechna oznámení."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"a ještě <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="few">Skupina obsahuje ještě <xliff:g id="NUMBER_1">%s</xliff:g> oznámení.</item>
- <item quantity="many">Skupina obsahuje ještě <xliff:g id="NUMBER_1">%s</xliff:g> oznámení.</item>
- <item quantity="other">Skupina obsahuje ještě <xliff:g id="NUMBER_1">%s</xliff:g> oznámení.</item>
- <item quantity="one">Skupina obsahuje ještě <xliff:g id="NUMBER_0">%s</xliff:g> oznámení.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Skupina obsahuje ještě # oznámení.}few{Skupina obsahuje ještě # oznámení.}many{Skupina obsahuje ještě # oznámení.}other{Skupina obsahuje ještě # oznámení.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Obrazovka je uzamčena v orientaci na šířku."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Obrazovka je uzamčena v orientaci na výšku."</string>
<string name="dessert_case" msgid="9104973640704357717">"Pult se sladkostmi"</string>
@@ -228,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatické otáčení"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otočení obrazovky"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Spořič obrazovky"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Přístup k fotoaparátu"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Přístup k mikrofonu"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Dostupné"</string>
@@ -257,12 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Zapínání…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Spořič dat zapnut"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="few">%d zařízení</item>
- <item quantity="many">%d zařízení</item>
- <item quantity="other">%d zařízení</item>
- <item quantity="one">%d zařízení</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# zařízení}few{# zařízení}many{# zařízení}other{# zařízení}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Svítilna"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Fotoaparát se používá"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobilní data"</string>
@@ -319,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Znovu klepněte"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Otevřete přejetím prstem nahoru"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Otevřete klepnutím na ikonu odemknutí"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Odemknuto obličejem. Klepněte na ikonu odemknutí."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Odemknuto obličejem. Stisknutím otevřete."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Obličej rozpoznán. Stisknutím otevřete."</string>
@@ -355,13 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relaci pokračovat?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začít znovu"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ano, pokračovat"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Režim hosta"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Jste v režimu hosta"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Přidáním nového uživatele ukončíte režim hosta a smažete všechny aplikace a data z aktuální relace hosta."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Bylo dosaženo limitu uživatelů"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="few">Lze přidat až <xliff:g id="COUNT">%d</xliff:g> uživatele.</item>
- <item quantity="many">Lze přidat až <xliff:g id="COUNT">%d</xliff:g> uživatele.</item>
- <item quantity="other">Lze přidat až <xliff:g id="COUNT">%d</xliff:g> uživatelů.</item>
- <item quantity="one">Lze vytvořit jen jednoho uživatele.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Lze vytvořit jen jednoho uživatele.}few{Přidat můžete maximálně # uživatele.}many{Přidat můžete maximálně # uživatele.}other{Přidat můžete maximálně # uživatelů.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Odstranit uživatele?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Veškeré aplikace a data tohoto uživatele budou smazána."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Odstranit"</string>
@@ -547,18 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Připomenutí"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Zpět"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Odloženo o <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="few">%d hodiny</item>
- <item quantity="many">%d hodiny</item>
- <item quantity="other">%d hodin</item>
- <item quantity="one">%d hodina</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="few">%d minuty</item>
- <item quantity="many">%d minuty</item>
- <item quantity="other">%d minut</item>
- <item quantity="one">%d minuta</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hodina}=2{# hodiny}few{# hodiny}many{# hodiny}other{# hodin}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuta}few{# minuty}many{# minuty}other{# minut}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Spořič baterie"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Tlačítko <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -707,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Upozornění"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Baterie"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Snímky obrazovek"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Všeobecné zprávy"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Okamžité aplikace"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Nastavit"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Úložiště"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Tipy"</string>
<string name="instant_apps" msgid="8337185853050247304">"Okamžité aplikace"</string>
@@ -781,12 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"přepnout"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Ovládání zařízení"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Vyberte aplikaci, pro kterou chcete přidat ovládací prvky"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="few">Byly přidány <xliff:g id="NUMBER_1">%s</xliff:g> ovládací prvky.</item>
- <item quantity="many">Bylo přidáno <xliff:g id="NUMBER_1">%s</xliff:g> ovládacího prvku.</item>
- <item quantity="other">Bylo přidáno <xliff:g id="NUMBER_1">%s</xliff:g> ovládacích prvků.</item>
- <item quantity="one">Byl přidán <xliff:g id="NUMBER_0">%s</xliff:g> ovládací prvek.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Byl přidán # ovládací prvek.}few{Byly přidány # ovládací prvky.}many{Bylo přidáno # ovládacího prvku.}other{Bylo přidáno # ovládacích prvků.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Odstraněno"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Přidáno do oblíbených"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Přidáno do oblíbených na pozici <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -943,12 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Přidat dlaždici"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepřidávat dlaždici"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Zvolte uživatele"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> aplikace jsou aktivní</item>
- <item quantity="many"><xliff:g id="COUNT_1">%s</xliff:g> aplikace je aktivních</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplikací je aktivních</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplikace je aktivní</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikace je aktivní}few{# aplikace jsou aktivní}many{# aplikace je aktivních}other{# aplikací je aktivních}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nové informace"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivní aplikace"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Tyto aplikace jsou spuštěné a aktivní, i když je nepoužíváte. Zlepšuje to jejich funkčnost, ale může to mít dopad na výdrž baterie."</string>
@@ -977,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Je nastaven budík"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Fotoaparát a mikrofon jsou vypnuté"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# oznámení}few{# oznámení}many{# oznámení}other{# oznámení}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Vysílání"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zastavit vysílání v aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Pokud budete vysílat v aplikaci <xliff:g id="SWITCHAPP">%1$s</xliff:g> nebo změníte výstup, aktuální vysílání se zastaví"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Vysílat v aplikaci <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Změna výstupu"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Neznámé"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d. MMMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"H:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
index 2af84d97653c..64e83e0c31b8 100644
--- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Vypnuto"</item>
<item msgid="460891964396502657">"Zapnuto"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Nedostupné"</item>
+ <item msgid="8014986104355098744">"Vypnuto"</item>
+ <item msgid="5966994759929723339">"Zapnuto"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 405864c9cea2..c1e364b2745b 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Enheden er låst"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanner ansigt"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Send"</string>
- <string name="phone_label" msgid="5715229948920451352">"åbn telefon"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"åbn taleassistent"</string>
- <string name="camera_label" msgid="8253821920931143699">"åbn kamera"</string>
<string name="cancel" msgid="1089011503403416730">"Annuller"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Bekræft"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Prøv igen"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensorer er slået fra"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Ryd alle notifikationer."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"<xliff:g id="NUMBER">%s</xliff:g> mere"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> notifikation mere i gruppen.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> notifikationer mere i gruppen.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# notifikation mere i gruppen.}one{# notifikation mere i gruppen.}other{# notifikationer mere i gruppen.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Skærmen er nu låst i liggende retning."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Skærmen er nu låst i stående format."</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessertcase"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Roter automatisk"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Roter skærmen automatisk"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokation"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Pauseskærm"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameraadgang"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonadgang"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tilgængelig"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Aktiverer…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Datasparefunktion er slået til"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d enhed</item>
- <item quantity="other">%d enheder</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# enhed}one{# enhed}other{# enheder}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lommelygte"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kameraet er i brug"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobildata"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Tryk igen"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Stryg opad for at åbne"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Tryk på oplåsningsikonet for at åbne"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Låst op vha. ansigt. Tryk på oplåsningsikonet for at åbne."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Låst op ved hjælp af ansigt. Tryk for at åbne."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Ansigt genkendt. Tryk for at åbne."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsætte din session?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start forfra"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, fortsæt"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Gæstetilstand"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Gæstetilstand er aktiveret"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Hvis du tilføjer en ny bruger, deaktiveres gæstetilstanden, og alle apps og data slettes fra den aktuelle gæstesession."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Grænsen for antal brugere er nået"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Du kan tilføje op til <xliff:g id="COUNT">%d</xliff:g> bruger.</item>
- <item quantity="other">Du kan tilføje op til <xliff:g id="COUNT">%d</xliff:g> brugere.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Der kan kun oprettes én bruger.}one{Du kan tilføje op til # bruger}other{Du kan tilføje op til # brugere}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Vil du fjerne brugeren?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Alle apps og data for denne bruger slettes."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Fjern"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Påmind mig"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Fortryd"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Udsat i <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d time</item>
- <item quantity="other">%d timer</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d minut</item>
- <item quantity="other">%d minutter</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# time}=2{# timer}one{# time}other{# timer}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minut}one{# minut}other{# minutter}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Batterisparefunktion"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g>-knap"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Underretninger"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Batteri"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Generelle meddelelser"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Konfiguration"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Lagerplads"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Tips"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"slå til/fra"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Enhedsstyring"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Vælg en app for at tilføje styring"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> styring er tilføjet.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> styring er tilføjet.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# styringselement er tilføjet.}one{# styringselement er tilføjet.}other{# styringselementer er tilføjet.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Fjernet"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Angivet som favorit"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Angivet som favorit. Position <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tilføj handlingsfelt"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Tilføj ikke felt"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Vælg bruger"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> app er aktiv</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apps er aktive</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app er aktiv}one{# app er aktiv}other{# apps er aktive}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nye oplysninger"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktive apps"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Disse apps er aktive og kører, også når du ikke bruger dem. Det forbedrer deres funktionalitet, men det kan også påvirke batteritiden."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmen er indstillet"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera og mikrofon er slået fra"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notifikation}one{# notifikation}other{# notifikationer}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Udsender"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop udsendelsen <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Hvis du udsender <xliff:g id="SWITCHAPP">%1$s</xliff:g> eller skifter output, stopper din aktuelle udsendelse"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Udsend <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Skift output"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Ukendt"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d. MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"tt.mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk.mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml
index 0fe06b3433c3..f0132dc66130 100644
--- a/packages/SystemUI/res/values-da/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Fra"</item>
<item msgid="460891964396502657">"Til"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Ikke tilgængelig"</item>
+ <item msgid="8014986104355098744">"Fra"</item>
+ <item msgid="5966994759929723339">"Til"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 97e403844c59..618901cae413 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Gerät gesperrt"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Gesicht wird gescannt"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Senden"</string>
- <string name="phone_label" msgid="5715229948920451352">"Telefon öffnen"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"Sprachassistent öffnen"</string>
- <string name="camera_label" msgid="8253821920931143699">"Kamera öffnen"</string>
<string name="cancel" msgid="1089011503403416730">"Abbrechen"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Bestätigen"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Noch einmal versuchen"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"\"Sensoren aus\" ist aktiv"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Alle Benachrichtigungen löschen"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> weitere Benachrichtigungen vorhanden.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> weitere Benachrichtigung vorhanden.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# weitere Benachrichtigung vorhanden.}other{# weitere Benachrichtigungen vorhanden.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Bildschirm bleibt im Querformat."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Bildschirm bleibt im Hochformat."</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessertbehälter"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. drehen"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Bildschirm automatisch drehen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Standort"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Bildschirmschoner"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Kamerazugriff"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonzugriff"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Verfügbar"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Wird aktiviert…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Datensparmodus an"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d Geräte</item>
- <item quantity="one">%d Gerät</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# Gerät}other{# Geräte}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Taschenlampe"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera wird verwendet"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobile Daten"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Noch einmal tippen"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Zum Öffnen nach oben wischen"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Tippe zum Öffnen auf das Symbol „Entsperren“"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Gerät mit dem Gesicht entsperrt. Tippe zum Öffnen auf das Symbol „Entsperren“."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Gerät mit dem Gesicht entsperrt. Tippe zum Öffnen."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Gesicht erkannt. Tippe zum Öffnen."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Möchtest du deine Sitzung fortsetzen?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Neu starten"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, weiter"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Gastmodus"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Du befindest dich im Gastmodus"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Durch Hinzufügen eines neuen Nutzers wird der Gastmodus beendet und alle Apps und Daten der aktuellen Gastsitzung werden gelöscht."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Nutzerlimit erreicht"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Du kannst bis zu <xliff:g id="COUNT">%d</xliff:g> Nutzer hinzufügen.</item>
- <item quantity="one">Es kann nur ein Nutzer erstellt werden.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Es kann nur ein Nutzer erstellt werden.}other{Du kannst bis zu # Nutzer hinzufügen.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Nutzer entfernen?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Alle Apps und Daten dieses Nutzers werden gelöscht."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Entfernen"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Erinnern"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Rückgängig machen"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Erinnerung in <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d Stunden</item>
- <item quantity="one">%d Stunde</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d Minuten</item>
- <item quantity="one">%d Minute</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# Stunde}=2{# Stunden}other{# Stunden}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# Minute}other{# Minuten}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Energiesparmodus"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Taste <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Pos1"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Benachrichtigungen"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Akku"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Nachrichten"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Android Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Einrichtung"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Speicher"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Hinweise"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"Wechseln"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Gerätesteuerung"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"App zum Hinzufügen von Steuerelementen auswählen"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> Steuerelemente hinzugefügt.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> Steuerelement hinzugefügt.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# Steuerelement hinzugefügt.}other{# Steuerelemente hinzugefügt.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Entfernt"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Zu Favoriten hinzugefügt"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Zu Favoriten hinzugefügt, Position <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Hinzufügen"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nicht hinzufügen"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Nutzer auswählen"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> Apps sind aktiv</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> App ist aktiv</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# App ist aktiv}other{# Apps sind aktiv}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Neue Informationen"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktive Apps"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Diese Apps sind aktiv und werden auch dann ausgeführt, wenn du sie gerade nicht verwendest. Dies wird für einige ihrer Funktionen benötigt, kann aber auch die Akkulaufzeit beeinträchtigen."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Wecker gestellt"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera und Mikrofon ausgeschaltet"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# Benachrichtigung}other{# Benachrichtigungen}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Übertragung läuft"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> nicht mehr streamen?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Wenn du <xliff:g id="SWITCHAPP">%1$s</xliff:g> streamst oder die Ausgabe änderst, wird dein aktueller Stream beendet"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> streamen"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Ausgabe ändern"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unbekannt"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml
index ba610b33ccfa..bc50e1603ea4 100644
--- a/packages/SystemUI/res/values-de/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Aus"</item>
<item msgid="460891964396502657">"An"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Nicht verfügbar"</item>
+ <item msgid="8014986104355098744">"Aus"</item>
+ <item msgid="5966994759929723339">"An"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index fe349b47caaf..4a15ede13596 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Η συσκευή κλειδώθηκε"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Σάρωση προσώπου"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Αποστολή"</string>
- <string name="phone_label" msgid="5715229948920451352">"άνοιγμα τηλεφώνου"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"άνοιγμα φωνητικής υποβοήθησης"</string>
- <string name="camera_label" msgid="8253821920931143699">"άνοιγμα φωτογραφικής μηχανής"</string>
<string name="cancel" msgid="1089011503403416730">"Ακύρωση"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Επιβεβαίωση"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Δοκιμάστε ξανά"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Απενεργοποίηση αισθητήρων ενεργή"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Διαγραφή όλων των ειδοποιήσεων."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> επιπλέον ειδοποιήσεις εντός της ομάδας.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> επιπλέον ειδοποίηση εντός της ομάδας.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# ακόμη ειδοποίηση μέσα στην ομάδα.}other{# ακόμη ειδοποιήσεις μέσα στην ομάδα.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Η οθόνη έχει κλειδωθεί σε οριζόντιο προσανατολισμό."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Η οθόνη έχει κλειδωθεί σε κατακόρυφο προσανατολισμό."</string>
<string name="dessert_case" msgid="9104973640704357717">"Επιδόρπιο"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Αυτόματη περιστροφή"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Αυτόματη περιστροφή οθόνης"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Τοποθεσία"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Προφύλαξη οθόνης"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Πρόσβαση κάμερας"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Πρόσβαση μικροφώνου"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Διαθέσιμη"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Σημείο πρόσβασης Wi-Fi"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Ενεργοποίηση…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Εξοικ. δεδομ. ενεργή"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d συσκευές</item>
- <item quantity="one">%d συσκευή</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# συσκευή}other{# συσκευές}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Φακός"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Η κάμερα χρησιμοποιείται"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Δεδομένα κινητής τηλεφωνίας"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Πατήστε ξανά"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Σύρετε προς τα επάνω για άνοιγμα"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Πατήστε το εικονίδιο ξεκλειδώματος για άνοιγμα"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Ξεκλείδωμα με πρόσωπο. Πατήστε το εικονίδιο ξεκλειδώματος."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Ξεκλείδωμα με αναγνώριση προσώπου. Πατήστε για άνοιγμα."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Το πρόσωπο αναγνωρίστηκε. Πατήστε για άνοιγμα."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Θέλετε να συνεχίσετε την περίοδο σύνδεσής σας;"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Έναρξη από την αρχή"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ναι, συνέχεια"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Λειτουργία επισκέπτη"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Βρίσκεστε σε λειτουργία επισκέπτη"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Με την προσθήκη νέου χρήστη θα γίνει έξοδος από τη λειτουργία επισκέπτη και θα διαγραφούν όλες οι εφαρμογές και τα δεδομένα από την τρέχουσα περίοδο σύνδεσης επισκέπτη."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Συμπληρώθηκε το όριο χρηστών"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Μπορείτε να προσθέσετε έως <xliff:g id="COUNT">%d</xliff:g> χρήστες.</item>
- <item quantity="one">Είναι δυνατή η δημιουργία μόνο ενός χρήστη.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Είναι δυνατή η δημιουργία μόνο ενός χρήστη.}other{Μπορείτε να προσθέσετε έως και # χρήστες}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Κατάργηση χρήστη;"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Όλες οι εφαρμογές και τα δεδομένα αυτού του χρήστη θα διαγραφούν."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Κατάργηση"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Να γίνει υπενθύμιση"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Αναίρεση"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Σε αναβολή για <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d ώρες</item>
- <item quantity="one">%d ώρα</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d λεπτά</item>
- <item quantity="one">%d λεπτό</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ώρα}=2{# ώρες}other{# ώρες}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# λεπτό}other{# λεπτά}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Εξοικονόμηση μπαταρίας"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Κουμπί <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Ειδοποιήσεις"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Μπαταρία"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Στιγμιότυπα οθόνης"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Γενικά μηνύματα"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Εφαρμογές"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Ρύθμιση"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Αποθηκευτικός χώρος"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Συμβουλές"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Εφαρμογές"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"εναλλαγή"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Στοιχεία ελέγχου συσκευής"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Επιλογή εφαρμογής για προσθήκη στοιχείων ελέγχου"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">Προστέθηκαν <xliff:g id="NUMBER_1">%s</xliff:g> στοιχεία ελέγχου.</item>
- <item quantity="one">Προστέθηκε <xliff:g id="NUMBER_0">%s</xliff:g> στοιχείο ελέγχου.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Προστέθηκε # στοιχείο ελέγχου.}other{Προστέθηκαν # στοιχεία ελέγχου.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Καταργήθηκε"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Προστέθηκε στα αγαπημένα"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Προστέθηκε στα αγαπημένα, στη θέση <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Προσθήκη πλακιδίου"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Χωρίς προσθ. πλακιδ."</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Επιλογή χρήστη"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> εφαρμογές είναι ενεργές</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> εφαρμογή είναι ενεργή</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# εφαρμογή είναι ενεργή}other{# εφαρμογές είναι ενεργές}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Νέες πληροφορίες"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Ενεργές εφαρμογές"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Αυτές οι εφαρμογές είναι ενεργές και εκτελούνται, ακόμη και αν δεν τις χρησιμοποιείτε. Αυτό βελτιώνει τη λειτουργικότητά τους, αλλά μπορεί να επηρεάσει τη διάρκεια ζωής της μπαταρίας."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Το ξυπνητήρι ρυθμίστηκε"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Η κάμερα και το μικρόφωνο έχουν απενεργοποιηθεί"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ειδοποίηση}other{# ειδοποιήσεις}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Μετάδοση"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Διακοπή μετάδοσης με την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Εάν κάνετε μετάδοση με την εφαρμογή <xliff:g id="SWITCHAPP">%1$s</xliff:g> ή αλλάξετε την έξοδο, η τρέχουσα μετάδοση θα σταματήσει"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Μετάδοση με την εφαρμογή <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Αλλαγή εξόδου"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Άγνωστο"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"ΗΗΗ, ΜΜΜ η"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"ώ:λλ"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:λλ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml
index 74c091e4ed18..352af39bfe11 100644
--- a/packages/SystemUI/res/values-el/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Ανενεργή"</item>
<item msgid="460891964396502657">"Ενεργή"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Μη διαθέσιμο"</item>
+ <item msgid="8014986104355098744">"Ανενεργό"</item>
+ <item msgid="5966994759929723339">"Ενεργό"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 58d06971920f..634a9daa2abe 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Device locked"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanning face"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Send"</string>
- <string name="phone_label" msgid="5715229948920451352">"open phone"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"open voice assist"</string>
- <string name="camera_label" msgid="8253821920931143699">"open camera"</string>
<string name="cancel" msgid="1089011503403416730">"Cancel"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirm"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Try again"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensors off active"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Clear all notifications."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+<xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> more notifications inside.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> more notification inside.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# more notification inside.}other{# more notifications inside.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Screen is locked in landscape orientation."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Screen is locked in portrait orientation."</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screensaver"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Camera access"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mic access"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Available"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Turning on…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Data Saver is on"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d devices</item>
- <item quantity="one">%d device</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# device}other{# devices}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Torch"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Camera in use"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobile data"</string>
@@ -315,6 +307,7 @@
<string name="tap_again" msgid="1315420114387908655">"Tap again"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Press the unlock icon to open"</string>
+ <string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"Unlocked by face. Swipe up to open."</string>
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Unlocked by face. Press the unlock icon to open."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Unlocked by face. Press to open."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Face recognised. Press to open."</string>
@@ -351,11 +344,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yes, continue"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"You are in guest mode"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Adding a new user will exit guest mode and delete all apps and data from the current guest session."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"User limit reached"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item>
- <item quantity="one">Only one user can be created.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Only one user can be created.}other{You can add up to # users.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Remove user?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"All apps and data of this user will be deleted."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Remove"</string>
@@ -541,14 +534,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Remind me"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Undo"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Snoozed for <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d hours</item>
- <item quantity="one">%d hour</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minutes</item>
- <item quantity="one">%d minute</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hour}=2{# hours}other{# hours}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minute}other{# minutes}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Battery Saver"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +684,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alerts"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Battery"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"General Messages"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Setup"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Storage"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Hints"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +759,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"toggle"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> controls added.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> control added.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# control added.}other{# controls added.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Removed"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Favourited"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Favourited, position <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +916,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apps are active</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> app is active</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app is active}other{# apps are active}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"New information"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Active apps"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"These apps are active and running, even when you’re not using them. This improves their functionality, but it may also affect battery life."</string>
@@ -963,4 +945,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm set"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Camera and mic are off"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}other{# notifications}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Change output"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
index 6f8cfb695ac6..56cdbef092f2 100644
--- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Off"</item>
<item msgid="460891964396502657">"On"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Unavailable"</item>
+ <item msgid="8014986104355098744">"Off"</item>
+ <item msgid="5966994759929723339">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index b68d6eebb905..f0e2208a22d0 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Device locked"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanning face"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Send"</string>
- <string name="phone_label" msgid="5715229948920451352">"open phone"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"open voice assist"</string>
- <string name="camera_label" msgid="8253821920931143699">"open camera"</string>
<string name="cancel" msgid="1089011503403416730">"Cancel"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirm"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Try again"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensors off active"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Clear all notifications."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+<xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> more notifications inside.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> more notification inside.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# more notification inside.}other{# more notifications inside.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Screen is locked in landscape orientation."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Screen is locked in portrait orientation."</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screensaver"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Camera access"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mic access"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Available"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Turning on…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Data Saver is on"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d devices</item>
- <item quantity="one">%d device</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# device}other{# devices}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Flashlight"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Camera in use"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobile data"</string>
@@ -315,6 +307,7 @@
<string name="tap_again" msgid="1315420114387908655">"Tap again"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Press the unlock icon to open"</string>
+ <string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"Unlocked by face. Swipe up to open."</string>
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Unlocked by face. Press the unlock icon to open."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Unlocked by face. Press to open."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Face recognised. Press to open."</string>
@@ -351,11 +344,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yes, continue"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"You are in guest mode"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Adding a new user will exit guest mode and delete all apps and data from the current guest session."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"User limit reached"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item>
- <item quantity="one">Only one user can be created.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Only one user can be created.}other{You can add up to # users.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Remove user?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"All apps and data of this user will be deleted."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Remove"</string>
@@ -541,14 +534,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Remind me"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Undo"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Snoozed for <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d hours</item>
- <item quantity="one">%d hour</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minutes</item>
- <item quantity="one">%d minute</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hour}=2{# hours}other{# hours}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minute}other{# minutes}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Battery Saver"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +684,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alerts"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Battery"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"General Messages"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Setup"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Storage"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Hints"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +759,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"toggle"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> controls added.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> control added.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# control added.}other{# controls added.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Removed"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Favourited"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Favourited, position <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +916,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apps are active</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> app is active</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app is active}other{# apps are active}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"New information"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Active apps"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"These apps are active and running, even when you’re not using them. This improves their functionality, but it may also affect battery life."</string>
@@ -963,4 +945,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm set"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Camera and mic are off"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}other{# notifications}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Change output"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
index 6f8cfb695ac6..56cdbef092f2 100644
--- a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Off"</item>
<item msgid="460891964396502657">"On"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Unavailable"</item>
+ <item msgid="8014986104355098744">"Off"</item>
+ <item msgid="5966994759929723339">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 58d06971920f..634a9daa2abe 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Device locked"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanning face"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Send"</string>
- <string name="phone_label" msgid="5715229948920451352">"open phone"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"open voice assist"</string>
- <string name="camera_label" msgid="8253821920931143699">"open camera"</string>
<string name="cancel" msgid="1089011503403416730">"Cancel"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirm"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Try again"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensors off active"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Clear all notifications."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+<xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> more notifications inside.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> more notification inside.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# more notification inside.}other{# more notifications inside.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Screen is locked in landscape orientation."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Screen is locked in portrait orientation."</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screensaver"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Camera access"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mic access"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Available"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Turning on…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Data Saver is on"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d devices</item>
- <item quantity="one">%d device</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# device}other{# devices}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Torch"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Camera in use"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobile data"</string>
@@ -315,6 +307,7 @@
<string name="tap_again" msgid="1315420114387908655">"Tap again"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Press the unlock icon to open"</string>
+ <string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"Unlocked by face. Swipe up to open."</string>
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Unlocked by face. Press the unlock icon to open."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Unlocked by face. Press to open."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Face recognised. Press to open."</string>
@@ -351,11 +344,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yes, continue"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"You are in guest mode"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Adding a new user will exit guest mode and delete all apps and data from the current guest session."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"User limit reached"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item>
- <item quantity="one">Only one user can be created.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Only one user can be created.}other{You can add up to # users.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Remove user?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"All apps and data of this user will be deleted."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Remove"</string>
@@ -541,14 +534,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Remind me"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Undo"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Snoozed for <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d hours</item>
- <item quantity="one">%d hour</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minutes</item>
- <item quantity="one">%d minute</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hour}=2{# hours}other{# hours}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minute}other{# minutes}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Battery Saver"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +684,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alerts"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Battery"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"General Messages"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Setup"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Storage"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Hints"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +759,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"toggle"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> controls added.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> control added.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# control added.}other{# controls added.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Removed"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Favourited"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Favourited, position <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +916,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apps are active</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> app is active</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app is active}other{# apps are active}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"New information"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Active apps"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"These apps are active and running, even when you’re not using them. This improves their functionality, but it may also affect battery life."</string>
@@ -963,4 +945,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm set"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Camera and mic are off"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}other{# notifications}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Change output"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
index 6f8cfb695ac6..56cdbef092f2 100644
--- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Off"</item>
<item msgid="460891964396502657">"On"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Unavailable"</item>
+ <item msgid="8014986104355098744">"Off"</item>
+ <item msgid="5966994759929723339">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 58d06971920f..634a9daa2abe 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Device locked"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanning face"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Send"</string>
- <string name="phone_label" msgid="5715229948920451352">"open phone"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"open voice assist"</string>
- <string name="camera_label" msgid="8253821920931143699">"open camera"</string>
<string name="cancel" msgid="1089011503403416730">"Cancel"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirm"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Try again"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensors off active"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Clear all notifications."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+<xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> more notifications inside.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> more notification inside.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# more notification inside.}other{# more notifications inside.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Screen is locked in landscape orientation."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Screen is locked in portrait orientation."</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screensaver"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Camera access"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mic access"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Available"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Turning on…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Data Saver is on"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d devices</item>
- <item quantity="one">%d device</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# device}other{# devices}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Torch"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Camera in use"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobile data"</string>
@@ -315,6 +307,7 @@
<string name="tap_again" msgid="1315420114387908655">"Tap again"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Press the unlock icon to open"</string>
+ <string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"Unlocked by face. Swipe up to open."</string>
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Unlocked by face. Press the unlock icon to open."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Unlocked by face. Press to open."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Face recognised. Press to open."</string>
@@ -351,11 +344,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yes, continue"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"You are in guest mode"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Adding a new user will exit guest mode and delete all apps and data from the current guest session."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"User limit reached"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item>
- <item quantity="one">Only one user can be created.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Only one user can be created.}other{You can add up to # users.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Remove user?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"All apps and data of this user will be deleted."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Remove"</string>
@@ -541,14 +534,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Remind me"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Undo"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Snoozed for <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d hours</item>
- <item quantity="one">%d hour</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minutes</item>
- <item quantity="one">%d minute</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hour}=2{# hours}other{# hours}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minute}other{# minutes}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Battery Saver"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +684,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alerts"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Battery"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"General Messages"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Setup"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Storage"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Hints"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +759,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"toggle"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Choose app to add controls"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> controls added.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> control added.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# control added.}other{# controls added.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Removed"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Favourited"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Favourited, position <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +916,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apps are active</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> app is active</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app is active}other{# apps are active}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"New information"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Active apps"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"These apps are active and running, even when you’re not using them. This improves their functionality, but it may also affect battery life."</string>
@@ -963,4 +945,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm set"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Camera and mic are off"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}other{# notifications}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Change output"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
index 6f8cfb695ac6..56cdbef092f2 100644
--- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Off"</item>
<item msgid="460891964396502657">"On"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Unavailable"</item>
+ <item msgid="8014986104355098744">"Off"</item>
+ <item msgid="5966994759929723339">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 7fde88cb6462..1418095fbfe6 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‏‏‏‎‎‎‎‏‏‎‎‎‎‎‏‏‏‏‎‎‎‏‎‎‎‏‎‏‏‏‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎Device locked‎‏‎‎‏‎"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎Scanning face‎‏‎‎‏‎"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‎‏‎‎‏‎‎‏‏‎‏‎‏‎‎‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎Send‎‏‎‎‏‎"</string>
- <string name="phone_label" msgid="5715229948920451352">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎‎‎open phone‎‏‎‎‏‎"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‎‎‏‎‏‎‏‎‏‎‏‏‏‎‏‏‎‎‏‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎open voice assist‎‏‎‎‏‎"</string>
- <string name="camera_label" msgid="8253821920931143699">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‏‎‏‏‎‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‎‎‏‏‎open camera‎‏‎‎‏‎"</string>
<string name="cancel" msgid="1089011503403416730">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‎‎Cancel‎‏‎‎‏‎"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎Confirm‎‏‎‎‏‎"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‎‎‎‏‏‎‎‎Try again‎‏‎‎‏‎"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‎‏‏‎‎‏‏‏‏‏‏‎Sensors off active‎‏‎‎‏‎"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‎‎‎‎‎Clear all notifications.‎‏‎‎‏‎"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‏‎‎‎‎+ ‎‏‎‎‏‏‎<xliff:g id="NUMBER">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%s</xliff:g>‎‏‎‎‏‏‏‎ more notifications inside.‎‏‎‎‏‎</item>
- <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%s</xliff:g>‎‏‎‎‏‏‏‎ more notification inside.‎‏‎‎‏‎</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎# more notification inside.‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎# more notifications inside.‎‏‎‎‏‎}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‎‎‎‎‎‏‏‎‎‏‎‏‏‏‎‎‏‎‎‏‎‏‏‎‎‏‏‎‏‎‎‏‏‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎‎‏‎‎‎‎Screen is locked in landscape orientation.‎‏‎‎‏‎"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎Screen is locked in portrait orientation.‎‏‎‎‏‎"</string>
<string name="dessert_case" msgid="9104973640704357717">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‏‏‎‏‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‎‏‎‏‎‏‎‏‎Dessert Case‎‏‎‎‏‎"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‎‎‏‎‏‎‎‎‎‎‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‎‎Auto-rotate‎‏‎‎‏‎"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‏‎‎‎‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‎‎‎‏‎‎Auto-rotate screen‎‏‎‎‏‎"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎Location‎‏‎‎‏‎"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‏‎‎‎‎Screen saver‎‏‎‎‏‎"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎‏‏‎‏‎‎‎‎‎‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‎‎‏‎‎‎‏‎‏‏‎‏‏‎Camera access‎‏‎‎‏‎"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎‎‏‎‎‎‏‏‏‎Mic access‎‏‎‎‏‎"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‏‎‏‎‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‏‎‎‏‎‏‎‎Available‎‏‎‎‏‎"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎‎‎‎Hotspot‎‏‎‎‏‎"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‎‏‏‎‏‎‏‏‎‎‏‎‏‎‎‏‏‏‏‎‏‎‎‎Turning on…‎‏‎‎‏‎"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‏‎‎‎‏‎‏‎‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‏‎‎‏‏‎‎‎Data Saver is on‎‏‎‎‏‎"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‎‎‎%d devices‎‏‎‎‏‎</item>
- <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‎‎‎%d device‎‏‎‎‏‎</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‏‎‏‎‏‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‎‏‎‎‎‎‏‎# device‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‏‎‏‎‏‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‎‏‎‎‎‎‏‎# devices‎‏‎‎‏‎}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‏‎Flashlight‎‏‎‎‏‎"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‏‎‏‏‏‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‏‏‎‏‏‏‎‏‏‎Camera in use‎‏‎‎‏‎"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‎‎‏‎‎‎‎‎‏‏‎‎‎‏‎‏‎‏‏‎‏‎Mobile data‎‏‎‎‏‎"</string>
@@ -315,6 +307,7 @@
<string name="tap_again" msgid="1315420114387908655">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎‎‎‏‎‏‎‎‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎Tap again‎‏‎‎‏‎"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎Swipe up to open‎‏‎‎‏‎"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‏‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‏‎‎‏‎Press the unlock icon to open‎‏‎‎‏‎"</string>
+ <string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‏‏‎‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‎‎‏‎Unlocked by face. Swipe up to open.‎‏‎‎‏‎"</string>
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎‎Unlocked by face. Press the unlock icon to open.‎‏‎‎‏‎"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‏‎‎‏‎‎‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎Unlocked by face. Press to open.‎‏‎‎‏‎"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‏‎‏‏‎‏‏‎‏‏‏‎‏‏‎‎‏‎‏‏‎‎Face recognized. Press to open.‎‏‎‎‏‎"</string>
@@ -351,11 +344,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎Do you want to continue your session?‎‏‎‎‏‎"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‎‏‏‏‎‏‎Start over‎‏‎‎‏‎"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‎‎‏‏‎‏‎Yes, continue‎‏‎‎‏‎"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‎‏‎Guest mode‎‏‎‎‏‎"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‏‎‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎You are in guest mode‎‏‎‎‏‎"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‏‎‎‎‏‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Adding a new user will exit guest mode and delete all apps and data from the current guest session.‎‏‎‎‏‎"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‎‎‎‎‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‎User limit reached‎‏‎‎‏‎"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‏‏‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‎‎You can add up to ‎‏‎‎‏‏‎<xliff:g id="COUNT">%d</xliff:g>‎‏‎‎‏‏‏‎ users.‎‏‎‎‏‎</item>
- <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‏‏‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‎‎Only one user can be created.‎‏‎‎‏‎</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‏‎‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‎‎‎‏‏‎‏‎‎‎Only one user can be created.‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‏‎‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‎‎‎‏‏‎‏‎‎‎You can add up to # users.‎‏‎‎‏‎}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‎‎‏‎‎Remove user?‎‏‎‎‏‎"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‎‎‎‎‎‏‎‏‏‎‎‏‎All apps and data of this user will be deleted.‎‏‎‎‏‎"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‎‏‎‎‎‏‎‏‎‏‎‎‎‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‎‏‎‎‎‎Remove‎‏‎‎‏‎"</string>
@@ -541,14 +534,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‏‏‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‏‎‏‏‎Remind me‎‏‎‎‏‎"</string>
<string name="snooze_undo" msgid="2738844148845992103">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎Undo‎‏‎‎‏‎"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‏‎‏‎‏‏‏‎‎‏‎‏‎‎‏‎‏‏‎‎‏‏‏‎‎‏‎‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‏‎Snoozed for ‎‏‎‎‏‏‎<xliff:g id="TIME_AMOUNT">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‎‏‏‏‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎%d hours‎‏‎‎‏‎</item>
- <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‎‏‏‏‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎%d hour‎‏‎‎‏‎</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‏‎‏‎‏‏‎‎‎%d minutes‎‏‎‎‏‎</item>
- <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‏‎‏‎‏‏‎‎‎%d minute‎‏‎‎‏‎</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎# hour‎‏‎‎‏‎}=2{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎# hours‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎# hours‎‏‎‎‏‎}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‎‏‏‎# minute‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‎‏‏‎# minutes‎‏‎‎‏‎}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‎‎‏‏‎‏‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‏‏‎‏‎‏‎Battery Saver‎‏‎‎‏‎"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‎‏‏‏‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‏‎Button ‎‏‎‎‏‏‎<xliff:g id="NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎Home‎‏‎‎‏‎"</string>
@@ -697,7 +684,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‏‏‏‎‏‏‏‏‎‎Alerts‎‏‎‎‏‎"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‎‏‏‎‎‏‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎Battery‎‏‎‎‏‎"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎‎‎‏‎‏‎‏‎‏‎‎‏‏‎‏‏‏‎‏‎‎‏‎‎‏‎‎‎‏‏‏‎‏‎Screenshots‎‏‎‎‏‎"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‎‏‏‎‎‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎General Messages‎‏‎‎‏‎"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‏‏‎‎‏‎‎‏‎‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎‎Instant Apps‎‏‎‎‏‎"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‎‎‎‏‏‎‏‎‎‏‎‏‎‎‎‏‎‎‏‏‏‏‎‎Setup‎‏‎‎‏‎"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‏‎‎‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎Storage‎‏‎‎‏‎"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎‏‏‎‎‎‏‎‎‎‏‎‏‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‎Hints‎‏‎‎‏‎"</string>
<string name="instant_apps" msgid="8337185853050247304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‏‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‎‎‏‎‎‎‎Instant Apps‎‏‎‎‏‎"</string>
@@ -771,10 +759,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‏‎‎‎‏‎‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‏‎toggle‎‏‎‎‏‎"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎Device controls‎‏‎‎‏‎"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎Choose app to add controls‎‏‎‎‏‎"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%s</xliff:g>‎‏‎‎‏‏‏‎ controls added.‎‏‎‎‏‎</item>
- <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%s</xliff:g>‎‏‎‎‏‏‏‎ control added.‎‏‎‎‏‎</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‏‎‎‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‎‎‏‏‎# control added.‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‏‎‎‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‎‎‏‏‎# controls added.‎‏‎‎‏‎}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‏‏‎‏‏‏‏‏‏‏‏‎Removed‎‏‎‎‏‎"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‎‏‎‎‏‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎Favorited‎‏‎‎‏‎"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎Favorited, position ‎‏‎‎‏‏‎<xliff:g id="NUMBER">%d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
@@ -931,10 +916,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‎‎‎Add tile‎‏‎‎‏‎"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‎Do not add tile‎‏‎‎‏‎"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‎Select user‎‏‎‎‏‎"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="COUNT_1">%s</xliff:g>‎‏‎‎‏‏‏‎ apps are active‎‏‎‎‏‎</item>
- <item quantity="one">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="COUNT_0">%s</xliff:g>‎‏‎‎‏‏‏‎ app is active‎‏‎‎‏‎</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‎‏‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‏‏‏‎# app is active‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‎‏‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‏‏‏‎# apps are active‎‏‎‎‏‎}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‎‎‎‏‎‏‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‏‎‏‎‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‎‎‎‏‏‎‎‎‎New information‎‏‎‎‏‎"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‎‎‏‎‏‎‏‎Active apps‎‏‎‎‏‎"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‎‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎These apps are active and running, even when you’re not using them. This improves their functionality, but it may also affect battery life.‎‏‎‎‏‎"</string>
@@ -963,4 +945,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‎‏‎‏‎‏‎‎‎‎‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‎‏‎‎‏‏‎‎Alarm set‎‏‎‎‏‎"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‎‏‎‎‎‏‎‏‎‎‏‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‏‎‏‎‎‎‎‎‎‎‎‏‎Camera and mic are off‎‏‎‎‏‎"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‎# notification‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‎# notifications‎‏‎‎‏‎}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‎‎‎‎‎‎‎‏‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎‎‏‎‏‏‏‎‏‏‎‏‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‎Broadcasting‎‏‎‎‏‎"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‏‎‏‏‏‎‏‎‎‎‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‎‏‏‏‏‎‎‏‏‎Stop broadcasting ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‎‎‏‎‎‎‎‎‎‏‎If you broadcast ‎‏‎‎‏‏‎<xliff:g id="SWITCHAPP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ or change the output, your current broadcast will stop‎‏‎‎‏‎"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‏‏‎‏‎‏‏‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‎‎‎‏‎‎‎‏‎‏‎Broadcast ‎‏‎‎‏‏‎<xliff:g id="SWITCHAPP">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‎‎Change output‎‏‎‎‏‎"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎Unknown‎‏‎‎‏‎"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎‎‏‏‎‎‎‏‏‏‎‏‎‎‎‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎EEE, MMM d‎‏‎‎‏‎"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‏‏‎‏‎‎‏‎‎‏‎h:mm‎‏‎‎‏‎"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎kk:mm‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
index 99ef50c9d8af..3a8e34c2ebdc 100644
--- a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‎‎‏‎‎Off‎‏‎‎‏‎"</item>
<item msgid="460891964396502657">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎‏‏‏‎‎‏‏‎‏‎‏‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎On‎‏‎‎‏‎"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‏‎Unavailable‎‏‎‎‏‎"</item>
+ <item msgid="8014986104355098744">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‎‎‏‏‎‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‏‏‏‎‎‎‎Off‎‏‎‎‏‎"</item>
+ <item msgid="5966994759929723339">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‏‎‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‎On‎‏‎‎‏‎"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index e0fa6fd58db2..26d980169833 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Escaneando rostro"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
- <string name="phone_label" msgid="5715229948920451352">"abrir teléfono"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"abrir el asistente de voz"</string>
- <string name="camera_label" msgid="8253821920931143699">"abrir cámara"</string>
<string name="cancel" msgid="1089011503403416730">"Cancelar"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmar"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Volver a intentarlo"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensores desactivados sí"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Eliminar todas las notificaciones"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"<xliff:g id="NUMBER">%s</xliff:g> más"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> notificaciones más en el grupo.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> notificación más en el grupo.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# notificación más en el grupo.}other{# notificaciones más en el grupo.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"La pantalla está bloqueada en modo horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"La pantalla está bloqueada en modo vertical."</string>
<string name="dessert_case" msgid="9104973640704357717">"Caja para postres"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar la pantalla automáticamente"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Protector de pantalla"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Acceso a la cámara"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Acceso al mic."</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponible"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Activando…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Ahorro de datos act."</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d dispositivos</item>
- <item quantity="one">%d dispositivo</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# dispositivo}other{# dispositivos}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Linterna"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Cámara en uso"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Datos móviles"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Presiona otra vez"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Desliza el dedo hacia arriba para abrir"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Presiona el ícono de desbloquear para abrir"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Desbloqueo con rostro. Presiona el ícono para abrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Desbloqueo con rostro. Presiona para abrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Rostro reconocido. Presiona para abrir."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres retomar la sesión?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sí, continuar"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Modo de Invitado"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Estás en el modo de invitado"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Si agregas un usuario nuevo, se desactivará el modo de invitado y se borrarán todas las apps y los datos de la sesión de invitado actual."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Alcanzaste el límite de usuarios"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Puedes agregar hasta <xliff:g id="COUNT">%d</xliff:g> usuarios.</item>
- <item quantity="one">Solo se puede crear un usuario.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Solo se puede crear un usuario.}other{Puedes agregar hasta # usuarios.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"¿Confirmas que quieres quitar el usuario?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Se borrarán todas las aplicaciones y los datos de este usuario."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Quitar"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Recuérdame"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Deshacer"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Posponer <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d horas</item>
- <item quantity="one">%d hora</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minutos</item>
- <item quantity="one">%d minuto</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hora}=2{# horas}other{# horas}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuto}other{# minutos}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Ahorro de batería"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Inicio"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alertas"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Batería"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturas de pantalla"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Mensajes generales"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Apps instantáneas"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Configuración"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Almacenamiento"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Sugerencias"</string>
<string name="instant_apps" msgid="8337185853050247304">"Apps instantáneas"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"activar o desactivar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controles de dispositivos"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Elige la app para agregar los controles"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">Se agregaron <xliff:g id="NUMBER_1">%s</xliff:g> controles.</item>
- <item quantity="one">Se agregó <xliff:g id="NUMBER_0">%s</xliff:g> control.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Se agregó # control.}other{Se agregaron # controles.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Quitados"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Está en favoritos"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Está en favoritos en la posición <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Agregar tarjeta"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"No agregar tarjeta"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apps están activas</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> app está activa</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está activa}other{# apps están activas}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nueva información"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps activas"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Estas apps están activas y en ejecución, incluso mientras no las usas. Esto mejora su funcionalidad, pero también afecta la duración de batería."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Se estableció la alarma"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"La cámara y el micrófono están apagados"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificación}other{# notificaciones}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmitiendo"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"¿Quieres dejar de transmitir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si transmites <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambias la salida, tu transmisión actual se detendrá"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmitir <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Cambia la salida"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconocido"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d de MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
index abcec7f644dd..44e9cf2ca61a 100644
--- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Desactivado"</item>
<item msgid="460891964396502657">"Sí"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"No disponible"</item>
+ <item msgid="8014986104355098744">"No"</item>
+ <item msgid="5966994759929723339">"Sí"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 7576da3d47c7..1876d77f2238 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Escaneando cara"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
- <string name="phone_label" msgid="5715229948920451352">"abrir teléfono"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"abrir el asistente de voz"</string>
- <string name="camera_label" msgid="8253821920931143699">"abrir cámara"</string>
<string name="cancel" msgid="1089011503403416730">"Cancelar"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmar"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Reintentar"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensores desactivados"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Borrar todas las notificaciones"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"<xliff:g id="NUMBER">%s</xliff:g> más"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> notificaciones más dentro.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> notificación más dentro.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# notificación más en el grupo.}other{# notificaciones más en el grupo.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"La pantalla está bloqueada en modo horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"La pantalla está bloqueada en modo vertical."</string>
<string name="dessert_case" msgid="9104973640704357717">"Caja para postres"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar pantalla automáticamente"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Salvapantallas"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Acceso a cámara"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Acceso al micro"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponible"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Compartir Internet"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Activando…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Ahorro de datos activado"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d dispositivos</item>
- <item quantity="one">%d dispositivo</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# dispositivo}other{# dispositivos}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Linterna"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Cámara en uso"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Datos móviles"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Toca de nuevo"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Desliza el dedo hacia arriba para abrir"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Pulsa el icono desbloquear para abrir"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Desbloqueado con la cara. Toca el icono de desbloquear para abrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Desbloqueado con la cara. Pulsa para abrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Cara reconocida. Pulsa para abrir."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres continuar con tu sesión?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sí, continuar"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Modo Invitado"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Estás en modo Invitado"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Si añades un nuevo usuario, saldrás del modo Invitado y se eliminarán todas las aplicaciones y datos de la sesión de invitado actual."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Has alcanzado el límite de usuarios"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Puedes añadir hasta <xliff:g id="COUNT">%d</xliff:g> usuarios.</item>
- <item quantity="one">Solo se puede crear un usuario.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Solo se puede crear un usuario.}other{Puedes añadir # usuarios como máximo.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"¿Quitar usuario?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Se eliminarán todas las aplicaciones y datos de este usuario."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Quitar"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Recordar"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Deshacer"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Volverá a mostrarse en <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d horas</item>
- <item quantity="one">%d hora</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minutos</item>
- <item quantity="one">%d minuto</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hora}=2{# horas}other{# horas}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuto}other{# minutos}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Ahorro de batería"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Inicio"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alertas"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Batería"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturas de pantalla"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Mensajes generales"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Aplicaciones Instantáneas"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Configuración"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Almacenamiento"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Sugerencias"</string>
<string name="instant_apps" msgid="8337185853050247304">"Aplicaciones Instantáneas"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"activar/desactivar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Control de dispositivos"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Elige una aplicación para añadir controles"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">Se han añadido <xliff:g id="NUMBER_1">%s</xliff:g> controles.</item>
- <item quantity="one">Se ha añadido <xliff:g id="NUMBER_0">%s</xliff:g> control.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# control añadido.}other{# controles añadidos.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Quitado"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Añadido a favoritos"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Añadido a favoritos (posición <xliff:g id="NUMBER">%d</xliff:g>)"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Añadir recuadro"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"No añadir recuadro"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecciona un usuario"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplicaciones activas</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplicación activa</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplicación activa}other{# aplicaciones activas}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Información nueva"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplicaciones activas"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Estas aplicaciones están activas y en funcionamiento, incluso aunque no las estés usando. Esto mejora su funcionalidad, pero también puede afectar a la duración de la batería."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarma añadida"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"La cámara y el micrófono están desactivados"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificación}other{# notificaciones}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitiendo"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"¿Dejar de emitir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si emites <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambias la salida, tu emisión actual se detendrá"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitir <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Cambiar salida"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconocido"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml
index 7d8be488c5c5..8d5c3c6bcb6d 100644
--- a/packages/SystemUI/res/values-es/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Desactivado"</item>
<item msgid="460891964396502657">"Activado"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"No disponible"</item>
+ <item msgid="8014986104355098744">"Desactivado"</item>
+ <item msgid="5966994759929723339">"Activado"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index efad53fdd33c..c64fef9cba6b 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Seade on lukustatud"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Näo skannimine"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Saada"</string>
- <string name="phone_label" msgid="5715229948920451352">"ava telefon"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"ava häälabi"</string>
- <string name="camera_label" msgid="8253821920931143699">"ava kaamera"</string>
<string name="cancel" msgid="1089011503403416730">"Tühista"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Kinnita"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Proovi uuesti"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Valik Andurid on väljas on aktiivne"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Kustuta kõik teatised."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">Sees on veel <xliff:g id="NUMBER_1">%s</xliff:g> märguannet.</item>
- <item quantity="one">Sees on veel <xliff:g id="NUMBER_0">%s</xliff:g> märguanne.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Sees on veel # märguanne.}other{Sees on veel # märguannet.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Ekraan on lukustatud horisontaalsuunas."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Ekraan on lukustatud vertikaalsuunas."</string>
<string name="dessert_case" msgid="9104973640704357717">"Maiustusekorv"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. pööramine"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Kuva automaatne pööramine"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Asukoht"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Ekraanisäästja"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Juurdepääs kaamerale"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Juurdepääs mikrofonile"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Saadaval"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Kuumkoht"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Sisselülitamine …"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Andmem. säästja sees"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d seadet</item>
- <item quantity="one">%d seade</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# seade}other{# seadet}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Taskulamp"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kasutusel olev kaamera"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobiilne andmeside"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Puudutage uuesti"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Pühkige avamiseks üles"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Avamiseks vajutage avamise ikooni"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Avati näoga. Avamiseks vajutage avamise ikooni."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Avati näoga. Avamiseks vajutage."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Nägu tuvastati. Avamiseks vajutage."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Kas soovite seansiga jätkata?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Alusta uuesti"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Jah, jätka"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Külalisrežiim"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Olete külalisrežiimis"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Uue kasutaja lisamisel suletakse külalisrežiim ning praeguse külastajaseansi rakendused ja andmed kustutatakse."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Kasutajate limiit on täis"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Võite lisada kuni <xliff:g id="COUNT">%d</xliff:g> kasutajat.</item>
- <item quantity="one">Luua saab ainult ühe kasutaja.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Luua saab ainult ühe kasutaja.}other{Saate lisada kuni # kasutajat}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Kas eemaldada kasutaja?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Kasutaja kõik rakendused ja andmed kustutatakse."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Eemalda"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Tuleta mulle meelde"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Võta tagasi"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Edasi lükatud <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d tundi</item>
- <item quantity="one">%d tund</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minutit</item>
- <item quantity="one">%d minut</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# tund}=2{# tundi}other{# tundi}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minut}other{# minutit}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Akusäästja"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Nupp <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Avakuva"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Hoiatused"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Aku"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Ekraanipildid"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Üldised sõnumid"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Installimata avatavad rakendused"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Seadistamine"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Salvestusruum"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Vihjed"</string>
<string name="instant_apps" msgid="8337185853050247304">"Installimata avatavad rakendused"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"lülita"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Seadmete juhikud"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Valige juhtelementide lisamiseks rakendus"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">Lisati <xliff:g id="NUMBER_1">%s</xliff:g> juhtnuppu.</item>
- <item quantity="one">Lisati <xliff:g id="NUMBER_0">%s</xliff:g> juhtnupp.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Lisati # juhtnupp.}other{Lisati # juhtnuppu.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Eemaldatud"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Lisatud lemmikuks"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Lisatud lemmikuks, positsioon <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Lisa paan"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ära lisa paani"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kasutaja valimine"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> rakendust on aktiivsed</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> rakendus on aktiivne</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# rakendus on aktiivne}other{# rakendust on aktiivsed}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Uus teave"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktiivsed rakendused"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Need rakendused on aktiivsed ja neid käitatakse isegi siis, kui te neid ei kasuta. Tänu sellele toimivad need paremini, kuid see võib mõjutada aku tööiga."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm on määratud"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kaamera ja mikrofon on välja lülitatud"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# märguanne}other{# märguannet}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Edastamine"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Kas peatada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> ülekandmine?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Kui kannate rakendust <xliff:g id="SWITCHAPP">%1$s</xliff:g> üle või muudate väljundit, peatatakse teie praegune ülekanne"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Rakenduse <xliff:g id="SWITCHAPP">%1$s</xliff:g> ülekandmine"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Väljundi muutmine"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Tundmatu"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml
index 29895d1a437a..07eddef9383e 100644
--- a/packages/SystemUI/res/values-et/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Väljas"</item>
<item msgid="460891964396502657">"Sees"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Pole saadaval"</item>
+ <item msgid="8014986104355098744">"Väljas"</item>
+ <item msgid="5966994759929723339">"Sees"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index ce67b7df408b..0c1302ebb982 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Gailua blokeatuta dago"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Aurpegia eskaneatzen"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Bidali"</string>
- <string name="phone_label" msgid="5715229948920451352">"ireki telefonoan"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"ireki ahots-laguntza"</string>
- <string name="camera_label" msgid="8253821920931143699">"ireki kamera"</string>
<string name="cancel" msgid="1089011503403416730">"Utzi"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Berretsi"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Saiatu berriro"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Aktibo dago sentsore guztiak desaktibatzen dituen aukera"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Garbitu jakinarazpen guztiak."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">Beste <xliff:g id="NUMBER_1">%s</xliff:g> jakinarazpen daude barnean.</item>
- <item quantity="one">Beste <xliff:g id="NUMBER_0">%s</xliff:g> jakinarazpen daude barnean.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Beste # jakinarazpen dago barnean.}other{Beste # jakinarazpen daude barnean.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Pantaila horizontalki blokeatuta dago."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Pantaila bertikalki blokeatuta dago."</string>
<string name="dessert_case" msgid="9104973640704357717">"Postreen kutxa"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Biratze automatikoa"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Biratu pantaila automatikoki"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Kokapena"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Pantaila-babeslea"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Kamera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonoa"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Baimenduta"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Wifi-gunea"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Aktibatzen…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Datu-aurrezlea aktibatuta"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d gailu</item>
- <item quantity="one">%d gailu</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# gailu}other{# gailu}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Linterna"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera abian da"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Datu-konexioa"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Sakatu berriro"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Pasatu hatza gora irekitzeko"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Irekitzeko, sakatu desblokeatzeko ikonoa"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Aurpegiaren bidez desblokeatu da. Irekitzeko, sakatu desblokeatzeko ikonoa."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Aurpegiaren bidez desblokeatu da. Sakatu irekitzeko."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Ezagutu da aurpegia. Sakatu irekitzeko."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Saioarekin jarraitu nahi duzu?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Hasi berriro"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Bai, jarraitu"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Gonbidatu modua"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Gonbidatu moduan zaude"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Beste erabiltzaile bat gehituz gero, gonbidatu modua itxiko da eta oraingo gonbidatuentzako saioko aplikazio eta datu guztiak ezabatuko dira."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Erabiltzaile-mugara iritsi zara"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Gehienez, <xliff:g id="COUNT">%d</xliff:g> erabiltzaile gehi ditzakezu.</item>
- <item quantity="one">Erabiltzaile bakarra sor daiteke.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Erabiltzaile bakarra sor daiteke.}other{# erabiltzaile gehi ditzakezu gehienez.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Erabiltzailea kendu nahi duzu?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Erabiltzailearen aplikazio eta datu guztiak ezabatuko dira."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Kendu"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Gogorarazi"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Desegin"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g>z atzeratu da"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d ordu</item>
- <item quantity="one">%d ordu</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minutu</item>
- <item quantity="one">%d minutu</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ordu}=2{# ordu}other{# ordu}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minutu}other{# minutu}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Bateria-aurrezlea"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> botoia"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Hasiera"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alertak"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Pantaila-argazkiak"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Mezu orokorrak"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Zuzeneko aplikazioak"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Konfigurazioa"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Memoria"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Aholkuak"</string>
<string name="instant_apps" msgid="8337185853050247304">"Zuzeneko aplikazioak"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"aldatu"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Gailuak kontrolatzeko widgetak"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Aukeratu aplikazio bat kontrolatzeko aukerak gehitzeko"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> kontrol-aukera gehitu dira.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> kontrol-aukera gehitu da.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Kontrolatzeko # aukera gehitu da.}other{Kontrolatzeko # aukera gehitu dira.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Kenduta"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Gogokoetan dago"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"<xliff:g id="NUMBER">%d</xliff:g>. gogokoa da"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Gehitu lauza"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ez gehitu lauza"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Hautatu erabiltzaile bat"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplikazio aktibo daude</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplikazio aktibo dago</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikazio aktibo dago}other{# aplikazio aktibo daude}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Informazio berria"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktibo dauden aplikazioak"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Aplikazio hauek aktibo daude eta funtzionatzen ari dira, nahiz eta zu haiek erabiltzen ez aritu. Aukera honek haien funtzioa hobetzen du, baina baliteke bateriaren iraupenari ere eragitea."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarma ezarrita dago"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera eta mikrofonoa desaktibatuta daude"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# jakinarazpen}other{# jakinarazpen}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Igortzen"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren audioa igortzeari utzi nahi diozu?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> aplikazioaren audioa igortzen baduzu, edo audio-irteera aldatzen baduzu, une hartako igorpena eten egingo da"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Igorri <xliff:g id="SWITCHAPP">%1$s</xliff:g> aplikazioaren audioa"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Aldatu audio-irteera"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Ezezaguna"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
index baddea16b833..3bf49c8e0c77 100644
--- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Desaktibatuta"</item>
<item msgid="460891964396502657">"Aktibatuta"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Ez dago erabilgarri"</item>
+ <item msgid="8014986104355098744">"Desaktibatuta"</item>
+ <item msgid="5966994759929723339">"Aktibatuta"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index c8cf9696560d..bd0b037bf291 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"دستگاه قفل است"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"درحال اسکن کردن چهره"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ارسال"</string>
- <string name="phone_label" msgid="5715229948920451352">"باز کردن تلفن"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"«دستیار صوتی» را باز کنید"</string>
- <string name="camera_label" msgid="8253821920931143699">"باز کردن دوربین"</string>
<string name="cancel" msgid="1089011503403416730">"لغو"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"تأیید"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"امتحان مجدد"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"«حسگرها خاموش» فعال است"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"پاک کردن تمام اعلان‌ها"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> اعلان دیگر در گروه.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> اعلان دیگر در گروه.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# اعلان دیگر در گروه وجود دارد.}one{# اعلان دیگر در گروه وجود دارد.}other{# اعلان دیگر در گروه وجود دارد.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"صفحه اکنون در حالت افقی قفل است."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"صفحه اکنون در جهت عمودی قفل است."</string>
<string name="dessert_case" msgid="9104973640704357717">"ویترین دسر"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"چرخش خودکار"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"چرخش خودکار صفحه‌نمایش"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"مکان"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"محافظ صفحه"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"دسترسی به دوربین"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"دسترسی به میکروفون"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"دردسترس"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"نقطه اتصال"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"روشن کردن…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"صرفه‌جویی داده روشن است"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">‏%d دستگاه</item>
- <item quantity="other">‏%d دستگاه</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# دستگاه}one{# دستگاه}other{# دستگاه}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"چراغ قوه"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"دوربین درحال استفاده"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"داده تلفن همراه"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"دوباره ضربه بزنید"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"برای باز کردن، انگشتتان را تند به‌بالا بکشید"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"برای باز کردن، نماد قفل‌گشایی را فشار دهید"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"قفلْ با چهره باز شد. برای باز کردن، نماد قفل‌گشایی را فشار دهید."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"قفلْ با چهره باز شد. برای باز کردن، فشار دهید."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"چهره شناسایی شد. برای باز کردن، فشار دهید."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"آیا می‌خواهید جلسه‌تان را ادامه دهید؟"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"شروع مجدد"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"بله، ادامه داده شود"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"حالت مهمان"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"در حالت مهمان هستید"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"با افزودن کاربر جدید، از حالت مهمان خارج خواهید شد و همه برنامه‌ها و داده‌ها از جلسه مهمان کنونی حذف خواهند شد."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"به تعداد مجاز تعداد کاربر رسیده‌اید"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">می‌توانید حداکثر <xliff:g id="COUNT">%d</xliff:g> کاربر اضافه کنید.</item>
- <item quantity="other">می‌توانید حداکثر <xliff:g id="COUNT">%d</xliff:g> کاربر اضافه کنید.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{فقط یک کاربر می‌توان ایجاد کرد.}one{حداکثر # کاربر می‌توانید اضافه کنید.}other{حداکثر # کاربر می‌توانید اضافه کنید.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"کاربر حذف شود؟"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"همه برنامه‌ها و داده‌های این کاربر حذف می‌شود."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"حذف"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"به من یادآوری شود"</string>
<string name="snooze_undo" msgid="2738844148845992103">"واگرد"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> به تعویق افتاد"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">‏%d ساعت</item>
- <item quantity="other">‏%d ساعت</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">‏%d دقیقه</item>
- <item quantity="other">‏%d دقیقه</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ساعت}=2{# ساعت}one{# ساعت}other{# ساعت}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# دقیقه}one{# دقیقه}other{# دقیقه}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"بهینه‌سازی باتری"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"دکمه <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"ابتدا"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"هشدارها"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"باتری"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"نماگرفت‌ها"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"پیام‌های عمومی"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"برنامه‌های فوری"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"راه‌اندازی"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"فضای ذخیره‌سازی"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"نکات"</string>
<string name="instant_apps" msgid="8337185853050247304">"برنامه‌های فوری"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"روشن/ خاموش کردن"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"کنترل‌های دستگاه"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"انتخاب برنامه برای افزودن کنترل‌ها"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> کنترل اضافه شده است.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> کنترل اضافه شده است.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# کنترل اضافه شد.}one{# کنترل اضافه شد.}other{# کنترل اضافه شد.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"حذف شد"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"به موارد دلخواه اضافه شد"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"اضافه‌شده به موارد دلخواه، جایگاه <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"کاشی اضافه شود"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"کاشی اضافه نشود"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"انتخاب کاربر"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> برنامه فعال است</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> برنامه فعال است</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# برنامه فعال است}one{# برنامه فعال است}other{# برنامه فعال است}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"اطلاعات جدید"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"برنامه‌های فعال"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"این برنامه‌ها فعال هستند و اجرا می‌شوند، حتی وقتی که از آن‌ها استفاده نکنید. این کار باعث بهبود عملکرد برنامه‌ها می‌شود، اما بر عمر باتری نیز تأثیر می‌گذارد."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"زنگ ساعت تنظیم شد"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"دوربین و میکروفون خاموش هستند"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# اعلان}one{# اعلان}other{# اعلان}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"همه‌فرستی"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"همه‌فرستی <xliff:g id="APP_NAME">%1$s</xliff:g> متوقف شود؟"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"اگر <xliff:g id="SWITCHAPP">%1$s</xliff:g> را همه‌فرستی کنید یا خروجی را تغییر دهید، همه‌فرستی کنونی متوقف خواهد شد"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"همه‌فرستی <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"تغییر خروجی"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"نامشخص"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
index ecc8d1cc7b13..85f0bfdf86dd 100644
--- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"خاموش"</item>
<item msgid="460891964396502657">"روشن"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"دردسترس نیست"</item>
+ <item msgid="8014986104355098744">"خاموش"</item>
+ <item msgid="5966994759929723339">"روشن"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index b395aac76a8d..329cc44b4e23 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Laite lukittu"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Kasvojen skannaus"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Lähetä"</string>
- <string name="phone_label" msgid="5715229948920451352">"avaa puhelin"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"Avaa ääniapuri"</string>
- <string name="camera_label" msgid="8253821920931143699">"avaa kamera"</string>
<string name="cancel" msgid="1089011503403416730">"Peru"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Vahvista"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Yritä uudelleen"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Anturit pois päältä aktiivinen"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Tyhjennä kaikki ilmoitukset."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">+<xliff:g id="NUMBER_1">%s</xliff:g> ilmoitusta ryhmässä</item>
- <item quantity="one">+<xliff:g id="NUMBER_0">%s</xliff:g> ilmoitus ryhmässä</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{+# ilmoitus ryhmässä.}other{+# ilmoitusta ryhmässä.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Ruutu on lukittu vaakasuuntaan."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Ruutu on lukittu pystysuuntaan."</string>
<string name="dessert_case" msgid="9104973640704357717">"Jälkiruokavitriini"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automaattinen kääntö"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Käännä näyttöä automaattisesti."</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Sijainti"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Näytönsäästäjä"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Pääsy kameraan"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Pääsy mikrofoniin"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Käytettävissä"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Otetaan käyttöön…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Data Saver on käytössä"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d laitetta</item>
- <item quantity="one">%d laite</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# laite}other{# laitetta}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Taskulamppu"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera käytössä"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobiilidata"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Napauta uudelleen"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Avaa pyyhkäisemällä ylös"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Jatka painamalla lukituksen avauskuvaketta."</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Avattu kasvojen avulla. Jatka lukituksen avauskuvakkeella."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Avattu kasvojen avulla. Avaa painamalla."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Kasvot tunnistettu. Avaa painamalla."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Haluatko jatkaa istuntoa?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Aloita alusta"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Kyllä, haluan jatkaa"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Vierastila"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Olet vierastilassa"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Uuden käyttäjän lisääminen poistaa sinut vierastilasta. Kaikki sovellukset ja data poistetaan myös samalla nykyisestä vierailija-käyttökerrasta."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Käyttäjäraja saavutettu"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Voit lisätä korkeintaan <xliff:g id="COUNT">%d</xliff:g> käyttäjää.</item>
- <item quantity="one">Käyttäjiä voi olla vain yksi.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Voit luoda vain yhden käyttäjän.}other{Voit lisätä korkeintaan # käyttäjää.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Poistetaanko käyttäjä?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Kaikki käyttäjän tiedot ja sovellukset poistetaan."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Poista"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Muistuta minua"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Kumoa"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Torkku: <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d tuntia</item>
- <item quantity="one">%d tunti</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minuuttia</item>
- <item quantity="one">%d minuutti</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# tunti}=2{# tuntia}other{# tuntia}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuutti}other{# minuuttia}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Virransäästö"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Painike <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Ilmoitukset"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Akku"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Kuvakaappaukset"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Yleiset viestit"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Määritys"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Tallennustila"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Vihjeet"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"vaihda"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Laitehallinta"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Valitse sovellus lisätäksesi säätimiä"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> säädintä lisätty</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> säädin lisätty</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# säädin lisätty.}other{# säädintä lisätty.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Poistettu"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Lisätty suosikkeihin"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Lisätty suosikkeihin sijalle <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Lisää laatta"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Älä lisää laattaa"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Valitse käyttäjä"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> sovellusta on aktiivisia</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> sovellus on aktiivinen</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# sovellus on aktiivinen}other{# sovellusta aktiivisena}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Uutta tietoa"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktiiviset sovellukset"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Nämä sovellukset ovat aktiivisia ja ne ovat käynnissä, vaikka et käyttäisi niitä. Näin sovellusten toimivuus paranee, mutta se voi vaikutta akunkestoon."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Hälytys asetettu"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera ja mikrofoni ovat pois päältä"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ilmoitus}other{# ilmoitusta}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Lähettää"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Lopetetaanko <xliff:g id="APP_NAME">%1$s</xliff:g>-sovelluksen lähettäminen?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jos lähetät <xliff:g id="SWITCHAPP">%1$s</xliff:g>-sovellusta tai muutat ulostuloa, nykyinen lähetyksesi loppuu"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Lähetä <xliff:g id="SWITCHAPP">%1$s</xliff:g>-sovellusta"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Muuta ulostuloa"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Tuntematon"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"VKP, KKK p"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
index 5844fb9b79c5..1505dc5c06bb 100644
--- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Poissa päältä"</item>
<item msgid="460891964396502657">"Päällä"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Ei saatavilla"</item>
+ <item msgid="8014986104355098744">"Poissa päältä"</item>
+ <item msgid="5966994759929723339">"Päällä"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 4e00125450da..1fc6b8c9ecd9 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Appareil verrouillé"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Numérisation du visage"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Envoyer"</string>
- <string name="phone_label" msgid="5715229948920451352">"Ouvrir le téléphone"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"ouvrir l\'assistance vocale"</string>
- <string name="camera_label" msgid="8253821920931143699">"Ouvrir l\'appareil photo"</string>
<string name="cancel" msgid="1089011503403416730">"Annuler"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmer"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Réessayer"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Option « Capteurs désactivés » active"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Supprimer toutes les notifications"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> autre notification à l\'intérieur.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> autres notifications à l\'intérieur.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# autre notification à l\'intérieur.}one{# autre notification à l\'intérieur.}other{# autres notifications à l\'intérieur.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"L\'écran est verrouillé en mode paysage."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"L\'écran est verrouillé en mode portrait."</string>
<string name="dessert_case" msgid="9104973640704357717">"Vitrine des desserts"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation automatique"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Écran de veille"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Accès à l\'appareil photo"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Accès au micro"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Accessible"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Point d\'accès sans fil"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Activation en cours…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Écon. données activé"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d appareil</item>
- <item quantity="other">%d appareils</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# appareil}one{# appareil}other{# appareils}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lampe de poche"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"L\'appareil photo est en cours d\'utilisation"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Données cellulaires"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Toucher de nouveau"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Balayez l\'écran vers le haut pour ouvrir"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Appuyez sur l\'icône Déverrouiller pour ouvrir"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Déverr. par reconn. faciale. App. sur l\'icône pour ouvrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Déverr. par reconnaissance faciale. Appuyez pour ouvrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Visage reconnu. Appuyez pour ouvrir."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la session?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recommencer"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Oui, continuer"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Mode Invité"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Vous êtes en mode Invité"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Si vous ajoutez un nouvel utilisateur, vous quitterez le mode Invité, et toutes les applications et données de la session d\'invité en cours seront supprimées."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Limite d\'utilisateurs atteinte"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Vous pouvez ajouter jusqu\'à <xliff:g id="COUNT">%d</xliff:g> utilisateur.</item>
- <item quantity="other">Vous pouvez ajouter jusqu\'à <xliff:g id="COUNT">%d</xliff:g> utilisateurs.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Vous ne pouvez créer qu\'un seul utilisateur.}one{Vous pouvez ajouter jusqu\'à # utilisateur.}other{Vous pouvez ajouter jusqu\'à # utilisateurs.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Supprimer l\'utilisateur?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Toutes les applications et les données de cet utilisateur seront supprimées."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Supprimer"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Me rappeler"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Annuler"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Reporté pour <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d heure</item>
- <item quantity="other">%d heures</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d minute</item>
- <item quantity="other">%d minutes</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# heure}=2{# heures}one{# heure}other{# heures}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minute}one{# minute}other{# minutes}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Économiseur de pile"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Bouton <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Accueil"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alertes"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Pile"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Saisies d\'écran"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Messages généraux"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Applications instantanées"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Configuration"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Stockage"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Conseils"</string>
<string name="instant_apps" msgid="8337185853050247304">"Applications instantanées"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"basculer"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Commandes des appareils"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Sélectionnez l\'application pour laquelle ajouter des commandes"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> commande ajoutée.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> commandes ajoutées.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# commande ajoutée.}one{# commande ajoutée.}other{# commandes ajoutées.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Supprimé"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Ajouté aux favoris"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Ajouté aux favoris, en position <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ajouter la tuile"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne pas ajouter de tuile"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Choisir l\'utilisateur"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> application est active</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> applications sont actives</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# application est active}one{# application est active}other{# applications sont actives}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nouvelle information"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applications actives"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applications sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnalité, mais peut également affecter l\'autonomie de la pile."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"L\'alarme a été réglée"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"L\'appareil photo et le micro sont désactivés"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}one{# notification}other{# notifications}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Diffusion en cours…"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Arrêter la diffusion de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si vous diffusez <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou changez la sortie, votre diffusion actuelle s\'arrêtera"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Diffuser <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Changer la sortie"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Inconnue"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
index 9d78e91f0f69..4ec00846283a 100644
--- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Désactivé"</item>
<item msgid="460891964396502657">"Activé"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Non accessible"</item>
+ <item msgid="8014986104355098744">"Désactivé"</item>
+ <item msgid="5966994759929723339">"Activé"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 14f7d06c34d1..79acfdbdb882 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Appareil verrouillé"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Analyse du visage en cours"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Envoyer"</string>
- <string name="phone_label" msgid="5715229948920451352">"ouvrir le téléphone"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"ouvrir l\'assistance vocale"</string>
- <string name="camera_label" msgid="8253821920931143699">"ouvrir l\'appareil photo"</string>
<string name="cancel" msgid="1089011503403416730">"Annuler"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmer"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Réessayer"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Option \"Capteurs désactivés\" active"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Supprimer toutes les notifications"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"<xliff:g id="NUMBER">%s</xliff:g> autres"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> autre notification à l\'intérieur.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> autres notifications à l\'intérieur.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# autre notification dans le groupe.}one{# autre notification dans le groupe.}other{# autres notifications dans le groupe.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"L\'écran est verrouillé en mode paysage."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"L\'écran est verrouillé en mode portrait."</string>
<string name="dessert_case" msgid="9104973640704357717">"Vitrine des desserts"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation automatique"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Économiseur d\'écran"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Accès à l\'appareil photo"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Accès au micro"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponible"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Point d\'accès"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Activation…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Écon. données activé"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d appareil</item>
- <item quantity="other">%d appareils</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# appareil}one{# appareil}other{# appareils}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lampe de poche"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Caméra en cours d\'utilisation"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Données mobiles"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Appuyer à nouveau"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Balayer vers le haut pour ouvrir"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Appuyez sur l\'icône de déverrouillage pour ouvrir"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Déverrouillé par visage. Appuyez sur icône déverrouillage pour ouvrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Déverrouillé par visage. Appuyez pour ouvrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Visage reconnu. Appuyez pour ouvrir."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la dernière session ?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Non, nouvelle session"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Oui, continuer"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Mode Invité"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Vous êtes en mode Invité"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Si vous ajoutez un utilisateur, le mode Invité sera désactivé et toutes les applis et les données de la session Invité actuelle seront supprimées."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Limite nombre utilisateurs atteinte"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Vous pouvez ajouter <xliff:g id="COUNT">%d</xliff:g> profil utilisateur.</item>
- <item quantity="other">Vous pouvez ajouter jusqu\'à <xliff:g id="COUNT">%d</xliff:g> profils utilisateur.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Vous ne pouvez créer qu\'un seul utilisateur.}one{Vous pouvez ajouter # utilisateur.}other{Vous pouvez ajouter jusqu\'à # utilisateurs.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Supprimer l\'utilisateur ?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Toutes les applications et les données de cet utilisateur seront supprimées."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Supprimer"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"M\'envoyer un rappel"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Annuler"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Répétée après <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d heure</item>
- <item quantity="other">%d heures</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d minute</item>
- <item quantity="other">%d minutes</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# heure}=2{# heures}one{# heure}other{# heures}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minute}one{# minute}other{# minutes}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Économiseur de batterie"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Bouton <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Accueil"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alertes"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Batterie"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Captures d\'écran"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Nouveaux messages"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Applis instantanées"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Configurer"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Espace de stockage"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Astuces"</string>
<string name="instant_apps" msgid="8337185853050247304">"Applis instantanées"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"activer/désactiver"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Commandes des appareils"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Sélectionnez l\'appli pour laquelle ajouter des commandes"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> commande ajoutée.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> commandes ajoutées.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# commande ajoutée.}one{# commande ajoutée.}other{# commandes ajoutées.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Supprimé"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Ajouté aux favoris"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Ajouté aux favoris, en position <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ajouter le bloc"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne pas ajouter le bloc"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Choisir l\'utilisateur"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> appli est active</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> applis sont actives</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# appli est active}one{# appli est active}other{# applis sont actives}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nouvelles informations"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applis actives"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applis sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnement, mais peut également affecter l\'autonomie de la batterie."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarme réglée"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Appareil photo et micro désactivés"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}one{# notification}other{# notifications}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Diffusion…"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Arrêter la diffusion de <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si vous diffusez <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou que vous modifiez le résultat, votre annonce actuelle s\'arrêtera"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Diffuser <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Modifier le résultat"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Inconnue"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM j"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"hh:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
index 47fa9c571402..8c6c4f555a5c 100644
--- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Désactivé"</item>
<item msgid="460891964396502657">"Activé"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Indisponible"</item>
+ <item msgid="8014986104355098744">"Désactivé"</item>
+ <item msgid="5966994759929723339">"Activé"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 8e03b128b4fd..e73aa032171a 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Analizando cara"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
- <string name="phone_label" msgid="5715229948920451352">"abrir teléfono"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"abrir asistente de voz"</string>
- <string name="camera_label" msgid="8253821920931143699">"abrir cámara"</string>
<string name="cancel" msgid="1089011503403416730">"Cancelar"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmar"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Tentar de novo"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"A opción Desactivar sensores está activada"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Eliminar todas as notificacións."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"<xliff:g id="NUMBER">%s</xliff:g> máis"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> notificacións máis no grupo.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> notificación máis no grupo.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Hai # notificación máis no grupo.}other{Hai # notificacións máis no grupo.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"A pantalla está bloqueada en orientación horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"A pantalla está bloqueada en orientación vertical."</string>
<string name="dessert_case" msgid="9104973640704357717">"Caixa de sobremesa"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Xirar automaticamente"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Xirar pantalla automaticamente"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localización"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Protector de pantalla"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Acceso á cámara"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Acceso ao micrófono"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Dispoñible"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Zona wifi"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Activando…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Aforro datos activo"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d dispositivos</item>
- <item quantity="one">%d dispositivo</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# dispositivo}other{# dispositivos}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lanterna"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Cámara en uso"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Datos móbiles"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Toca de novo"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Pasa o dedo cara arriba para abrir"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Preme a icona de desbloquear para abrir a porta"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Usouse o desbloqueo facial. Preme a icona de desbloquear."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Usouse o desbloqueo facial. Preme para abrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Recoñeceuse a cara. Preme para abrir."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Queres continuar coa túa sesión?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Comezar de novo"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Si, continuar"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Modo de convidado"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Estás usando o modo de convidado"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ao engadir un usuario novo, sairás do modo de convidado e eliminaranse todas as aplicacións e datos da sesión de convidado actual."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Alcanzouse o límite de usuarios"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Podes engadir ata <xliff:g id="COUNT">%d</xliff:g> usuarios.</item>
- <item quantity="one">Só se pode crear un usuario.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Só se pode crear 1 usuario.}other{Podes engadir # usuarios como máximo.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Queres quitar o usuario?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Eliminaranse todas as aplicacións e os datos deste usuario."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Quitar"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Lembrarme"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Desfacer"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Adiouse <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d horas</item>
- <item quantity="one">%d hora</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minutos</item>
- <item quantity="one">%d minuto</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hora}=2{# horas}other{# horas}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuto}other{# minutos}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Aforro de batería"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Inicio"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alertas"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Batería"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturas de pantalla"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Mensaxes xerais"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Aplicacións Instantáneas"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Configurar"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Almacenamento"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Consellos"</string>
<string name="instant_apps" msgid="8337185853050247304">"Aplicacións Instantáneas"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"activar/desactivar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Control de dispositivos"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Escolle unha aplicación para engadir controis"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">Engadíronse <xliff:g id="NUMBER_1">%s</xliff:g> controis.</item>
- <item quantity="one">Engadiuse <xliff:g id="NUMBER_0">%s</xliff:g> control.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Engadiuse # control.}other{Engadíronse # controis.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Quitouse"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Está entre os controis favoritos"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Está entre os controis favoritos (posición: <xliff:g id="NUMBER">%d</xliff:g>)"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Engadir atallo"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Non engadir atallo"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplicacións activas</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplicación activa</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{Hai # aplicación activa}other{Hai # aplicacións activas}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova información"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplicacións activas"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Estas aplicacións están activas e en execución mesmo cando non as usas. Deste xeito mellórase o seu funcionamento, pero tamén é posible que se vexa afectada a duración da batería."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarma definida"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"A cámara e o micrófono están desactivados"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificación}other{# notificacións}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Difusión"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Queres deixar de emitir contido a través de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se emites contido a través de <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou cambias de saída, a emisión en curso deterase"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitir contido a través de <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Cambiar de saída"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Descoñecida"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
index 229836c76e09..590ec4ac515c 100644
--- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Desactivado"</item>
<item msgid="460891964396502657">"Activado"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Non dispoñible"</item>
+ <item msgid="8014986104355098744">"Desactivado"</item>
+ <item msgid="5966994759929723339">"Activado"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 56737257f2a6..e00f5ff22297 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"ડિવાઇસ લૉક કરેલું છે"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ચહેરો સ્કૅન કરવો"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"મોકલો"</string>
- <string name="phone_label" msgid="5715229948920451352">"ફોન ખોલો"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"વૉઇસ સહાય ખોલો"</string>
- <string name="camera_label" msgid="8253821920931143699">"કૅમેરા ખોલો"</string>
<string name="cancel" msgid="1089011503403416730">"રદ કરો"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"કન્ફર્મ કરો"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"ફરી પ્રયાસ કરો"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"\'સેન્સર બંધ છે\'ની સુવિધા સક્રિય છે"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"બધા સૂચનો સાફ કરો."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> વધુ સૂચના અંદર છે.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> વધુ સૂચના અંદર છે.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{વધુ # નોટિફિકેશન અંદર છે.}one{વધુ # નોટિફિકેશન અંદર છે.}other{વધુ # નોટિફિકેશન અંદર છે.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"સ્ક્રીન લેન્ડસ્કેપ ઓરિએન્ટેશનમાં લૉક કરેલ છે."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"સ્ક્રીન પોટ્રેટ ઓરિએન્ટેશનમાં લૉક કરેલ છે."</string>
<string name="dessert_case" msgid="9104973640704357717">"ડેઝર્ટ કેસ"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ઑટો રોટેટ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ઑટો રોટેટ સ્ક્રીન"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"સ્થાન"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"સ્ક્રીન સેવર"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"કૅમેરાનો ઍક્સેસ"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"માઇકનો ઍક્સેસ"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ઉપલબ્ધ છે"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"હૉટસ્પૉટ"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ચાલુ કરી રહ્યાં છીએ…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"ડેટા સેવર ચાલુ છે"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d ઉપકરણ</item>
- <item quantity="other">%d ઉપકરણો</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# ડિવાઇસ}one{# ડિવાઇસ}other{# ડિવાઇસ}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"ફ્લૅશલાઇટ"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"કૅમેરાનો ઉપયોગ થાય છે"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"મોબાઇલ ડેટા"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"ફરીથી ટૅપ કરો"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ખોલવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"ખોલવા માટે \'અનલૉક કરો\' આઇકન દબાવો"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ચહેરા દ્વારા અનલૉક કર્યું. ખોલવા \'અનલૉક કરો\' આઇકન દબાવો."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ચહેરા દ્વારા અનલૉક કર્યું. ખોલવા માટે દબાવો."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ચહેરો ઓળખ્યો. ખોલવા માટે દબાવો."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"શું તમે તમારું સત્ર ચાલુ રાખવા માંગો છો?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"શરૂ કરો"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"હા, ચાલુ રાખો"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"અતિથિ મોડ"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"તમે અતિથિ મોડમાં છો"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"કોઈ નવા વપરાશકર્તાને ઉમેરવાથી અતિથિ મોડમાંથી નીકળી જવાશે તેમજ હાલના અતિથિ સત્રમાંથી તમામ ઍપ અને ડેટા ડિલીટ થઈ જશે."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"વપરાશકર્તા સંખ્યાની મર્યાદા"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">તમે <xliff:g id="COUNT">%d</xliff:g> વપરાશકર્તા સુધી ઉમેરી શકો છો.</item>
- <item quantity="other">તમે <xliff:g id="COUNT">%d</xliff:g> વપરાશકર્તાઓ સુધી ઉમેરી શકો છો.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{માત્ર એક જ વપરાશકર્તા બનાવી શકાય છે.}one{તમે વધુમાં વધુ # વપરાશકર્તા ઉમેરી શકો છો.}other{તમે વધુમાં વધુ # વપરાશકર્તા ઉમેરી શકો છો.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"વપરાશકર્તાને દૂર કરીએ?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"આ વપરાશકર્તાની તમામ ઍપ્લિકેશનો અને ડેટા કાઢી નાખવામાં આવશે."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"કાઢી નાખો"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"મને યાદ કરાવો"</string>
<string name="snooze_undo" msgid="2738844148845992103">"છેલ્લો ફેરફાર રદ કરો"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> માટે સ્નૂઝ કરો"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d કલાક</item>
- <item quantity="other">%d કલાક</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d મિનિટ</item>
- <item quantity="other">%d મિનિટ</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# કલાક}=2{# કલાક}one{# કલાક}other{# કલાક}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# મિનિટ}one{# મિનિટ}other{# મિનિટ}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"બૅટરી સેવર"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"બટન <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"અલર્ટ"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"બૅટરી"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"સ્ક્રીનશૉટ"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"સામાન્ય સંદેશા"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"સેટઅપ કરો"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"સ્ટોરેજ"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"હિન્ટ"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ટૉગલ કરો"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ડિવાઇસનાં નિયંત્રણો"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"નિયંત્રણો ઉમેરવા માટે ઍપ પસંદ કરો"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> નિયંત્રણ ઉમેર્યું.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> નિયંત્રણો ઉમેર્યા.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# નિયંત્રણ ઉમેર્યું.}one{# નિયંત્રણ ઉમેર્યું.}other{# નિયંત્રણ ઉમેર્યા.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"કાઢી નાખ્યું"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"મનપસંદમાં ઉમેર્યું"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"મનપસંદમાં ઉમેર્યું, સ્થાન <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ટાઇલ ઉમેરો"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ટાઇલ ઉમેરશો નહીં"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"વપરાશકર્તા પસંદ કરો"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> ઍપ સક્રિય છે</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ઍપ સક્રિય છે</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# ઍપ સક્રિય છે}one{# ઍપ સક્રિય છે}other{# ઍપ સક્રિય છે}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"નવી માહિતી"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"સક્રિય ઍપ"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"જ્યારે તમે આ ઍપનો ઉપયોગ ન કરતા હો, ત્યારે પણ તે સક્રિય અને ચાલતી હોય છે. આનાથી તેની કાર્યક્ષમતામાં સુધારો થાય છે, પરંતુ બૅટરીની આવરદાને અસર પણ થઈ શકે છે."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"અલાર્મ સેટ"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"કૅમેરા અને માઇક બંધ છે"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# નોટિફિકેશન}one{# નોટિફિકેશન}other{# નોટિફિકેશન}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"બ્રૉડકાસ્ટ કરી રહ્યાં છે"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> બ્રોડકાસ્ટ કરવાનું રોકીએ?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"જો તમે <xliff:g id="SWITCHAPP">%1$s</xliff:g> બ્રોડકાસ્ટ કરો અથવા આઉટપુટ બદલો, તો તમારું હાલનું બ્રોડકાસ્ટ બંધ થઈ જશે"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> બ્રોડકાસ્ટ કરો"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"આઉટપુટ બદલો"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"અજાણ"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
index c502ba3e1ffa..73b372079ec9 100644
--- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"બંધ છે"</item>
<item msgid="460891964396502657">"ચાલુ છે"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"અનુપલબ્ધ"</item>
+ <item msgid="8014986104355098744">"બંધ છે"</item>
+ <item msgid="5966994759929723339">"ચાલુ છે"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index c0bea8aec635..b74d2874fcad 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"डिवाइस लॉक है"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"डिवाइस अनलॉक करने के लिए चेहरा स्कैन किया जाता है"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"भेजें"</string>
- <string name="phone_label" msgid="5715229948920451352">"फ़ोन खोलें"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"आवाज़ से डिवाइस को इस्तेमाल करें"</string>
- <string name="camera_label" msgid="8253821920931143699">"कैमरा खोलें"</string>
<string name="cancel" msgid="1089011503403416730">"रद्द करें"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"पुष्टि करें"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"फिर से कोशिश करें"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"सेंसर बंद हैं"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"सभी सूचनाएं साफ़ करें."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">इसमें <xliff:g id="NUMBER_1">%s</xliff:g> और सूचनाएं हैं.</item>
- <item quantity="other">इसमें <xliff:g id="NUMBER_1">%s</xliff:g> और सूचनाएं हैं.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{ग्रुप में # सूचना है.}one{ग्रुप में # सूचना है.}other{ग्रुप में # सूचनाएं हैं.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"स्क्रीन लैंडस्केप दिशा में लॉक है."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"स्‍क्रीन पोर्ट्रेट दिशा में लॉक है."</string>
<string name="dessert_case" msgid="9104973640704357717">"मिठाई का डिब्बा"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रीन का अपने-आप दिशा बदलना (ऑटो-रोटेट)"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"जगह"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"स्क्रीन सेवर"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"कैमरे का ऐक्सेस"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"माइक्रोफ़ोन का ऐक्सेस"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"उपलब्ध"</string>
@@ -248,17 +243,14 @@
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"रंग में सुधार करने की सुविधा"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"उपयोगकर्ता सेटिंग"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"हो गया"</string>
- <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"रद्द करें"</string>
+ <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"बंद करें"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"कनेक्ट है"</string>
<string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"कनेक्ट किया गया, बैटरी <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> है"</string>
<string name="quick_settings_connecting" msgid="2381969772953268809">"कनेक्ट हो रहा है..."</string>
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"हॉटस्पॉट"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"हॉटस्पॉट चालू हो रहा है…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"डेटा बचाया जा रहा है"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d डिवाइस</item>
- <item quantity="other">%d डिवाइस</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# डिवाइस}one{# डिवाइस}other{# डिवाइस}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"फ़्लैशलाइट"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"कैमरा इस्तेमाल में है"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"मोबाइल डेटा"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"फिर से टैप करें"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"खोलने के लिए ऊपर स्वाइप करें"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"डिवाइस अनलॉक करने के लिए, अनलॉक आइकॉन को दबाएं"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"चेहरे से अनलॉक किया. डिवाइस अनलॉक करने के लिए, अनलॉक आइकॉन को दबाएं."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"चेहरे से अनलॉक किया गया. डिवाइस खोलने के लिए टैप करें."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"चेहरे की पहचान हो गई. डिवाइस खोलने के लिए टैप करें."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"क्‍या आपको अपना सेशन जारी रखना है?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"फिर से शुरू करें"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"हां, जारी रखें"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"मेहमान मोड"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"आप मेहमान मोड में हैं"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"किसी नए उपयोगकर्ता को जोड़ने पर, मेहमान मोड को बंद कर दिया जाएगा. साथ ही, मेहमान के तौर पर ब्राउज़ करने के मौजूदा सेशन से, सभी ऐप्लिकेशन और डेटा को मिटा दिया जाएगा."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"अब और उपयोगकर्ता नहीं जोड़े जा सकते"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">आप ज़्यादा से ज़्यादा <xliff:g id="COUNT">%d</xliff:g> उपयोगकर्ता जोड़ सकते हैं.</item>
- <item quantity="other">आप ज़्यादा से ज़्यादा <xliff:g id="COUNT">%d</xliff:g> उपयोगकर्ता जोड़ सकते हैं.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{सिर्फ़ एक उपयोगकर्ता खाता जोड़ा जा सकता है.}one{ज़्यादा से ज़्यादा # उपयोगकर्ता खाता जोड़ा जा सकता है.}other{ज़्यादा से ज़्यादा # उपयोगकर्ता खाते जोड़े जा सकते हैं.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"उपयोगकर्ता निकालें?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"इस उपयोगकर्ता के सभी ऐप और डेटा को हटा दिया जाएगा."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"हटाएं"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"बाद में याद दिलाएं"</string>
<string name="snooze_undo" msgid="2738844148845992103">"पहले जैसा करें"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> के लिए याद दिलाया गया"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d घंटे</item>
- <item quantity="other">%d घंटे</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d मिनट</item>
- <item quantity="other">%d मिनट</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# घंटा}=2{# घंटे}one{# घंटा}other{# घंटे}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# मिनट}one{# मिनट}other{# मिनट}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"बैटरी सेवर"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"बटन <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"सूचनाएं"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"बैटरी"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"स्‍क्रीनशॉट"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"सामान्य संदेश"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"सेट अप"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"स्टोरेज"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"संकेत"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"टॉगल करें"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"डिवाइस कंट्रोल"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"कंट्रोल जोड़ने के लिए ऐप्लिकेशन चुनें"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> कंट्रोल जोड़ा गया.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> कंट्रोल जोड़े गए.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# कंट्रोल जोड़ा गया.}one{# कंट्रोल जोड़ा गया.}other{# कंट्रोल जोड़े गए.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"हटाया गया"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"पसंदीदा बनाया गया"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"पसंदीदा बनाया गया, क्रम संख्या <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"टाइल जोड़ें"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"टाइल न जोड़ें"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"उपयोगकर्ता चुनें"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> ऐप्लिकेशन चालू है</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ऐप्लिकेशन चालू हैं</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# ऐप्लिकेशन चालू है}one{# ऐप्लिकेशन चालू है}other{# ऐप्लिकेशन चालू हैं}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"नई जानकारी"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ये ऐप्लिकेशन चालू हैं"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ये ऐप्लिकेशन चालू हैं और आपके इस्तेमाल न करने पर भी चल रहे हैं. इससे, ये बेहतर तरीके से फ़ंक्शन करते हैं. हालांकि, इससे बैटरी लाइफ़ पर भी असर पड़ सकता है."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"अलार्म सेट किया गया"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"कैमरा और माइक बंद हैं"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# सूचना}one{# सूचना}other{# सूचनाएं}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ब्रॉडकास्ट ऐप्लिकेशन"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर ब्रॉडकास्ट करना रोकें?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> पर ब्रॉडकास्ट शुरू करने पर या आउटपुट बदलने पर, आपका मौजूदा ब्रॉडकास्ट बंद हो जाएगा"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> पर ब्रॉडकास्ट करें"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"आउटपुट बदलें"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"कोई जानकारी नहीं"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
index 9af07bc65b8c..a156b0c43ca6 100644
--- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"बंद है"</item>
<item msgid="460891964396502657">"चालू है"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"उपलब्ध नहीं है"</item>
+ <item msgid="8014986104355098744">"बंद है"</item>
+ <item msgid="5966994759929723339">"चालू है"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 1ddf76775762..823d2f495d2a 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Uređaj je zaključan"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skeniranje lica"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Pošalji"</string>
- <string name="phone_label" msgid="5715229948920451352">"otvaranje telefona"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"otvaranje glasovne pomoći"</string>
- <string name="camera_label" msgid="8253821920931143699">"otvaranje fotoaparata"</string>
<string name="cancel" msgid="1089011503403416730">"Odustani"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potvrdi"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Pokušaj ponovo"</string>
@@ -205,11 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Senzori isključeni aktivno"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Brisanje svih obavijesti."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"još <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">U skupini je još <xliff:g id="NUMBER_1">%s</xliff:g> obavijest.</item>
- <item quantity="few">U skupini su još <xliff:g id="NUMBER_1">%s</xliff:g> obavijesti.</item>
- <item quantity="other">U skupini je još <xliff:g id="NUMBER_1">%s</xliff:g> obavijesti.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{U grupi je još # obavijest.}one{U grupi je još # obavijest.}few{U grupi su još # obavijesti.}other{U grupi je još # obavijesti.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Zaslon je zaključan u pejzažnoj orijentaciji."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Zaslon je zaključan u portretnoj orijentaciji."</string>
<string name="dessert_case" msgid="9104973640704357717">"Izlog za slastice"</string>
@@ -227,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. zakretanje"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko zakretanje zaslona"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Čuvar zaslona"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Pristup fotoaparatu"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Pristup mikrofonu"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Dostupno"</string>
@@ -256,11 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Žarišna točka"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Uključivanje…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Štednja pod. prom. uklj."</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d uređaj</item>
- <item quantity="few">%d uređaja</item>
- <item quantity="other">%d uređaja</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# uređaj}one{# uređaj}few{# uređaja}other{# uređaja}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Svjetiljka"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Upotrebljava se kamera"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobilni podaci"</string>
@@ -317,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Dodirnite ponovo"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Prijeđite prstom prema gore da biste otvorili"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Pritisnite ikonu otključavanja da biste otvorili"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Otključano pomoću lica. Pritisnite ikonu otključavanja da biste otvorili."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Otključano pomoću lica. Pritisnite da biste otvorili."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Lice je prepoznato. Pritisnite da biste otvorili."</string>
@@ -353,12 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, nastavi"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Način rada za goste"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Upotrebljavate način rada za goste"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ako dodate novog korisnika, napustit ćete način rada za goste i izbrisat će se svi podaci i aplikacije iz trenutačne gostujuće sesije."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Dosegnuto je ograničenje korisnika"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
- <item quantity="few">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
- <item quantity="other">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Može se izraditi samo jedan korisnik.}one{Možete dodati najviše # korisnika.}few{Možete dodati najviše # korisnika.}other{Možete dodati najviše # korisnika.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Ukloniti korisnika?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Izbrisat će se sve aplikacije i podaci ovog korisnika."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Ukloni"</string>
@@ -544,16 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Podsjeti me"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Poništi"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Odgođeno <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d sat</item>
- <item quantity="few">%d sata</item>
- <item quantity="other">%d sati</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d minuta</item>
- <item quantity="few">%d minute</item>
- <item quantity="other">%d minuta</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# h}=2{# h}one{# h}few{# h}other{# h}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# min}one{# min}few{# min}other{# min}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Štednja baterije"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Tipka <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Početak"</string>
@@ -702,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Upozorenja"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Baterija"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Snimke zaslona"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Općenite poruke"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant aplikacije"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Postavljanje"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Pohrana"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Savjeti"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant aplikacije"</string>
@@ -776,11 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"promijeni"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Odabir aplikacije za dodavanje kontrola"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one">Dodana je <xliff:g id="NUMBER_1">%s</xliff:g> kontrola.</item>
- <item quantity="few">Dodane su <xliff:g id="NUMBER_1">%s</xliff:g> kontrole.</item>
- <item quantity="other">Dodano je <xliff:g id="NUMBER_1">%s</xliff:g> kontrola.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Dodana je # kontrola.}one{Dodana je # kontrola.}few{Dodane su # kontrole.}other{Dodano je # kontrola.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Uklonjeno"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Dodano u favorite"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Dodano u favorite, položaj <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -937,11 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj pločicu"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nemoj dodati pločicu"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Odabir korisnika"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> aplikacija je aktivna</item>
- <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> aplikacije su aktivne</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplikacija je aktivno</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikacija je aktivna}one{# aplikacija je aktivna}few{# aplikacije su aktivne}other{# aplikacija je aktivno}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nove informacije"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivne aplikacije"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Te su aplikacije aktivne i pokrenute čak i kad ih ne koristite. Time se poboljšava njihova funkcionalnost, ali to može utjecati na trajanje baterije."</string>
@@ -970,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm je postavljen"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Fotoaparat i mikrofon su isključeni"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# obavijest}one{# obavijest}few{# obavijesti}other{# obavijesti}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitiranje"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zaustaviti emitiranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ako emitirate aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g> ili promijenite izlaz, vaše će se trenutačno emitiranje zaustaviti"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitiranje aplikacije <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Promjena izlaza"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nepoznato"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE., d. MMM."</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
index a057c48bbec9..b69b06419281 100644
--- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Isključeno"</item>
<item msgid="460891964396502657">"Uključeno"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Nedostupno"</item>
+ <item msgid="8014986104355098744">"Isključeno"</item>
+ <item msgid="5966994759929723339">"Uključeno"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 531fcf06d405..12e2d70de609 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Az eszköz zárolva van"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Arc keresése"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Küldés"</string>
- <string name="phone_label" msgid="5715229948920451352">"telefon megnyitása"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"hangsegéd megnyitása"</string>
- <string name="camera_label" msgid="8253821920931143699">"kamera megnyitása"</string>
<string name="cancel" msgid="1089011503403416730">"Mégse"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Megerősítés"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Újrapróbálkozás"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Az Érzékelők kikapcsolva kártya aktív"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Minden értesítés törlése"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> további értesítés.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> további értesítés.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# további értesítés a csoportban.}other{# további értesítés a csoportban.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"A képernyő zárolva van fekvő tájolásban."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"A képernyő zárolva van álló tájolásban."</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatikus elforgatás"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatikus képernyőforgatás"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Tartózkodási hely"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Képernyővédő"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Hozzáférés a kamerához"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonelérés"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Rendelkezésre áll"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Bekapcsolás…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Aktív adatcsökkentés"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d eszköz</item>
- <item quantity="one">%d eszköz</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# eszköz}other{# eszköz}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Zseblámpa"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"A kamera használatban van"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobiladatok"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Koppintson újra"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Csúsztasson felfelé a megnyitáshoz"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Az eszköz használatához nyomja meg a feloldás ikonját"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Arccal feloldva. A megnyitáshoz nyomja meg a feloldás ikont."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Zárolás arccal feloldva. Koppintson az eszköz használatához."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Arc felismerve. Koppintson az eszköz használatához."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Folytatja a munkamenetet?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Újrakezdés"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Igen, folytatom"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Vendég mód"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Vendég módban van"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Új felhasználó felvételével kilép a vendég módból, és az aktuális vendégmunkamenetből származó összes alkalmazás és adat törlődik majd."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Maximális felhasználószám elérve"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Legfeljebb <xliff:g id="COUNT">%d</xliff:g> felhasználót adhat hozzá.</item>
- <item quantity="one">Csak egy felhasználót lehet létrehozni.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Csak egy felhasználót lehet létrehozni.}other{Legfeljebb # felhasználót adhat hozzá.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Törli a felhasználót?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"A felhasználóhoz tartozó minden adat és alkalmazás törölve lesz."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Eltávolítás"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Emlékeztessen"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Visszavonás"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Elhalasztva: <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d óra</item>
- <item quantity="one">%d óra</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d perc</item>
- <item quantity="one">%d perc</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# óra}=2{# óra}other{# óra}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# perc}other{# perc}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Akkumulátorkímélő mód"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> gomb"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Kezdőképernyő"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Értesítések"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Akkumulátor"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Képernyőképek"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Általános üzenetek"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Azonnali alkalmazások"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Beállítás"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Tárhely"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Tippek"</string>
<string name="instant_apps" msgid="8337185853050247304">"Azonnali alkalmazások"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"váltás"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Eszközvezérlők"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Válasszon alkalmazást a vezérlők hozzáadásához"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> vezérlő hozzáadva.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> vezérlő hozzáadva.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# vezérlő hozzáadva.}other{# vezérlő hozzáadva.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Eltávolítva"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Hozzáadva a kedvencekhez"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Hozzáadva a kedvencekhez <xliff:g id="NUMBER">%d</xliff:g>. helyen"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Mozaik hozzáadása"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne legyen hozzáadva"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Felhasználóválasztás"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> alkalmazás aktív</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> alkalmazás aktív</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# alkalmazás aktív}other{# alkalmazás aktív}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Új információ"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktív alkalmazások"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ezek az alkalmazások aktívak és futnak, még akkor is, amikor nem használja őket. Ez javítja a működésüket, de hatással lehet az akkumulátor-élettartamra is."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Ébresztő beállítva"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"A kamera és a mikrofon ki vannak kapcsolva"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# értesítés}other{# értesítés}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Sugárzás"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Leállítja a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> közvetítését?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"A(z) <xliff:g id="SWITCHAPP">%1$s</xliff:g> közvetítése vagy a kimenet módosítása esetén a jelenlegi közvetítés leáll"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> közvetítése"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Kimenet módosítása"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Ismeretlen"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, HHH n"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"ó:pp"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"óó:pp"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
index 47109a334e6d..050bc14d54ff 100644
--- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Ki"</item>
<item msgid="460891964396502657">"Be"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Nem áll rendelkezésre"</item>
+ <item msgid="8014986104355098744">"Ki"</item>
+ <item msgid="5966994759929723339">"Be"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 7f2d5778bcbb..c702969c9058 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Սարքը կողպված է"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Դեմքի սկանավորում"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Ուղարկել"</string>
- <string name="phone_label" msgid="5715229948920451352">"բացել հեռախոսը"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"բացեք ձայնային հուշումը"</string>
- <string name="camera_label" msgid="8253821920931143699">"բացել ֆոտոխցիկը"</string>
<string name="cancel" msgid="1089011503403416730">"Չեղարկել"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Հաստատել"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Նորից փորձել"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Տվիչներն անջատված են"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Մաքրել բոլոր ծանուցումները:"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">Ներսում ևս <xliff:g id="NUMBER_1">%s</xliff:g> ծանուցում կա:</item>
- <item quantity="other">Ներսում ևս <xliff:g id="NUMBER_1">%s</xliff:g> ծանուցում կա:</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Խմբում ևս # ծանուցում կա։}one{Խմբում ևս # ծանուցում կա։}other{Խմբում ևս # ծանուցում կա։}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Էկրանը կողպված է հորիզոնական դիրքավորման մեջ:"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Էկրանը կողպված է ուղղաձիգ դիրքավորմամբ:"</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Ինքնապտտում"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ավտոմատ պտտել էկրանը"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Տեղորոշում"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Էկրանապահ"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Տեսախցիկի հասանելիություն"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Խոսափողի հասանելիություն"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Հասանելի է"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Թեժ կետ"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Միացում…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Թրաֆիկը տնտեսվում է"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d սարք</item>
- <item quantity="other">%d սարք</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# սարք}one{# սարք}other{# սարք}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Լապտեր"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Օգտագործվում է տեսախցիկը"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Բջջային ինտերնետ"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Նորից հպեք"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Բացելու համար սահեցրեք վերև"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Բացեք՝ սեղմելով ապակողպման պատկերակը"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Ապակողպվել է դեմքով։ Բացեք՝ սեղմելով ապակողպման պատկերակը։"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Ապակողպվել է դեմքով։ Սեղմեք բացելու համար։"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Դեմքը ճանաչվեց։ Սեղմեք բացելու համար։"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Շարունակե՞լ աշխատաշրջանը։"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Վերսկսել"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Այո, շարունակել"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Հյուրի ռեժիմ"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Դուք հյուրի ռեժիմում եք"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ավելացնելով նոր օգտատեր՝ դուք դուրս կգաք հյուրի ռեժիմից։ Հյուրի ընթացիկ աշխատաշրջանի բոլոր հավելվածներն ու տվյալները կջնջվեն։"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Սահմանաչափը սպառված է"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Հնարավոր է ավելացնել առավելագույնը <xliff:g id="COUNT">%d</xliff:g> օգտատեր։</item>
- <item quantity="other">Հնարավոր է ավելացնել առավելագույնը <xliff:g id="COUNT">%d</xliff:g> օգտատեր։</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Հնարավոր է ստեղծել միայն մեկ օգտատեր։}one{Կարող եք առավելագույնը # օգտատեր ավելացնել։}other{Կարող եք առավելագույնը # օգտատեր ավելացնել։}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Հեռացնե՞լ օգտատիրոջը:"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Այս օգտատիրոջ բոլոր հավելվածներն ու տվյալները կջնջվեն:"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Հեռացնել"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Հիշեցնել ինձ"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Հետարկել"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Հետաձգվել է <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>ով"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d ժամ</item>
- <item quantity="other">%d ժամ</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d րոպե</item>
- <item quantity="other">%d րոպե</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ժամ}=2{# ժամ}one{# ժամ}other{# ժամ}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# րոպե}one{# րոպե}other{# րոպե}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Մարտկոցի տնտեսում"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> կոճակ"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Գլխավոր էջ"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Ծանուցումներ"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Մարտկոց"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Սքրինշոթներ"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Ընդհանուր հաղորդագրություններ"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Ակնթարթային հավելվածներ"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Կարգավորում"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Տարածք"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Հուշումներ"</string>
<string name="instant_apps" msgid="8337185853050247304">"Ակնթարթային հավելվածներ"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"միացնել/անջատել"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Սարքերի կառավարման տարրեր"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Ընտրեք հավելված` կառավարման տարրեր ավելացնելու համար"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one">Ավելացվեց կառավարման <xliff:g id="NUMBER_1">%s</xliff:g> տարր։</item>
- <item quantity="other">Ավելացվեց կառավարման <xliff:g id="NUMBER_1">%s</xliff:g> տարր։</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Ավելացվեց կառավարման # տարր։}one{Ավելացվեց կառավարման # տարր։}other{Ավելացվեց կառավարման # տարր։}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Հեռացված է"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Ավելացված է ընտրանիում"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Ավելացված է ընտրանիում, դիրքը՝ <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ավելացնել սալիկ"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Չավելացնել սալիկ"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Ընտրեք օգտատեր"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> հավելված ակտիվ է</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> հավելված ակտիվ է</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{Ակտիվ է # հավելված}one{Ակտիվ է # հավելված}other{Ակտիվ է # հավելված}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Նոր տեղեկություն"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Ակտիվ հավելվածներ"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Այս հավելվածներն ակտիվ են և աշխատում են, նույնիսկ երբ դուք չեք օգտագործում դրանք։ Դա բարելավում է հավելվածների գործառույթները, սակայն կարող է նաև ազդել մարտկոցի աշխատաժամանակի վրա։"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Զարթուցիչը դրված է"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Տեսախցիկը և խոսափողն անջատված են"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ծանուցում}one{# ծանուցում}other{# ծանուցում}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Հեռարձակում"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Կանգնեցնել <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի հեռարձակումը"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Եթե հեռարձակեք <xliff:g id="SWITCHAPP">%1$s</xliff:g> հավելվածը կամ փոխեք աուդիո ելքը, ձեր ընթացիկ հեռարձակումը կկանգնեցվի։"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Հեռարձակել <xliff:g id="SWITCHAPP">%1$s</xliff:g> հավելվածը"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Փոխել աուդիո ելքը"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Անհայտ"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
index a78b7a10e58e..6015fbd75b3c 100644
--- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Անջատված է"</item>
<item msgid="460891964396502657">"Միացված է"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Հասանելի չէ"</item>
+ <item msgid="8014986104355098744">"Անջատված է"</item>
+ <item msgid="5966994759929723339">"Միացված է"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index d49215985deb..e52d08d7f721 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Perangkat terkunci"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Memindai wajah"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Kirim"</string>
- <string name="phone_label" msgid="5715229948920451352">"buka ponsel"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"buka bantuan suara"</string>
- <string name="camera_label" msgid="8253821920931143699">"buka kamera"</string>
<string name="cancel" msgid="1089011503403416730">"Batal"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Konfirmasi"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Coba lagi"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensor nonaktif diaktifkan"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Menghapus semua pemberitahuan."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> notifikasi lainnya di dalam.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> notifikasi lainnya di dalam.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# notifikasi lainnya di dalam.}other{# notifikasi lainnya di dalam.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Layar dikunci dalam orientasi lanskap."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Layar dikunci dalam orientasi potret."</string>
<string name="dessert_case" msgid="9104973640704357717">"Etalase Hidangan Penutup"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Putar Otomatis"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Putar layar otomatis"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screensaver"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Akses kamera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Akses mikrofon"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tersedia"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Mengaktifkan…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Penghemat Data aktif"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d perangkat</item>
- <item quantity="one">%d perangkat</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# perangkat}other{# perangkat}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lampu Senter"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera sedang digunakan"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Data seluler"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Ketuk lagi"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Geser ke atas untuk membuka"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Tekan ikon buka kunci untuk membuka"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Kunci dibuka dengan wajah. Tekan ikon buka kunci untuk membuka."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Kunci dibuka dengan wajah. Tekan untuk membuka."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Wajah dikenali. Tekan untuk membuka."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Lanjutkan sesi Anda?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulai ulang"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ya, lanjutkan"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Mode tamu"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Anda sedang menggunakan mode tamu"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Menambahkan pengguna baru akan membuat Anda keluar dari mode tamu dan menghapus semua aplikasi serta data dari sesi tamu saat ini."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Batas pengguna tercapai"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Anda dapat menambahkan hingga <xliff:g id="COUNT">%d</xliff:g> pengguna.</item>
- <item quantity="one">Hanya dapat membuat satu pengguna.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Hanya dapat membuat satu pengguna.}other{Anda dapat menambahkan maksimal # pengguna.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Hapus pengguna?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Semua aplikasi dan data pengguna ini akan dihapus."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Hapus"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Ingatkan saya"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Urungkan"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Ditunda selama <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d jam</item>
- <item quantity="one">%d jam</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d menit</item>
- <item quantity="one">%d menit</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# jam}=2{# jam}other{# jam}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# menit}other{# menit}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Penghemat Baterai"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Tombol <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Notifikasi"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Baterai"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshot"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Pesan Umum"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Aplikasi Instan"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Penyiapan"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Penyimpanan"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Petunjuk"</string>
<string name="instant_apps" msgid="8337185853050247304">"Aplikasi Instan"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"alihkan"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrol perangkat"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Pilih aplikasi untuk menambahkan kontrol"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> kontrol ditambahkan.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> kontrol ditambahkan.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontrol ditambahkan.}other{# kontrol ditambahkan.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Dihapus"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Difavoritkan"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Difavoritkan, posisi <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tambahkan kartu"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Jangan tambah kartu"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pilih pengguna"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplikasi aktif</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplikasi aktif</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikasi aktif}other{# aplikasi aktif}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Informasi baru"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplikasi aktif"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Aplikasi ini aktif dan berjalan, meski Anda tidak sedang menggunakannya. Hal ini akan meningkatkan fungsi aplikasi, tetapi juga dapat memengaruhi masa pakai baterai."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm disetel"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera dan mikrofon nonaktif"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notifikasi}other{# notifikasi}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Menyiarkan"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hentikan siaran <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jika Anda menyiarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g> atau mengubah output, siaran saat ini akan dihentikan"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Siarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Ubah output"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Tidak diketahui"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml
index 6f81b29990cd..b8eb2f7d654e 100644
--- a/packages/SystemUI/res/values-in/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Nonaktif"</item>
<item msgid="460891964396502657">"Aktif"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Tidak tersedia"</item>
+ <item msgid="8014986104355098744">"Nonaktif"</item>
+ <item msgid="5966994759929723339">"Aktif"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 387bdd7fbad5..e8d2d866aec3 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Tækið er læst"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Andlit skannað"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Senda"</string>
- <string name="phone_label" msgid="5715229948920451352">"opna síma"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"opna raddaðstoð"</string>
- <string name="camera_label" msgid="8253821920931143699">"opna myndavél"</string>
<string name="cancel" msgid="1089011503403416730">"Hætta við"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Staðfesta"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Reyna aftur"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Slökkt á skynjurum valið"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Hreinsa allar tilkynningar."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> tilkynning í viðbót.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> tilkynningar í viðbót.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# tilkynning í viðbót.}one{# tilkynning í viðbót.}other{# tilkynningar í viðbót.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Skjárinn er læstur í langsniði."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Skjárinn er læstur í skammsniði."</string>
<string name="dessert_case" msgid="9104973640704357717">"Eftirréttaborð"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Sjálfvirkur snúningur"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Snúa skjá sjálfkrafa"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Staðsetning"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Skjávari"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Aðgangur að myndavél"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Aðgangur að hljóðnema"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tiltækt"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Heitur reitur"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Kveikir…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Gagnasparnaður á"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d tæki</item>
- <item quantity="other">%d tæki</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# tæki}one{# tæki}other{# tæki}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Vasaljós"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Myndavél í notkun"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Farsímagögn"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Ýttu aftur"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Strjúktu upp til að opna"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Ýttu á táknið til að taka úr lás til að opna"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Opnað með andliti. Ýttu á táknið taka úr lás til að opna."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Opnað með andliti. Ýttu til að opna."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Andlitið var greint. Ýttu til að opna."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Viltu halda áfram með lotuna?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Byrja upp á nýtt"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Já, halda áfram"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Gestastilling"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Þú ert í gestastillingu"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Þegar nýjum notanda er bætt við er gestastillingu lokað og öllum forritum og gögnum úr núverandi gestalotu eytt."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Notandahámarki náð"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Þú getur bætt við allt að <xliff:g id="COUNT">%d</xliff:g> notanda.</item>
- <item quantity="other">Þú getur bætt við allt að <xliff:g id="COUNT">%d</xliff:g> notendum.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Aðeins er hægt að stofna einn notanda.}one{Þú getur bætt við allt að # notanda.}other{Þú getur bætt við allt að # notendum.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Fjarlægja notandann?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Öllum forritum og gögnum þessa notanda verður eytt."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Fjarlægja"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Minna mig á"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Afturkalla"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Þaggað í <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d klukkustund</item>
- <item quantity="other">%d klukkustundir</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d mínúta</item>
- <item quantity="other">%d mínútur</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# klukkustund}=2{# klukkustundir}one{# klukkustund}other{# klukkustundir}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# mínúta}one{# mínúta}other{# mínútur}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Rafhlöðusparnaður"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Hnappur <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Tilkynningar"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Rafhlaða"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Skjámyndir"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Almenn skilaboð"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Skyndiforrit"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Uppsetning"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Geymslurými"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Vísbendingar"</string>
<string name="instant_apps" msgid="8337185853050247304">"Skyndiforrit"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"kveikja/slökkva"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Tækjastjórnun"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Veldu forrit til að bæta við stýringum"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> stýringu bætt við.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> stýringum bætt við.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# stýringu bætt við.}one{# stýringu bætt við.}other{# stýringum bætt við.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Fjarlægt"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Eftirlæti"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Eftirlæti, staða <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Bæta reit við"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ekki bæta reit við"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Velja notanda"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> forrit virkt</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> forrit virk</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# forrit virkt}one{# forrit virkt}other{# forrit virk}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nýjar upplýsingar"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Virk forrit"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Þessi forrit eru virk og í gangi jafnvel þótt þú sért ekki að nota þau. Þetta bætir virkni þeirra en gæti einnig haft áhrif á rafhlöðuendingu."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Vekjari stilltur"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Slökkt á myndavél og hljóðnema"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# tilkynning}one{# tilkynning}other{# tilkynningar}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Útsending í gangi"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hætta að senda út <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ef þú sendir út <xliff:g id="SWITCHAPP">%1$s</xliff:g> eða skiptir um úttak lýkur yfirstandandi útsendingu"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Senda út <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Skipta um úttak"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Óþekkt"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"k:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml
index 29bce8257913..12dd776a357c 100644
--- a/packages/SystemUI/res/values-is/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Slökkt"</item>
<item msgid="460891964396502657">"Kveikt"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Ekki í boði"</item>
+ <item msgid="8014986104355098744">"Slökkt"</item>
+ <item msgid="5966994759929723339">"Kveikt"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index f3ff5efe0e06..5d318bfb1adf 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloccato"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scansione del viso"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Invia"</string>
- <string name="phone_label" msgid="5715229948920451352">"apri telefono"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"apri Voice Assist"</string>
- <string name="camera_label" msgid="8253821920931143699">"apri fotocamera"</string>
<string name="cancel" msgid="1089011503403416730">"Annulla"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Conferma"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Riprova"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Opzione Sensori disattivati attiva"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Cancella tutte le notifiche."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">Altre <xliff:g id="NUMBER_1">%s</xliff:g> notifiche nel gruppo.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> altra notifica nel gruppo.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# altra notifica nel gruppo.}other{Altre # notifiche nel gruppo.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Lo schermo è bloccato in orientamento orizzontale."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Lo schermo è bloccato in orientamento verticale."</string>
<string name="dessert_case" msgid="9104973640704357717">"Vetrina di dolci"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotazione automatica"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotazione automatica dello schermo"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Geolocalizzazione"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Salvaschermo"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Accesso alla fotocamera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Accesso al microfono"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponibile"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Attivazione…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Risp. dati attivo"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d dispositivi</item>
- <item quantity="one">%d dispositivo</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# dispositivo}other{# dispositivi}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Torcia"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Fotocamera in uso"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Dati mobili"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Tocca di nuovo"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Scorri verso l\'alto per aprire"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Premi l\'icona Sblocca per aprire"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Sbloccato con il volto. Premi l\'icona Sblocca per aprire."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Sbloccato con il volto. Premi per aprire."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Volto riconosciuto. Premi per aprire."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Vuoi continuare la sessione?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Ricomincia"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sì, continua"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Modalità Ospite"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Sei in modalità Ospite"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Se aggiungi un nuovo utente, la modalità Ospite viene disattivata e vengono eliminati tutti i dati e le app della sessione Ospite corrente."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Limite di utenti raggiunto"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Puoi aggiungere fino a <xliff:g id="COUNT">%d</xliff:g> utenti.</item>
- <item quantity="one">È possibile creare un solo utente.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Può essere creato un solo utente.}other{Puoi aggiungere fino a # utenti.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Rimuovere l\'utente?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Tutte le app e i dati di questo utente verranno eliminati."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Rimuovi"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Ricordamelo"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Annulla"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Posticipato di <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d ore</item>
- <item quantity="one">%d ora</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minuti</item>
- <item quantity="one">%d minuto</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ora}=2{# ore}other{# ore}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuto}other{# minuti}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Risparmio energetico"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Pulsante <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home page"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Avvisi"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Batteria"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshot"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Messaggi generali"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"App istantanee"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Configurazione"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Spazio di archiviazione"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Suggerimenti"</string>
<string name="instant_apps" msgid="8337185853050247304">"App istantanee"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"attiva/disattiva"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controllo dispositivi"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Scegli un\'app per aggiungere controlli"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> controlli aggiunti.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> controllo aggiunto.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# controllo aggiunto.}other{# controlli aggiunti.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Rimosso"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Aggiunto ai preferiti"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Preferito, posizione <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Aggiungi riquadro"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Non aggiungerlo"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleziona utente"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> app attive</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> app attiva</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{C\'è # app attiva}other{Ci sono # app attive}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nuove informazioni"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"App attive"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Queste app sono attive e in esecuzione, anche quando non le utilizzi. Questo migliora la loro funzionalità, ma influisce sulla durata della batteria."</string>
@@ -963,4 +946,16 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Sveglia impostata"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Fotocamera e microfono non attivi"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notifica}other{# notifiche}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Trasmissione in corso…"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vuoi interrompere la trasmissione dell\'app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se trasmetti l\'app <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambi l\'uscita, la trasmissione attuale viene interrotta"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Trasmetti l\'app <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Cambia uscita"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string>
+ <!-- no translation found for dream_date_complication_date_format (8191225366513860104) -->
+ <skip />
+ <!-- no translation found for dream_time_complication_12_hr_time_format (4691197486690291529) -->
+ <skip />
+ <!-- no translation found for dream_time_complication_24_hr_time_format (6248280719733640813) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml
index 757a866463e6..5ec557bbaf7d 100644
--- a/packages/SystemUI/res/values-it/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Off"</item>
<item msgid="460891964396502657">"On"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Non disponibile"</item>
+ <item msgid="8014986104355098744">"Off"</item>
+ <item msgid="5966994759929723339">"On"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 58e452376fc6..25e08c634b6b 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"המכשיר נעול"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"סורק פנים"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"שליחה"</string>
- <string name="phone_label" msgid="5715229948920451352">"פתיחת הטלפון"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"פתיחת האסיסטנט"</string>
- <string name="camera_label" msgid="8253821920931143699">"פתיחת המצלמה"</string>
<string name="cancel" msgid="1089011503403416730">"ביטול"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"אישור"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"ניסיון נוסף"</string>
@@ -205,12 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"ההגדרה \'חיישנים כבויים\' פעילה"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"הסרת כל ההתראות."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="two">עוד <xliff:g id="NUMBER_1">%s</xliff:g> התראות נוספות.</item>
- <item quantity="many">עוד <xliff:g id="NUMBER_1">%s</xliff:g> התראות.</item>
- <item quantity="other">עוד <xliff:g id="NUMBER_1">%s</xliff:g> התראות נוספות.</item>
- <item quantity="one">יש התראה נוספת.<xliff:g id="NUMBER_0">%s</xliff:g>.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{עוד התראה אחת (#) בקבוצה.}two{עוד # התראות בקבוצה.}many{עוד # התראות בקבוצה.}other{עוד # התראות בקבוצה.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"המסך נעול עכשיו לרוחב."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"המסך נעול כעת לאורך."</string>
<string name="dessert_case" msgid="9104973640704357717">"מזנון קינוחים"</string>
@@ -228,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"סיבוב אוטומטי"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"סיבוב אוטומטי של המסך"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"מיקום"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"שומר מסך"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"גישה למצלמה"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"גישה למיקרופון"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"יש גישה"</string>
@@ -257,12 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"‏נקודת אינטרנט (hotspot)"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ההפעלה מתבצעת…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"חוסך הנתונים פועל"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="two">‏%d מכשירים</item>
- <item quantity="many">‏%d מכשירים</item>
- <item quantity="other">‏%d מכשירים</item>
- <item quantity="one">מכשיר אחד</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{מכשיר אחד (#)}two{# מכשירים}many{# מכשירים}other{# מכשירים}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"פנס"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"המצלמה בשימוש"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"חבילת גלישה"</string>
@@ -319,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"צריך להקיש פעם נוספת"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"צריך להחליק כדי לפתוח"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"לפתיחה, לוחצים על סמל ביטול הנעילה"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"הנעילה בוטלה בזיהוי פנים. פותחים בלחיצה על סמל ביטול הנעילה."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"הנעילה בוטלה באמצעות זיהוי הפנים. יש ללחוץ כדי לפתוח."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"הפנים זוהו. יש ללחוץ כדי לפתוח."</string>
@@ -355,13 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"האם ברצונך להמשיך בפעילות באתר?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"סשן חדש"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"כן, להמשיך"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"מצב אורח"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"התחברת במצב אורח"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"הוספת משתמש חדש תגרום ליציאה ממצב האורח ותמחק את כל האפליקציות והנתונים מהגלישה הנוכחית כאורח."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"הגעת למגבלת המשתמשים שניתן להוסיף"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="two">ניתן להוסיף עד <xliff:g id="COUNT">%d</xliff:g> משתמשים.</item>
- <item quantity="many">ניתן להוסיף עד <xliff:g id="COUNT">%d</xliff:g> משתמשים.</item>
- <item quantity="other">ניתן להוסיף עד <xliff:g id="COUNT">%d</xliff:g> משתמשים.</item>
- <item quantity="one">ניתן ליצור רק משתמש אחד.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{ניתן ליצור רק משתמש אחד.}two{אפשר להוסיף עד # משתמשים.}many{אפשר להוסיף עד # משתמשים.}other{אפשר להוסיף עד # משתמשים.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"להסיר את המשתמש?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"כל האפליקציות והנתונים של המשתמש הזה יימחקו."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"הסרה"</string>
@@ -547,18 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"אשמח לקבל תזכורת"</string>
<string name="snooze_undo" msgid="2738844148845992103">"ביטול"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"נדחה לטיפול בעוד <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="two">שעתיים</item>
- <item quantity="many">‏%d שעות</item>
- <item quantity="other">‏%d שעות</item>
- <item quantity="one">שעה</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="two">‏%d דקות</item>
- <item quantity="many">‏%d דקות</item>
- <item quantity="other">‏%d דקות</item>
- <item quantity="one">דקה</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{שעה}=2{שעתיים}many{# שעות}other{# שעות}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{דקה}two{# דקות}many{# דקות}other{# דקות}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"חיסכון בסוללה"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"לחצן <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"דף הבית"</string>
@@ -707,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"התראות"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"סוללה"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"צילומי מסך"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"הודעות כלליות"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"אפליקציות ללא התקנה"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"הגדרה"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"אחסון"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"טיפים"</string>
<string name="instant_apps" msgid="8337185853050247304">"אפליקציות ללא התקנה"</string>
@@ -781,12 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"החלפת מצב"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"פקדי מכשירים"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"יש לבחור אפליקציה כדי להוסיף פקדים"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="two">נוספו <xliff:g id="NUMBER_1">%s</xliff:g> פקדים.</item>
- <item quantity="many">נוספו <xliff:g id="NUMBER_1">%s</xliff:g> פקדים.</item>
- <item quantity="other">נוספו <xliff:g id="NUMBER_1">%s</xliff:g> פקדים.</item>
- <item quantity="one">נוסף פקד אחד (<xliff:g id="NUMBER_0">%s</xliff:g>).</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{נוסף אמצעי בקרה אחד (#).}two{נוספו # אמצעי בקרה.}many{נוספו # אמצעי בקרה.}other{נוספו # אמצעי בקרה.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"הוסר"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"סומן כמועדף"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"סומן כמועדף, במיקום <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -943,12 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"הוספת אריח"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"לא להוסיף אריח"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"בחירת משתמש"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="two"><xliff:g id="COUNT_1">%s</xliff:g> אפליקציות פעילות</item>
- <item quantity="many"><xliff:g id="COUNT_1">%s</xliff:g> אפליקציות פעילות</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> אפליקציות פעילות</item>
- <item quantity="one">אפליקציה אחת (<xliff:g id="COUNT_0">%s</xliff:g>) פעילה</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{אפליקציה אחת (#) פעילה}two{# אפליקציות פעילות}many{# אפליקציות פעילות}other{# אפליקציות פעילות}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"מידע חדש"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"אפליקציות פעילות"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"האפליקציות האלה פעילות גם כשלא משתמשים בהן. הפעולה של האפליקציות משפרת את הפונקציונליות שלהן, אבל היא עשויה גם להשפיע על חיי הסוללה."</string>
@@ -977,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ההתראה מוגדרת"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"המצלמה והמיקרופון כבויים"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{התראה אחת}two{# התראות}many{# התראות}other{# התראות}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"שידור"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"האם להפסיק לשדר את התוכן מאפליקציית <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"אם משדרים את התוכן מאפליקציית <xliff:g id="SWITCHAPP">%1$s</xliff:g> או משנים את הפלט, השידור הנוכחי יפסיק לפעול"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"שידור תוכן מאפליקציית <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"שינוי הפלט"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"לא ידוע"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"‏יום EEE,‏ d בMMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
index 46be20c1f64d..91577b81a5d3 100644
--- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"כבוי"</item>
<item msgid="460891964396502657">"פועל"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"לא זמין"</item>
+ <item msgid="8014986104355098744">"כבוי"</item>
+ <item msgid="5966994759929723339">"מופעל"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 827accab4493..69a959268d7f 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"デバイスはロックされています"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"顔のスキャン"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"送信"</string>
- <string name="phone_label" msgid="5715229948920451352">"電話を起動"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"音声アシストを開く"</string>
- <string name="camera_label" msgid="8253821920931143699">"カメラを起動"</string>
<string name="cancel" msgid="1089011503403416730">"キャンセル"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"確認"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"再試行"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"センサー OFF: 有効"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"通知をすべて消去。"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"他 <xliff:g id="NUMBER">%s</xliff:g> 件"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">他 <xliff:g id="NUMBER_1">%s</xliff:g> 件の通知</item>
- <item quantity="one">他 <xliff:g id="NUMBER_0">%s</xliff:g> 件の通知</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{グループ内にあと # 件の通知があります。}other{グループ内にあと # 件の通知があります。}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"画面は横向きにロックされています。"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"画面は縦向きにロックされています。"</string>
<string name="dessert_case" msgid="9104973640704357717">"デザートケース"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動回転"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"画面を自動回転します"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"位置情報"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"スクリーン セーバー"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"カメラへのアクセス"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"マイクへのアクセス"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"使用可能"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"アクセスポイント"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ON にしています…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"データセーバー ON"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d 台のデバイス</item>
- <item quantity="one">%d 台のデバイス</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# 台のデバイス}other{# 台のデバイス}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"ライト"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"カメラを使用中"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"モバイルデータ"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"もう一度タップしてください"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"開くには上にスワイプします"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"ロック解除アイコンを押して開きます"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"顔でロック解除しました。アイコンを押すと開きます。"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"顔でロック解除しました。押すと開きます。"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"顔を認識しました。押すと開きます。"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"セッションを続行しますか?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"最初から開始"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"続行"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"ゲストモード"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"ゲストモード使用中"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"新しいユーザーを追加するとゲストモードは終了し、現在のゲスト セッションからすべてのアプリとデータが削除されます。"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"ユーザー数が上限に達しました"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">最大 <xliff:g id="COUNT">%d</xliff:g> 人のユーザーを追加できます。</item>
- <item quantity="one">作成できるユーザーは 1 人のみです。</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{作成できるユーザーは 1 人のみです。}other{最大 # 人のユーザーを追加できます。}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"ユーザーを削除しますか?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"このユーザーのアプリとデータがすべて削除されます。"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"削除"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"リマインダーの設定"</string>
<string name="snooze_undo" msgid="2738844148845992103">"元に戻す"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"スヌーズ: <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d時間</item>
- <item quantity="one">%d時間</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d分</item>
- <item quantity="one">%d分</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# 時間}=2{# 時間}other{# 時間}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# 分}other{# 分}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"バッテリー セーバー"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> ボタン"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"アラート"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"バッテリー"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"スクリーンショット"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"一般メッセージ"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"セットアップ"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"ストレージ"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"ヒント"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"切り替え"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"デバイス コントロール"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"コントロールを追加するアプリの選択"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> 件のコントロールを追加しました。</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> 件のコントロールを追加しました。</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# 件のコントロールを追加しました。}other{# 件のコントロールを追加しました。}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"削除済み"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"お気に入りに追加済み"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"お気に入りに追加済み、位置: <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"タイルを追加"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"タイルを追加しない"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ユーザーの選択"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> 個のアプリが実行中です</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> 個のアプリが実行中です</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# 個のアプリがアクティブです}other{# 個のアプリがアクティブです}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"最新情報"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"実行中のアプリ"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ユーザーが使用していない状態でもアクティブで実行中のアプリの一覧です。機能面は向上しますが、バッテリー駆動時間に影響する可能性があります。"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"アラームを設定しました"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"カメラとマイクが OFF です"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 件の通知}other{# 件の通知}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ブロードキャスト"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> のブロードキャストを停止しますか?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> をブロードキャストしたり、出力を変更したりすると、現在のブロードキャストが停止します。"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> をブロードキャスト"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"出力を変更"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"不明"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
index fd966da986b8..c2a3321dc19f 100644
--- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"OFF"</item>
<item msgid="460891964396502657">"ON"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"使用不可"</item>
+ <item msgid="8014986104355098744">"OFF"</item>
+ <item msgid="5966994759929723339">"ON"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 9ad4aa5df1da..fc1ecdac36ee 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"მოწყობილობა ჩაკეტილია"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"მიმდინარეობს სახის სკანირება"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"გაგზავნა"</string>
- <string name="phone_label" msgid="5715229948920451352">"ტელეფონის გახსნა"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"ხმოვანი დახმარების გახსნა"</string>
- <string name="camera_label" msgid="8253821920931143699">"კამერის გახსნა"</string>
<string name="cancel" msgid="1089011503403416730">"გაუქმება"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"დადასტურება"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"ხელახლა ცდა"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"სენსორების გამორთვა აქტიურია"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"ყველა შეტყობინების წაშლა"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">კიდევ <xliff:g id="NUMBER_1">%s</xliff:g> შეტყობინება ჯგუფში.</item>
- <item quantity="one">კიდევ <xliff:g id="NUMBER_0">%s</xliff:g> შეტყობინება ჯგუფში.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{კიდევ # შეტყობინება.}other{კიდევ # შეტყობინება.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"ეკრანი დაბლოკილია თარაზულ ორიენტაციაში"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"ეკრანი დაბლოკილია პორტრეტის ორიენტაციაში."</string>
<string name="dessert_case" msgid="9104973640704357717">"სადესერტო ყუთი"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ავტოროტაცია"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ეკრანის ავტომატური შეტრიალება"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"მდებარეობა"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ეკრანმზოგი"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"კამერაზე წვდომა"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"მიკროფონზე წვდომა"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ხელმისაწვდომი"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"წვდომის წერტილი"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ირთვება…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"მონაცემთა დამზოგველი ჩართულია"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d მოწყობილობა</item>
- <item quantity="one">%d მოწყობილობა</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# მოწყობილობა}other{# მოწყობილობა}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"ფანარი"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"კამერა გამოიყენება"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"მობილური ინტერნეტი"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"შეეხეთ ხელახლა"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"გასახსნელად გადაფურცლეთ ზემოთ"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"გასახსნელად დააჭირეთ განბლოკვის ხატულას"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"განიბლოკა სახით. გასახსნელად დააჭირეთ განბლოკვის ხატულას."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"განიბლოკა სახით. დააჭირეთ გასახსნელად."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ამოცნობილია სახით. დააჭირეთ გასახსნელად."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"გსურთ, თქვენი სესიის გაგრძელება?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ხელახლა დაწყება"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"დიახ, გავაგრძელოთ"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"სტუმრის რეჟიმი"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"თქვენ სტუმრის რეჟიმში ხართ"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"თუ ახალ მომხმარებელს დაამატებთ, სტუმრის რეჟიმი დაიხურება და სტუმრის რეჟიმის მიმდინარე სესიიდან ყველა აპი და მონაცემი წაიშლება."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"მიღწეულია მომხმარებელთა ლიმიტი"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">შესაძლებელია <xliff:g id="COUNT">%d</xliff:g>-მდე მომხმარებლის დამატება.</item>
- <item quantity="one">შესაძლებელია მხოლოდ ერთი მომხმარებლის შექმნა.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{შესაძლებელია მხოლოდ ერთი მომხმარებლის შექმნა.}other{შეგიძლიათ #-მდე მომხმარებლის დამატება.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"გსურთ მომხმარებლის წაშლა?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"ამ მომხმარებლის ყველა აპი და მონაცემი წაიშლება."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"წაშლა"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"შემახსენე"</string>
<string name="snooze_undo" msgid="2738844148845992103">"მოქმედების გაუქმება"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"ჩაჩუმებული იქნება <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d საათი</item>
- <item quantity="one">%d საათი</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d წუთი</item>
- <item quantity="one">%d წუთი</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# საათი}=2{# საათი}other{# საათი}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# წუთი}other{# წუთი}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"ბატარეის დამზოგი"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"ღილაკი „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"გაფრთხილებები"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"ბატარეა"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"ეკრანის ანაბეჭდები"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"ზოგადი შეტყობინებები"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"მყისიერი აპები"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"დაყენება"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"მეხსიერება"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"მინიშნებები"</string>
<string name="instant_apps" msgid="8337185853050247304">"მყისიერი აპები"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"გადართვა"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"მოწყობილ. მართვის საშუალებები"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"აირჩიეთ აპი მართვის საშუალებების დასამატებლად"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">დაემატა <xliff:g id="NUMBER_1">%s</xliff:g> მართვის საშუალება.</item>
- <item quantity="one">დაემატა <xliff:g id="NUMBER_0">%s</xliff:g> მართვის საშუალება.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{დაემატა მართვის # საშუალება.}other{დაემატა მართვის # საშუალება.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"ამოიშალა"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"რჩეულებშია"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"რჩეულებშია, პოზიციაზე <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"დაემატოს"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"არ დაემატოს"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"მომხმარებლის არჩევა"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other">აქტიურია <xliff:g id="COUNT_1">%s</xliff:g> აპი</item>
- <item quantity="one">აქტიურია <xliff:g id="COUNT_0">%s</xliff:g> აპი</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{აქტიურია # აპი}other{აქტიურია # აპი}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"ახალი ინფორმაცია"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"აქტიური აპები"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ეს აპები აქტიურია და გაშვებულია, მაშინაც კი, როცა მათ არ იყენებთ. ეს აუმჯობესებს მათ ფუნქციურობას, მაგრამ შეიძლება ბატარეის მუშაობის ხანგრძლივობაზე იმოქმედოს."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"მაღვიძარა დაყენებულია"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"კამერა და მიკროფონი გამორთულია"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# შეტყობინება}other{# შეტყობინება}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"იწყებთ მაუწყებლობას"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"გსურთ <xliff:g id="APP_NAME">%1$s</xliff:g>-ის ტრანსლაციის შეჩერება?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g>-ის ტრანსლაციის შემთხვევაში ან აუდიოს გამოსასვლელის შეცვლისას, მიმდინარე ტრანსლაცია შეჩერდება"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g>-ის ტრანსლაცია"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"აუდიოს გამოსასვლელის შეცვლა"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"უცნობი"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"დდდ, თთთ თ"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"სთ:წთ"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"სთ:წთ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
index 0c7d5af29890..c95187404d48 100644
--- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"გამორთვა"</item>
<item msgid="460891964396502657">"ჩართვა"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"მიუწვდომელია"</item>
+ <item msgid="8014986104355098744">"გამორთულია"</item>
+ <item msgid="5966994759929723339">"ჩართულია"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 695921f49a07..d47c26df09a8 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Құрылғы құлыпталды."</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Бетті сканерлеу"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Жіберу"</string>
- <string name="phone_label" msgid="5715229948920451352">"телефонды ашу"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"ашық дауыс көмекшісі"</string>
- <string name="camera_label" msgid="8253821920931143699">"камераны ашу"</string>
<string name="cancel" msgid="1089011503403416730">"Бас тарту"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Растау"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Қайталап көріңіз"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Датчиктер өшірулі."</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Барлық хабарларды жойыңыз."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">Ішінде тағы <xliff:g id="NUMBER_1">%s</xliff:g> хабарландыру.</item>
- <item quantity="one">Ішінде тағы <xliff:g id="NUMBER_0">%s</xliff:g> хабарландыру.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Ішінде тағы # хабарландыру бар.}other{Ішінде тағы # хабарландыру бар.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Экран ландшафт бағытында бекітілген."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Экран портрет бағытында бекітілген."</string>
<string name="dessert_case" msgid="9104973640704357717">"Десерт жағдайы"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматты түрде бұру"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматты айналатын экран"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Локация"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Скринсейвер"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Камераны пайдалану"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Микрофонды пайдалану"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Қолжетімді"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Хотспот"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Қосылуда…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Трафикті үнемдеу режимі қосулы"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d құрылғы</item>
- <item quantity="one">%d құрылғы</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# құрылғы}other{# құрылғы}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Қалта шам"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Камера қолданылып жатыр"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Мобильдік деректер"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Қайта түртіңіз."</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Ашу үшін жоғары қарай сырғытыңыз."</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Ашу үшін құлыпты ашу белгішесін басыңыз."</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Бет үлгісі арқылы ашылды. Ашу үшін құлыпты ашу белгішесін басыңыз."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Бетпен ашылды. Ашу үшін басыңыз."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Бет танылды. Ашу үшін басыңыз."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансты жалғастыру керек пе?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Қайта бастау"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Иә, жалғастыру"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Қонақ режимі"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Сіз қонақ режиміндесіз"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Жаңа пайдаланушы қосылған кезде, қонақ режимі жабылады, ағымдағы қонақ сеансындағы барлық қолданба мен дерек жойылады."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Пайдаланушылар саны шегіне жетті"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> пайдаланушыға дейін енгізуге болады.</item>
- <item quantity="one">Тек бір пайдаланушыны жасауға болады.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Тек бір пайдаланушыны жасауға болады.}other{Ең көбі # пайдаланушы қосуға болады.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Пайдаланушы жойылсын ба?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Осы пайдаланушының барлық қолданбалары мен деректері жойылады."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Жою"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Есіме салу"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Қайтару"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> кейінге қалдырылды"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d сағат</item>
- <item quantity="one">%d сағат</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d минут</item>
- <item quantity="one">%d минут</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# сағат}=2{# сағат}other{# сағат}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# минут}other{# минут}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Батареяны үнемдеу режимі"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> түймесі"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Ескертулер"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Батарея"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Скриншоттар"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Жалпы хабарлар"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Реттеу"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Жад"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Кеңестер"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ауыстыру"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Құрылғыны басқару элементтері"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Басқару элементтері қосылатын қолданбаны таңдаңыз"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> басқару элементі енгізілді.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> басқару элементі енгізілді.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# басқару элементі қосылды.}other{# басқару элементі қосылды.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Өшірілді"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Таңдаулыларға қосылды"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Таңдаулыларға қосылды, <xliff:g id="NUMBER">%d</xliff:g>-позиция"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Бөлшек қосу"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Бөлшек қоспау"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Пайдаланушыны таңдау"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> қолданба қосылып тұр.</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> қолданба қосылып тұр.</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# қолданба қосылып тұр.}other{# қолданба қосылып тұр.}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Жаңа ақпарат"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Істеп тұрған қолданбалар"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Бұл қолданбаларды пайдаланбасаңыз да, олар іске қосылып, жұмыс істеп тұрады. Бұл олардың жұмысын жақсартады, алайда батарея жұмысының ұзақтығына да әсер етуі мүмкін."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Оятқыш орнатылды"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камера мен микрофон өшірулі"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# хабарландыру}other{# хабарландыру}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Таратуда"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын таратуды тоқтатасыз ба?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> қолданбасын таратсаңыз немесе аудио шығысын өзгертсеңіз, қазіргі тарату сеансы тоқтайды."</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> қолданбасын тарату"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Аудио шығысын өзгерту"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Белгісіз"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"d MMM EEEE"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
index 546666327e69..c312b4957615 100644
--- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Өшірулі"</item>
<item msgid="460891964396502657">"Қосулы"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Қолжетімді емес."</item>
+ <item msgid="8014986104355098744">"Өшірулі."</item>
+ <item msgid="5966994759929723339">"Қосулы."</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 9870649aaaf5..48fedd222a1f 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"បានចាក់សោ​ឧបករណ៍"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ការ​ស្កេន​មុខ"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ផ្ញើ"</string>
- <string name="phone_label" msgid="5715229948920451352">"បើក​ទូរស័ព្ទ"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"បើកជំនួយសំឡេង"</string>
- <string name="camera_label" msgid="8253821920931143699">"បើក​ម៉ាស៊ីន​ថត"</string>
<string name="cancel" msgid="1089011503403416730">"បោះបង់"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"បញ្ជាក់"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"ព្យាយាម​ម្ដង​ទៀត"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"ឧបករណ៍​ចាប់សញ្ញា​បានបិទ"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"សម្អាត​ការ​ជូន​ដំណឹង​ទាំងអស់។"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">មានការជូនដំណឹង <xliff:g id="NUMBER_1">%s</xliff:g> ទៀតនៅខាងក្នុង</item>
- <item quantity="one">មានការជូនដំណឹង <xliff:g id="NUMBER_0">%s</xliff:g> ទៀតនៅខាងក្នុង</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{មានការជូនដំណឹង # ទៀតនៅខាងក្នុង។}other{មាន​ការ​ជូនដំណឹង # ទៀត​នៅ​ខាង​ក្នុង។}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"អេក្រង់​ជាប់​សោ​ក្នុង​ទិស​ផ្ដេក។"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"បា​ន​ចាក់​សោ​អេក្រង់​​ក្នុង​ទិស​បញ្ឈរ។"</string>
<string name="dessert_case" msgid="9104973640704357717">"ករណី Dessert"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"បង្វិល​ស្វ័យ​ប្រវត្តិ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"បង្វិលអេក្រង់ស្វ័យប្រវត្តិ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ទី​តាំង​"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ធាតុរក្សាអេក្រង់"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"ការចូលប្រើ​កាមេរ៉ា"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"ការចូលប្រើ​មីក្រូហ្វូន"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"អាចចូលប្រើបាន"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"ហតស្ប៉ត"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"កំពុង​បើក..."</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"កម្មវិធីសន្សំសំចៃទិន្នន័យបានបើក"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">ឧបករណ៍ %d</item>
- <item quantity="one">ឧបករណ៍ %d</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{ឧបករណ៍ #}other{ឧបករណ៍ #}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"ពិល"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"កំពុងប្រើ​កាមេរ៉ា"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"ទិន្នន័យ​ទូរសព្ទចល័ត"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"ចុច​ម្ដងទៀត"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"អូសឡើងលើ​ដើម្បីបើក"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"ចុចរូបដោះសោ ដើម្បីបើក"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"បានដោះសោ​ដោយប្រើមុខ។ សូមចុចរូបដោះសោ ដើម្បីបើក។"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"បានដោះសោដោយប្រើមុខ។ សូមចុច ដើម្បីបើក។"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"បានស្គាល់មុខ។ សូមចុច ដើម្បីបើក។"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"តើ​អ្នក​ចង់​បន្ត​វគ្គ​របស់​អ្នក​ទេ?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ចាប់ផ្ដើមសាជាថ្មី"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"បាទ​/ចាស ​បន្ត"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"មុខងារ​ភ្ញៀវ"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"អ្នកស្ថិតនៅក្នុងមុខងារភ្ញៀវ"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ការបញ្ចូលអ្នកប្រើប្រាស់ថ្មីនឹងធ្វើឱ្យចាកចេញពីមុខងារភ្ញៀវ និងលុបកម្មវិធីនិងទិន្នន័យទាំងអស់ចេញពីវគ្គភ្ញៀវបច្ចុប្បន្ន។"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"​បាន​ឈាន​ដល់ចំនួន​កំណត់អ្នកប្រើប្រាស់"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">អ្នកអាចបញ្ចូល​អ្នក​ប្រើប្រាស់បាន​រហូតដល់ <xliff:g id="COUNT">%d</xliff:g> នាក់។</item>
- <item quantity="one">អាច​បង្កើត​អ្នក​ប្រើប្រាស់​បាន​​តែម្នាក់ប៉ុណ្ណោះ។</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{អាច​បង្កើត​អ្នក​ប្រើប្រាស់​បាន​​តែម្នាក់ប៉ុណ្ណោះ។}other{អ្នកអាចបញ្ចូល​អ្នកប្រើប្រាស់​បាន​រហូតដល់ # នាក់។}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"យកអ្នកប្រើចេញ?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"កម្មវិធី និងទិន្នន័យទាំងអស់របស់អ្នកប្រើនេះនឹងត្រូវបានលុប។"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"ដកចេញ"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"រំលឹក​ខ្ញុំ"</string>
<string name="snooze_undo" msgid="2738844148845992103">"ត្រឡប់វិញ"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"បាន​ផ្អាក​រយៈពេល <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d ម៉ោង</item>
- <item quantity="one">%d ម៉ោង</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d នាទី</item>
- <item quantity="one">%d នាទី</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ម៉ោង}=2{# ម៉ោង}other{# ម៉ោង}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# នាទី}other{# នាទី}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"មុខងារ​សន្សំ​ថ្ម"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"ប៊ូតុង <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"ការជូនដំណឹង"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"ថ្ម"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"រូបថត​អេក្រង់"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"សារ​ទូទៅ"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"កម្មវិធី​ប្រើ​ភ្លាមៗ"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"ការរៀបចំ"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"ទំហំផ្ទុក"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"ការ​សម្រួល"</string>
<string name="instant_apps" msgid="8337185853050247304">"កម្មវិធី​ប្រើ​ភ្លាមៗ"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"បិទ/បើក"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ផ្ទាំងគ្រប់គ្រងឧបករណ៍"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"ជ្រើសរើស​កម្មវិធីដែលត្រូវបញ្ចូល​ផ្ទាំងគ្រប់គ្រង"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">បានបញ្ចូល​ការគ្រប់គ្រង <xliff:g id="NUMBER_1">%s</xliff:g>។</item>
- <item quantity="one">បានបញ្ចូល​ការគ្រប់គ្រង <xliff:g id="NUMBER_0">%s</xliff:g>។</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{បានបញ្ចូល​ការគ្រប់គ្រង #។}other{បានបញ្ចូល​ការគ្រប់គ្រង #។}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"បានដកចេញ"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"បានដាក់ជា​សំណព្វ"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"បានដាក់ជា​សំណព្វ ទីតាំង​ទី <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"បញ្ចូល​ប្រអប់"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"កុំ​បញ្ចូល​ប្រអប់"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ជ្រើសរើសអ្នកប្រើប្រាស់"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other">កម្មវិធី <xliff:g id="COUNT_1">%s</xliff:g> កំពុងដំណើរការ</item>
- <item quantity="one">កម្មវិធី <xliff:g id="COUNT_0">%s</xliff:g> កំពុងដំណើរការ</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{កម្មវិធី # កំពុងដំណើរការ}other{កម្មវិធី # កំពុងដំណើរការ}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"ព័ត៌មានថ្មី"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"កម្មវិធីសកម្ម"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"កម្មវិធីទាំងនេះគឺសកម្ម និងកំពុងដំណើរការ ទោះបីជាអ្នកមិនកំពុងប្រើវាក៏ដោយ។ ដំណើរការនេះធ្វើឱ្យមុខងាររបស់កម្មវិធីទាំងនេះប្រសើរឡើង ប៉ុន្តែវាក៏អាចប៉ះពាល់ដល់កម្រិតថាមពលថ្មផងដែរ។"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"រូបកំណត់​ម៉ោងរោទ៍"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"កាមេរ៉ា និង​មីក្រូហ្វូន​ត្រូវបានបិទ"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{ការ​ជូន​ដំណឹង #}other{ការ​ជូនដំណឹង #}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ការផ្សាយ"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"បញ្ឈប់ការផ្សាយ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ប្រសិនបើអ្នក​ផ្សាយ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ឬប្ដូរឧបករណ៍បញ្ចេញសំឡេង ការផ្សាយបច្ចុប្បន្នរបស់អ្នកនឹង​បញ្ឈប់"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"ការផ្សាយ <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ប្ដូរឧបករណ៍បញ្ចេញសំឡេង"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"មិនស្គាល់"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml
index f4830f5bbb75..ec748cfac142 100644
--- a/packages/SystemUI/res/values-km/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"បិទ"</item>
<item msgid="460891964396502657">"បើក"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"មិនមានទេ"</item>
+ <item msgid="8014986104355098744">"បិទ"</item>
+ <item msgid="5966994759929723339">"បើក"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index ca543425f9af..55afb910c775 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"ಸಾಧನ ಲಾಕ್ ಆಗಿದೆ"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ಮುಖವನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ಕಳುಹಿಸಿ"</string>
- <string name="phone_label" msgid="5715229948920451352">"ಫೋನ್ ತೆರೆಯಿರಿ"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"ಧ್ವನಿ ಸಹಾಯಕವನ್ನು ತೆರೆ"</string>
- <string name="camera_label" msgid="8253821920931143699">"ಕ್ಯಾಮರಾ ತೆರೆಯಿರಿ"</string>
<string name="cancel" msgid="1089011503403416730">"ರದ್ದುಮಾಡಿ"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"ದೃಢೀಕರಿಸಿ"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"ಸೆನ್ಸರ್‌ಗಳು ಆಫ್ ಆಗಿವೆ"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"ಎಲ್ಲಾ ಅಧಿಸೂಚನೆಗಳನ್ನು ತೆರವುಗೊಳಿಸು."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ಅಧಿಸೂಚನೆಗಳು ಒಳಗಿವೆ.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ಅಧಿಸೂಚನೆಗಳು ಒಳಗಿವೆ.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{ಇನ್ನೂ # ಅಧಿಸೂಚನೆ ಒಳಗಿದೆ.}one{ಇನ್ನೂ # ಅಧಿಸೂಚನೆಗಳು ಒಳಗಿವೆ.}other{ಇನ್ನೂ # ಅಧಿಸೂಚನೆಗಳು ಒಳಗಿವೆ.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"ಪರದೆಯನ್ನು ಲ್ಯಾಂಡ್‌ಸ್ಕೇಪ್ ಓರಿಯಂಟೇಶನ್‍ನಲ್ಲಿ ಲಾಕ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"ಪರದೆಯನ್ನು ಪೋರ್ಟ್ರೇಟ್ ಓರಿಯಂಟೇಶನ್‍ನಲ್ಲಿ ಲಾಕ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="dessert_case" msgid="9104973640704357717">"ಡೆಸರ್ಟ್ ಕೇಸ್"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ಸ್ವಯಂ-ತಿರುಗುವಿಕೆ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ಪರದೆಯನ್ನು ಸ್ವಯಂ-ತಿರುಗಿಸಿ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ಸ್ಥಳ"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ಸ್ಕ್ರೀನ್ ಸೇವರ್"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"ಕ್ಯಾಮರಾ ಆ್ಯಕ್ಸೆಸ್"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"ಮೈಕ್ ಆ್ಯಕ್ಸೆಸ್"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ಲಭ್ಯವಿದೆ"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"ಹಾಟ್‌ಸ್ಪಾಟ್"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ಆನ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"ಡೇಟಾ ಸೇವರ್ ಆನ್ ಆಗಿದೆ"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d ಸಾಧನಗಳು</item>
- <item quantity="other">%d ಸಾಧನಗಳು</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# ಸಾಧನ}one{# ಸಾಧನಗಳು}other{# ಸಾಧನಗಳು}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"ಫ್ಲಾಶ್‌ಲೈಟ್‌"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"ಕ್ಯಾಮರಾ ಬಳಕೆಯಲ್ಲಿದೆ"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"ಮೊಬೈಲ್ ಡೇಟಾ"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"ಪುನಃ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ತೆರೆಯಲು ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"ತೆರೆಯಲು ಅನ್‌ಲಾಕ್ ಐಕಾನ್ ಅನ್ನು ಒತ್ತಿ"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ಮುಖವನ್ನು ಬಳಸಿ ಅನ್‌ಲಾಕ್ ಮಾಡಲಾಗಿದೆ. ತೆರೆಯಲು ಅನ್‌ಲಾಕ್ ಐಕಾನ್ ಅನ್ನು ಒತ್ತಿ."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ಮುಖವನ್ನು ಬಳಸಿ ಅನ್‌ಲಾಕ್ ಮಾಡಲಾಗಿದೆ. ತೆರೆಯಲು ಒತ್ತಿ."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ಮುಖ ಗುರುತಿಸಲಾಗಿದೆ. ತೆರೆಯಲು ಒತ್ತಿ."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"ನಿಮ್ಮ ಸೆಷನ್‌ ಮುಂದುವರಿಸಲು ಇಚ್ಚಿಸುವಿರಾ?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ಪ್ರಾರಂಭಿಸಿ"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ಹೌದು, ಮುಂದುವರಿಸಿ"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"ಅತಿಥಿ ಮೋಡ್"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"ನೀವು ಅತಿಥಿ ಮೋಡ್‌ನಲ್ಲಿದ್ದೀರಿ"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸುವುದರಿಂದ ಅತಿಥಿ ಮೋಡ್‌ನಿಂದ ನಿರ್ಗಮಿಸುತ್ತದೆ ಮತ್ತು ಪ್ರಸ್ತುತ ಅತಿಥಿ ಸೆಶನ್‌ನಿಂದ ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸುತ್ತದೆ."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"ಬಳಕೆದಾರರ ಮಿತಿ ತಲುಪಿದೆ"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">ನೀವು <xliff:g id="COUNT">%d</xliff:g> ಬಳಕೆದಾರರವರೆಗೆ ಸೇರಿಸಬಹುದು.</item>
- <item quantity="other">ನೀವು <xliff:g id="COUNT">%d</xliff:g> ಬಳಕೆದಾರರವರೆಗೆ ಸೇರಿಸಬಹುದು.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{ಒಬ್ಬ ಬಳಕೆದಾರರನ್ನು ಮಾತ್ರ ರಚಿಸಬಹುದು.}one{ನೀವು # ಬಳಕೆದಾರರವರೆಗೆ ಸೇರಿಸಬಹುದು.}other{ನೀವು # ಬಳಕೆದಾರರವರೆಗೆ ಸೇರಿಸಬಹುದು.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"ಬಳಕೆದಾರರನ್ನು ತೆಗೆದುಹಾಕುವುದೇ?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"ಈ ಬಳಕೆದಾರರ ಎಲ್ಲಾ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುವುದು."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"ತೆಗೆದುಹಾಕಿ"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"ನನಗೆ ಜ್ಞಾಪಿಸಿ"</string>
<string name="snooze_undo" msgid="2738844148845992103">"ರದ್ದುಗೊಳಿಸಿ"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> ಗೆ ಸ್ನೂಜ್ ಮಾಡಲಾಗಿದೆ"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d ಗಂಟೆಗಳು</item>
- <item quantity="other">%d ಗಂಟೆಗಳು</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d ನಿಮಿಷಗಳು</item>
- <item quantity="other">%d ನಿಮಿಷಗಳು</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ಗಂಟೆ}=2{# ಗಂಟೆಗಳು}one{# ಗಂಟೆಗಳು}other{# ಗಂಟೆಗಳು}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# ನಿಮಿಷ}one{# ನಿಮಿಷಗಳು}other{# ನಿಮಿಷಗಳು}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"ಬ್ಯಾಟರಿ ಸೇವರ್‌‌"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> ಬಟನ್"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"ಅಲರ್ಟ್‌ಗಳು"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"ಬ್ಯಾಟರಿ"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್‌ಗಳು"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"ಸಾಮಾನ್ಯ ಸಂದೇಶಗಳು"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"ಸೆಟಪ್"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"ಸಂಗ್ರಹಣೆ"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"ಸುಳಿವುಗಳು"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ಟಾಗಲ್ ಮಾಡಿ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ಸಾಧನ ನಿಯಂತ್ರಣಗಳು"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"ನಿಯಂತ್ರಣಗಳನ್ನು ಸೇರಿಸಲು ಆ್ಯಪ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> ನಿಯಂತ್ರಣಗಳನ್ನು ಸೇರಿಸಲಾಗಿದೆ.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> ನಿಯಂತ್ರಣಗಳನ್ನು ಸೇರಿಸಲಾಗಿದೆ.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# ನಿಯಂತ್ರಣವನ್ನು ಸೇರಿಸಲಾಗಿದೆ.}one{# ನಿಯಂತ್ರಣಗಳನ್ನು ಸೇರಿಸಲಾಗಿದೆ.}other{# ನಿಯಂತ್ರಣಗಳನ್ನು ಸೇರಿಸಲಾಗಿದೆ.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"ಮೆಚ್ಚಲಾಗಿರುವುದು"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"ಮೆಚ್ಚಲಾಗಿರುವುದು, ಸ್ಥಾನ <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಬೇಡಿ"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ಬಳಕೆದಾರರನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> ಆ್ಯಪ್‌ಗಳು ಸಕ್ರಿಯವಾಗಿವೆ</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ಆ್ಯಪ್‌ಗಳು ಸಕ್ರಿಯವಾಗಿವೆ</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# ಆ್ಯಪ್ ಸಕ್ರಿಯವಾಗಿದೆ}one{# ಆ್ಯಪ್‌ಗಳು ಸಕ್ರಿಯವಾಗಿವೆ}other{# ಆ್ಯಪ್‌ಗಳು ಸಕ್ರಿಯವಾಗಿವೆ}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"ಹೊಸ ಮಾಹಿತಿ"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ಸಕ್ರಿಯ ಆ್ಯಪ್‌ಗಳು"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ಈ ಆ್ಯಪ್‌ಗಳನ್ನು ಬಳಸದಿದ್ದರೂ ಸಹ, ಅವುಗಳು ಸಕ್ರಿಯವಾಗಿರುತ್ತವೆ ಮತ್ತು ಚಾಲನೆಯಲ್ಲಿರುತ್ತವೆ. ಇದು ಅವುಗಳ ಫಂಕ್ಷನಾಲಿಟಿಯನ್ನು ಸುಧಾರಿಸುತ್ತದೆ, ಆದರೆ ಇದು ಬ್ಯಾಟರಿ ಬಾಳಿಕೆಯ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರಬಹುದು."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ಅಲಾರಾಂ ಹೊಂದಿಸಲಾಗಿದೆ"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ ಆಫ್ ಆಗಿದೆ"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ಅಧಿಸೂಚನೆ}one{# ಅಧಿಸೂಚನೆಗಳು}other{# ಅಧಿಸೂಚನೆಗಳು}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ಪ್ರಸಾರ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನ ಪ್ರಸಾರವನ್ನು ನಿಲ್ಲಿಸಬೇಕೆ?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ನೀವು <xliff:g id="SWITCHAPP">%1$s</xliff:g> ಅನ್ನು ಪ್ರಸಾರ ಮಾಡಿದರೆ ಅಥವಾ ಔಟ್‌ಪುಟ್ ಅನ್ನು ಬದಲಾಯಿಸಿದರೆ, ನಿಮ್ಮ ಪ್ರಸ್ತುತ ಪ್ರಸಾರವು ಸ್ಥಗಿತಗೊಳ್ಳುತ್ತದೆ"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ಅನ್ನು ಪ್ರಸಾರ ಮಾಡಿ"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ಔಟ್‌ಪುಟ್ ಅನ್ನು ಬದಲಾಯಿಸಿ"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ಅಪರಿಚಿತ"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
index 5cf2f5c2661e..864a607c8a39 100644
--- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"ಆಫ್ ಮಾಡಿ"</item>
<item msgid="460891964396502657">"ಆನ್ ಮಾಡಿ"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"ಲಭ್ಯವಿಲ್ಲ"</item>
+ <item msgid="8014986104355098744">"ಆಫ್ ಮಾಡಿ"</item>
+ <item msgid="5966994759929723339">"ಆನ್ ಮಾಡಿ"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 35fc41182f51..32d345f04532 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"기기 잠김"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"얼굴 스캔 중"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"보내기"</string>
- <string name="phone_label" msgid="5715229948920451352">"휴대전화 열기"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"음성 지원 열기"</string>
- <string name="camera_label" msgid="8253821920931143699">"카메라 열기"</string>
<string name="cancel" msgid="1089011503403416730">"취소"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"확인"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"다시 시도하세요."</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"센서 끄기 활성화"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"모든 알림 지우기"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"<xliff:g id="NUMBER">%s</xliff:g>개 더보기"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g>개 알림 더보기</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g>개 알림 더보기</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{그룹에 알림이 #개 더 있습니다.}other{그룹에 알림이 #개 더 있습니다.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"화면이 가로 방향으로 잠겨 있습니다."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"화면이 세로 방향으로 잠겨 있습니다."</string>
<string name="dessert_case" msgid="9104973640704357717">"디저트 케이스"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"자동 회전"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"화면 자동 회전"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"위치"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"화면 보호기"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"카메라 액세스"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"마이크 액세스"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"사용 가능"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"핫스팟"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"켜는 중..."</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"데이터 절약 모드"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">기기 %d대</item>
- <item quantity="one">기기 %d대</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{기기 #대}other{기기 #대}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"손전등"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"카메라 사용 중"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"모바일 데이터"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"다시 탭하세요."</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"위로 스와이프하여 열기"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"기기를 열려면 잠금 해제 아이콘을 누르세요."</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"얼굴 인식으로 잠금 해제되었습니다. 기기를 열려면 잠금 해제 아이콘을 누르세요."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"얼굴 인식으로 잠금 해제되었습니다. 열려면 누르세요."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"얼굴이 인식되었습니다. 열려면 누르세요."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"세션을 계속 진행하시겠습니까?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"다시 시작"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"계속 진행"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"게스트 모드"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"게스트 모드 사용 중"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"새로운 사용자를 추가하면 게스트 모드가 종료되고 기존 게스트 세션의 모든 앱과 데이터가 삭제됩니다."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"사용자 제한 도달"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">사용자를 <xliff:g id="COUNT">%d</xliff:g>명까지 추가할 수 있습니다.</item>
- <item quantity="one">사용자를 한 명만 만들 수 있습니다.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{사용자는 한 명만 등록할 수 있습니다.}other{사용자는 최대 #명을 등록할 수 있습니다.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"사용자를 삭제할까요?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"이 사용자의 모든 앱과 데이터가 삭제됩니다."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"삭제"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"알림 받기"</string>
<string name="snooze_undo" msgid="2738844148845992103">"실행취소"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> 동안 일시 중지됨"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d시간</item>
- <item quantity="one">%d시간</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d분</item>
- <item quantity="one">%d분</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{#시간}=2{#시간}other{#시간}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{#분}other{#분}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"절전 모드"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> 버튼"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"알림"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"배터리"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"스크린샷"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"일반 메시지"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"인스턴트 앱"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"설정"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"저장용량"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"힌트"</string>
<string name="instant_apps" msgid="8337185853050247304">"인스턴트 앱"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"전환"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"기기 컨트롤"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"컨트롤을 추가할 앱을 선택하세요"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">제어 기능 <xliff:g id="NUMBER_1">%s</xliff:g>개가 추가되었습니다.</item>
- <item quantity="one">제어 기능 <xliff:g id="NUMBER_0">%s</xliff:g>개가 추가되었습니다.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{설정이 #개 추가되었습니다.}other{설정이 #개 추가되었습니다.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"삭제됨"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"즐겨찾기에 추가됨"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"즐겨찾기에 추가됨, 위치 <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"타일 추가"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"타일 추가 안함"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"사용자 선택"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other">활성 상태인 앱 <xliff:g id="COUNT_1">%s</xliff:g>개</item>
- <item quantity="one">활성 상태인 앱 <xliff:g id="COUNT_0">%s</xliff:g>개</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{활성 상태인 앱 #개}other{활성 상태인 앱 #개}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"새로운 정보"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"활성 상태의 앱"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"사용 중이 아닐 때도 활성화되어 실행되는 앱입니다. 이 경우 앱 기능성이 향상되지만 배터리 수명에 영향을 줄 수도 있습니다."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"알람이 설정되었습니다."</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"카메라 및 마이크가 사용 중지되었습니다."</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{알림 #개}other{알림 #개}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"방송 중"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> 방송을 중지하시겠습니까?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> 앱을 방송하거나 출력을 변경하면 기존 방송이 중단됩니다"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> 방송"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"출력 변경"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"알 수 없음"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"MMM d일 EEE"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
index 3244ffaec49c..c52c17cedc32 100644
--- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"꺼짐"</item>
<item msgid="460891964396502657">"켜짐"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"사용 불가"</item>
+ <item msgid="8014986104355098744">"꺼짐"</item>
+ <item msgid="5966994759929723339">"켜짐"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 5cc895da5578..a9670589f0cc 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Түзмөк кулпуланды"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Жүз скандалууда"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Жөнөтүү"</string>
- <string name="phone_label" msgid="5715229948920451352">"телефонду ачуу"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"үн жардамчысысын ачуу"</string>
- <string name="camera_label" msgid="8253821920931143699">"камераны ачуу"</string>
<string name="cancel" msgid="1089011503403416730">"Жок"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Ырастоо"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Кайталоо"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"\"Сенсорлорду өчүрүүнү\" активдештирүү"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Бардык билдирмелерди өчүрүү."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">Дагы <xliff:g id="NUMBER_1">%s</xliff:g> эскертме бар.</item>
- <item quantity="one">Дагы <xliff:g id="NUMBER_0">%s</xliff:g> эскертме бар.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Дагы # билдирме бар.}other{Дагы # билдирме бар.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Экран туурасынан турган бойдон бекитилген."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Экран тикесинен турган бойдон бекитилген."</string>
<string name="dessert_case" msgid="9104973640704357717">"Десерт себети"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Авто буруу"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Экранды авто буруу"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Жайгашкан жер"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Көшөгө"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Камера"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Микрофон"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Жеткиликтүү"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Байланыш түйүнү"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Күйгүзүлүүдө…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Трафикти үнөмдөө күйүк"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d түзмөк</item>
- <item quantity="one">%d түзмөк</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# түзмөк}other{# түзмөк}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Кол чырак"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Камера колдонулууда"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Мобилдик Интернет"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Кайра таптап коюңуз"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Ачуу үчүн өйдө сүрүңүз"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Кулпуну ачуу сүрөтчөсүн басыңыз"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Кулпуну жүзүңүз менен ачтыңыз. Эми кулпуну ачуу сүрөтчөсүн басыңыз."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Кулпуну жүзүңүз менен ачтыңыз. Ачуу үчүн басыңыз."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Жүз таанылды. Ачуу үчүн басыңыз."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансыңызды улантасызбы?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Кайра баштоо"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ооба, уланта берели"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Конок режими"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Конок режиминдесиз"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Жаңы колдонуучуну кошсоңуз, конок режими жабылып, учурдагы конок сеансындагы бардык колдонмолор жана башка нерселер өчүп калат."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Дагы колдонуучу кошууга болбойт"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> колдонуучуга чейин кошууга болот.</item>
- <item quantity="one">Бир колдонуучуну гана кошууга болот.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Бир колдонуучуну гана кошууга болот.}other{# чейин колдонуучу кошсоңуз болот.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Колдонуучу алынып салынсынбы?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Бул колдонуучунун бардык колдонмолору жана маалыматтары өчүрүлөт."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Өчүрүү"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Эскертилсин"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Кайтаруу"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> тындырылды"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d саат</item>
- <item quantity="one">%d саат</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d мүнөт</item>
- <item quantity="one">%d мүнөт</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# саат}=2{# саат}other{# саат}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# мүнөт}other{# мүнөт}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Батареяны үнөмдөгүч"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> баскычы"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Башкы бет"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Эскертүүлөр"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Батарея"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Скриншоттор"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Жалпы билдирүүлөр"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Ыкчам ачылуучу колдонмолор"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Тууралоо"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Сактагыч"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Кеңештер"</string>
<string name="instant_apps" msgid="8337185853050247304">"Ыкчам ачылуучу колдонмолор"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"өчүрүү/күйгүзүү"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Түзмөктү башкаруу элементтери"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Башкаруу элементтери кошула турган колдонмону тандоо"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> башкаруу элементи кошулду.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> башкаруу элементи кошулду.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# көзөмөл кошулду.}other{# көзөмөл кошулду.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Өчүрүлдү"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Сүйүктүүлөргө кошулду"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Сүйүктүүлөргө <xliff:g id="NUMBER">%d</xliff:g>-позицияга кошулду"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ыкчам баскыч кошуу"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ыкчам баскыч кошулбасын"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Колдонуучуну тандоо"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> колдонмо иштеп жатат</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> колдонмо иштеп жатат</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# колдонмо иштеп жатат}other{# колдонмо иштеп жатат}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Жаңы маалымат"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Жигердүү колдонмолор"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Бул колдонмолор жабылып турса да, активдүү болуп, иштеп турушат. Алардын функционалдуулугу жакшырат, бирок батареянын кубатынын мөөнөтүнө кедергиси тийиши мүмкүн."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Ойготкуч коюлду"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камера жана микрофон өчүк"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# билдирме}other{# билдирме}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Кеңири таратуу"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунда кабарлоо токтотулсунбу?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Эгер <xliff:g id="SWITCHAPP">%1$s</xliff:g> колдонмосунда кабарласаңыз же аудионун чыгуусун өзгөртсөңүз, учурдагы кабарлоо токтотулат"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> колдонмосунда кабарлоо"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Аудионун чыгуусун өзгөртүү"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Белгисиз"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
index 5518fccac6ee..f872926aa945 100644
--- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Өчүк"</item>
<item msgid="460891964396502657">"Күйүк"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Жеткиликсиз"</item>
+ <item msgid="8014986104355098744">"Өчүк"</item>
+ <item msgid="5966994759929723339">"Күйүк"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 87ed4087f852..d86198e4d512 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"ອຸປະກອນຖືກລັອກໄວ້"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ການສະແກນໜ້າ"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ສົ່ງ"</string>
- <string name="phone_label" msgid="5715229948920451352">"​ເປີດ​​ແປ້ນ​ໂທ​ລະ​ສັບ"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"ຊ່ວ​ເຫຼືອ​ເປີດ​ສຽງ"</string>
- <string name="camera_label" msgid="8253821920931143699">"ເປີດ​ກ້ອງ"</string>
<string name="cancel" msgid="1089011503403416730">"ຍົກເລີກ"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"ຢືນຢັນ"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"ລອງໃໝ່"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"ປິດການເຮັດວຽກຂອງເຊັນເຊີແລ້ວ"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"ລຶບການແຈ້ງເຕືອນທັງໝົດ."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">ມີ <xliff:g id="NUMBER_1">%s</xliff:g> ການແຈ້ງເຕືອນເພີ່ມເຕີມຢູ່ທາງໃນ.</item>
- <item quantity="one">ມີ <xliff:g id="NUMBER_0">%s</xliff:g> ການແຈ້ງເຕືອນເພີ່ມເຕີມຢູ່ທາງໃນ.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{ມີ # ການແຈ້ງເຕືອນເພີ່ມເຕີມຢູ່ທາງໃນ.}other{ມີ # ການແຈ້ງເຕືອນເພີ່ມເຕີມຢູ່ທາງໃນ.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"ໜ້າຈໍຖືກລັອກໃນລວງນອນ."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"ໜ້າຈໍຖືກລັອກຢູ່ໃນໂໝດແນວຕັ້ງ."</string>
<string name="dessert_case" msgid="9104973640704357717">"ກ່ອງຂອງຫວານ"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ໝຸນ​ອັດ​ຕະ​ໂນ​ມັດ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ໝຸນໜ້າຈໍອັດຕະໂນມັດ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ສະຖານທີ່"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ພາບພັກໜ້າຈໍ"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"ການເຂົ້າເຖິງກ້ອງຖ່າຍຮູບ"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"ການເຂົ້າເຖິງໄມ"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ສາມາດໃຊ້ໄດ້"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"​ຮັອດ​ສະ​ປອດ"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ກຳລັງເປີດ..."</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"ເປີດຕົວປະຢັດອິນເຕີເນັດຢູ່"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d ອຸປະກອນ</item>
- <item quantity="one">%d ອຸປະກອນ</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{ອຸປະກອນ # ເຄື່ອງ}other{ອຸປະກອນ # ເຄື່ອງ}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"​ໄຟ​ສາຍ"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"ມີການໃຊ້ກ້ອງຖ່າຍຮູບຢູ່"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"ອິນເຕີເນັດມືຖື"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"ແຕະອີກເທື່ອໜຶ່ງ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ປັດຂຶ້ນເພື່ອເປີດ"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"ກົດໄອຄອນປົດລັອກເພື່ອເປີດ"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ປົດລັອກດ້ວຍໜ້າແລ້ວ. ກົດໄອຄອນປົດລັອກເພື່ອເປີດ."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ປົດລັອກດ້ວຍໜ້າແລ້ວ. ກົດເພື່ອເປີດ."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ຈຳແນກໜ້າໄດ້ແລ້ວ. ກົດເພື່ອເປີດ."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"ທ່ານ​ຕ້ອງ​ການ​ສືບ​ຕໍ່​ເຊດ​ຊັນ​ຂອງ​ທ່ານບໍ່?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ເລີ່ມຕົ້ນໃຫມ່"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"​ຕົກ​ລົງ, ດຳ​ເນີນ​ການ​ຕໍ່"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"ໂໝດແຂກ"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"ທ່ານກຳລັງຢູ່ໃນໂໝດແຂກ"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ການເພີ່ມຜູ້ໃຊ້ໃໝ່ຈະອອກຈາກໂໝດແຂກ ແລະ ລຶບແອັບ ແລະ ຂໍ້ມູນທັງໝົດອອກຈາກເຊດຊັນແຂກປັດຈຸບັນ."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"ຮອດຂີດຈຳກັດຜູ້ໃຊ້ແລ້ວ"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">ທ່ານສາມາດເພີ່ມໄດ້ສູງສຸດ <xliff:g id="COUNT">%d</xliff:g> ຄົນ.</item>
- <item quantity="one">ສາມາດສ້າງໄດ້ໜຶ່ງຜູ້ໃຊ້ເທົ່ານັ້ນ.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{ສາມາດສ້າງຜູ້ໃຊ້ໄດ້ຄົນດຽວເທົ່ານັ້ນ.}other{ທ່ານສາມາດເພີ່ມຜູ້ໃຊ້ໄດ້ສູງສຸດ # ຄົນ.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"ລຶບຜູ້ໃຊ້ອອກບໍ?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"ທຸກ​ແອັບ ແລະ ຂໍ້​ມູນ​ຂອງ​ຜູ້​ໃຊ້​ນີ້​ຈະ​ຖືກ​ລຶບ."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"ເອົາ​ອອກ"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"ແຈ້ງເຕືອນຂ້ອຍ"</string>
<string name="snooze_undo" msgid="2738844148845992103">"ຍົກເລີກ"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"ເລື່ອນໄປ <xliff:g id="TIME_AMOUNT">%1$s</xliff:g> ນາທີແລ້ວ"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d ຊົ່ວໂມງ</item>
- <item quantity="one">%d ຊົ່ວໂມງ</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d ນາທີ</item>
- <item quantity="one">%d ນາທີ</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ຊົ່ວໂມງ}=2{# ຊົ່ວໂມງ}other{# ຊົ່ວໂມງ}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# ນາທີ}other{# ນາທີ}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"ຕົວປະຢັດ​ແບັດເຕີຣີ"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"ປຸ່ມ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"ການແຈ້ງເຕືອນ"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"ແບັດເຕີຣີ"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"ຮູບຖ່າຍໜ້າຈໍ"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"ຂໍ້ຄວາມທົ່ວໄປ"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"ອິນສະແຕນແອັບ"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"ຕັ້ງຄ່າ"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"ບ່ອນເກັບຂໍ້ມູນ"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"ຄຳໃບ້"</string>
<string name="instant_apps" msgid="8337185853050247304">"ອິນສະແຕນແອັບ"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ສະຫຼັບ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ການຄວບຄຸມອຸປະກອນ"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"ເລືອກແອັບເພື່ອເພີ່ມການຄວບຄຸມ"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">ເພີ່ມ <xliff:g id="NUMBER_1">%s</xliff:g> ການຄວບຄຸມແລ້ວ.</item>
- <item quantity="one">ເພີ່ມ <xliff:g id="NUMBER_0">%s</xliff:g> ການຄວບຄຸມແລ້ວ.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{ເພີ່ມ # ການຄວບຄຸມແລ້ວ.}other{ເພີ່ມ # ການຄວບຄຸມແລ້ວ.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"ລຶບອອກແລ້ວ"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"ເພີ່ມລາຍການທີ່ມັກແລ້ວ"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"ເພີ່ມລາຍການທີ່ມັກແລ້ວ, ຕຳແໜ່ງ <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ເພີ່ມແຜ່ນ"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ຢ່າເພີ່ມແຜ່ນ"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ເລືອກຜູ້ໃຊ້"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ແອັບນຳໃຊ້ຢູ່</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> ແອັບນຳໃຊ້ຢູ່</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{ນຳໃຊ້ຢູ່ # ແອັບ}other{ນຳໃຊ້ຢູ່ # ແອັບ}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"ຂໍ້ມູນໃໝ່"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ແອັບທີ່ນຳໃຊ້ຢູ່"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ແອັບເຫຼົ່ານີ້ແມ່ນເປີດ ແລະ ເອີ້ນໃຊ້ຢູ່, ເຖິງແມ່ນວ່າທ່ານຈະບໍ່ໄດ້ກຳລັງໃຊ້ພວກມັນກໍຕາມ. ນີ້ຈະປັບປຸງການເຮັດວຽກຂອງພວກມັນ, ແຕ່ອາດກະທົບກັບອາຍຸແບັດເຕີຣີໄດ້."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ຕັ້ງໂມງປຸກແລ້ວ"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ປິດກ້ອງຖ່າຍຮູບ ແລະ ໄມແລ້ວ"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ການແຈ້ງເຕືອນ}other{# ການແຈ້ງເຕືອນ}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ກຳລັງອອກອາກາດ"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"ຢຸດການອອກອາກາດ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ຫາກທ່ານອອກອາກາດ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ຫຼື ປ່ຽນເອົ້າພຸດ, ການອອກອາກາດປັດຈຸບັນຂອງທ່ານຈະຢຸດ"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"ອອກອາກາດ <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ປ່ຽນເອົ້າພຸດ"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ບໍ່ຮູ້ຈັກ"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"ຊມ:ນທ"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"ຊມ:ນທ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
index c6b7e6c6e74d..6ae37e472b0c 100644
--- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"ປິດ"</item>
<item msgid="460891964396502657">"ເປີດ"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"ບໍ່ສາມາດໃຊ້ໄດ້"</item>
+ <item msgid="8014986104355098744">"ປິດ"</item>
+ <item msgid="5966994759929723339">"ເປີດ"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index c9aa65c0c999..56bfb2c3713c 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Įrenginys užrakintas"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Nuskaitomas veidas"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Siųsti"</string>
- <string name="phone_label" msgid="5715229948920451352">"atidaryti telefoną"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"atidaryti „Voice Assist“"</string>
- <string name="camera_label" msgid="8253821920931143699">"atidaryti fotoaparatą"</string>
<string name="cancel" msgid="1089011503403416730">"Atšaukti"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Patvirtinkite"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Bandyti dar kartą"</string>
@@ -205,12 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Parinktis „Jutikliai išjungti“ aktyvi"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Išvalyti visus pranešimus."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"Dar <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">Grupėje yra dar <xliff:g id="NUMBER_1">%s</xliff:g> pranešimas.</item>
- <item quantity="few">Grupėje yra dar <xliff:g id="NUMBER_1">%s</xliff:g> pranešimai.</item>
- <item quantity="many">Grupėje yra dar <xliff:g id="NUMBER_1">%s</xliff:g> pranešimo.</item>
- <item quantity="other">Grupėje yra dar <xliff:g id="NUMBER_1">%s</xliff:g> pranešimų.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Grupėje yra dar # pranešimas.}one{Grupėje yra dar # pranešimas.}few{Grupėje yra dar # pranešimai.}many{Grupėje yra dar # pranešimo.}other{Grupėje yra dar # pranešimų.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Užrakintas ekranas yra horizontalios orientacijos."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Užrakintas ekranas yra vertikalios orientacijos."</string>
<string name="dessert_case" msgid="9104973640704357717">"Desertų dėklas"</string>
@@ -228,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatinis pasukimas"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatiškai sukti ekraną"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Vietovė"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Ekrano užsklanda"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Prieiga prie fotoaparato"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Prieiga prie mikrofono"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Pasiekiama"</string>
@@ -257,12 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Viešosios interneto prieigos taškas"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Įjungiama…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Duom. taup. pr. įj."</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d įrenginys</item>
- <item quantity="few">%d įrenginiai</item>
- <item quantity="many">%d įrenginio</item>
- <item quantity="other">%d įrenginių</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# įrenginys}one{# įrenginys}few{# įrenginiai}many{# įrenginio}other{# įrenginių}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Žibintuvėlis"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Fotoaparatas naudojamas"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobiliojo ryšio duomenys"</string>
@@ -319,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Palieskite dar kartą"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Perbraukite aukštyn, kad atidarytumėte"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Paspauskite atrakinimo piktogramą, kad atidarytumėte"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Atrakinta pagal veidą. Pasp. atr. pikt., kad atidarytumėte."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Atrakinta pagal veidą. Paspauskite, kad atidarytumėte."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Veidas atpažintas. Paspauskite, kad atidarytumėte."</string>
@@ -355,13 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Ar norite tęsti sesiją?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Pradėti iš naujo"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Taip, tęsti"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Svečio režimas"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Naudojatės svečio režimu"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Pridėjus naują naudotoją, bus išeita iš svečio režimo ir iš dabartinės svečio sesijos bus ištrintos visos programos ir duomenys."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Pasiekta naudotojų riba"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Galite pridėti iki <xliff:g id="COUNT">%d</xliff:g> naudotojo.</item>
- <item quantity="few">Galite pridėti iki <xliff:g id="COUNT">%d</xliff:g> naudotojų.</item>
- <item quantity="many">Galite pridėti iki <xliff:g id="COUNT">%d</xliff:g> naudotojo.</item>
- <item quantity="other">Galite pridėti iki <xliff:g id="COUNT">%d</xliff:g> naudotojų.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Galima sukurti tik vieną naudotoją.}one{Galite pridėti iki # naudotojo.}few{Galite pridėti iki # naudotojų.}many{Galite pridėti iki # naudotojo.}other{Galite pridėti iki # naudotojų.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Pašalinti naudotoją?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Bus ištrinti visi šio naudotojo duomenys ir programos."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Pašalinti"</string>
@@ -547,18 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Priminti"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Anuliuoti"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Nustatyta snausti <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d valanda</item>
- <item quantity="few">%d valandos</item>
- <item quantity="many">%d valandos</item>
- <item quantity="other">%d valandų</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d minutė</item>
- <item quantity="few">%d minutės</item>
- <item quantity="many">%d minutės</item>
- <item quantity="other">%d minučių</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# valanda}=2{# valandos}one{# valanda}few{# valandos}many{# valandos}other{# valandų}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minutė}one{# minutė}few{# minutės}many{# minutės}other{# minučių}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Akum. taus. pr."</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Mygtukas <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Pagrindinis"</string>
@@ -707,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Įspėjimai"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Akumuliatorius"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Ekrano kopijos"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Bendrieji pranešimai"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Akimirksniu įkeliamos programos"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Sąranka"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Saugykla"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Užuominos"</string>
<string name="instant_apps" msgid="8337185853050247304">"Akimirksniu įkeliamos programos"</string>
@@ -781,12 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"perjungti"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Įrenginio valdikliai"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Pasirinkite programą, kad pridėtumėte valdiklių"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one">Pridėtas <xliff:g id="NUMBER_1">%s</xliff:g> valdiklis.</item>
- <item quantity="few">Pridėti <xliff:g id="NUMBER_1">%s</xliff:g> valdikliai.</item>
- <item quantity="many">Pridėta <xliff:g id="NUMBER_1">%s</xliff:g> valdiklio.</item>
- <item quantity="other">Pridėta <xliff:g id="NUMBER_1">%s</xliff:g> valdiklių.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Pridėtas # valdiklis.}one{Pridėtas # valdiklis.}few{Pridėti # valdikliai.}many{Pridėta # valdiklio.}other{Pridėta # valdiklių.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Pašalinta"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Įtraukta į mėgstamiausius"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Įtraukta į mėgstamiausius, padėtis: <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -943,12 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Pridėti išklotinės elementą"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepridėti išklotinės elemento"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Naudotojo pasirinkimas"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> programa yra aktyvi</item>
- <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> programos yra aktyvios</item>
- <item quantity="many"><xliff:g id="COUNT_1">%s</xliff:g> programos yra aktyvi</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> programų yra aktyvios</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aktyvi programa}one{# aktyvi programa}few{# aktyvios programos}many{# aktyvios programos}other{# aktyvių programų}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nauja informacija"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktyvios programos"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Šios programos yra aktyvios ir vykdomos, net jei jų nenaudojate. Tai atlikus patobulinamos jų funkcijos, bet taip pat gali būti paveiktas ir akumuliatoriaus veikimo laikas."</string>
@@ -977,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Signalas nustatytas"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Vaizdo kamera ir mikrofonas išjungti"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# pranešimas}one{# pranešimas}few{# pranešimai}many{# pranešimo}other{# pranešimų}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transliavimas"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Sustabdyti „<xliff:g id="APP_NAME">%1$s</xliff:g>“ transliaciją?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jei transliuosite „<xliff:g id="SWITCHAPP">%1$s</xliff:g>“ arba pakeisite išvestį, dabartinė transliacija bus sustabdyta"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transliuoti „<xliff:g id="SWITCHAPP">%1$s</xliff:g>“"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Keisti išvestį"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nežinoma"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
index 3e289e1f21ce..03d98c42ff19 100644
--- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Išjungta"</item>
<item msgid="460891964396502657">"Įjungta"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Nepasiekiama"</item>
+ <item msgid="8014986104355098744">"Išjungta"</item>
+ <item msgid="5966994759929723339">"Įjungta"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 7c1e658da448..65e7c5d74b39 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Ierīce ir bloķēta"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Sejas skenēšana"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Sūtīt"</string>
- <string name="phone_label" msgid="5715229948920451352">"atvērt tālruni"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"atvērt balss palīgu"</string>
- <string name="camera_label" msgid="8253821920931143699">"atvērt kameru"</string>
<string name="cancel" msgid="1089011503403416730">"Atcelt"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Apstiprināt"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Mēģināt vēlreiz"</string>
@@ -205,11 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Aktivizēts iestatījums “Sensori izslēgti”"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Notīrīt visus paziņojumus"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"vēl <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="zero">Vēl <xliff:g id="NUMBER_1">%s</xliff:g> paziņojumi grupā.</item>
- <item quantity="one">Vēl <xliff:g id="NUMBER_1">%s</xliff:g> paziņojums grupā.</item>
- <item quantity="other">Vēl <xliff:g id="NUMBER_1">%s</xliff:g> paziņojumi grupā.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Vēl # paziņojums grupā.}zero{Vēl # paziņojumi grupā.}one{Vēl # paziņojums grupā.}other{Vēl # paziņojumi grupā.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Ekrāns tagad ir bloķēts ainavas orientācijā."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Ekrāns tagad ir bloķēts portreta orientācijā."</string>
<string name="dessert_case" msgid="9104973640704357717">"Saldo ēdienu stends"</string>
@@ -227,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automātiska pagriešana"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automātiska ekrāna pagriešana"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Atrašanās vieta"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Ekrānsaudzētājs"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Kamera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofons"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Piekļuve atļauta"</string>
@@ -256,11 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Tīklājs"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Notiek ieslēgšana…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Datu liet. s. iesl."</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="zero">%d ierīču</item>
- <item quantity="one">%d ierīce</item>
- <item quantity="other">%d ierīces</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# ierīce}zero{# ierīču}one{# ierīce}other{# ierīces}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lukturītis"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera tiek lietota"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobilie dati"</string>
@@ -317,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Pieskarieties vēlreiz"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Velciet augšup, lai atvērtu"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Lai atvērtu, nospiediet atbloķēšanas ikonu"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Atbloķēta ar seju. Atvērt: nospiediet atbloķēšanas ikonu."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Ierīce atbloķēta ar seju. Nospiediet, lai atvērtu."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Seja atpazīta. Nospiediet, lai atvērtu."</string>
@@ -353,12 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Vai vēlaties turpināt savu sesiju?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Sākt no sākuma"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Jā, turpināt"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Viesa režīms"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Jūs izmantojat viesa režīmu"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ja pievienosiet jaunu lietotāju, viesa režīms tiks aizvērts un visas pašreizējās viesa sesijas lietotnes un dati tiks dzēsti."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Sasniegts lietotāju ierobežojums"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="zero">Varat pievienot ne vairāk kā <xliff:g id="COUNT">%d</xliff:g> lietotājus.</item>
- <item quantity="one">Varat pievienot ne vairāk kā <xliff:g id="COUNT">%d</xliff:g> lietotāju.</item>
- <item quantity="other">Varat pievienot ne vairāk kā <xliff:g id="COUNT">%d</xliff:g> lietotājus.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Var izveidot tikai vienu lietotāju.}zero{Varat pievienot līdz # lietotājiem.}one{Varat pievienot līdz # lietotājam.}other{Varat pievienot līdz # lietotājiem.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Vai noņemt lietotāju?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Tiks dzēstas visas šī lietotāja lietotnes un dati."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Noņemt"</string>
@@ -544,16 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Atgādināt"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Atsaukt"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Atlikts: <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="zero">%d stundas</item>
- <item quantity="one">%d stunda</item>
- <item quantity="other">%d stundas</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="zero">%d minūtes</item>
- <item quantity="one">%d minūte</item>
- <item quantity="other">%d minūtes</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# stunda}=2{# stundas}zero{# stundu}one{# stunda}other{# stundas}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minūte}zero{# minūšu}one{# minūte}other{# minūtes}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Akumulatora enerģijas taupīšanas režīms"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Poga <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Sākumvietas taustiņš"</string>
@@ -702,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Brīdinājumi"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Akumulators"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Ekrānuzņēmumi"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Vispārīgi ziņojumi"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Tūlītējās lietotnes"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Iestatīšana"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Krātuve"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Padomi"</string>
<string name="instant_apps" msgid="8337185853050247304">"Tūlītējās lietotnes"</string>
@@ -776,11 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"pārslēgt"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Ierīču vadīklas"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Izvēlieties lietotni, lai pievienotu vadīklas"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="zero">Pievienotas <xliff:g id="NUMBER_1">%s</xliff:g> vadīklas.</item>
- <item quantity="one">Pievienota <xliff:g id="NUMBER_1">%s</xliff:g> vadīkla.</item>
- <item quantity="other">Pievienotas <xliff:g id="NUMBER_1">%s</xliff:g> vadīklas.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Pievienota # vadīkla.}zero{Pievienotas # vadīklas.}one{Pievienota # vadīkla.}other{Pievienotas # vadīklas.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Noņemta"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Pievienota izlasei"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Pievienota izlasei, <xliff:g id="NUMBER">%d</xliff:g>. pozīcija"</string>
@@ -937,11 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Pievienot elementu"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepievienot elementu"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Lietotāja atlase"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="zero"><xliff:g id="COUNT_1">%s</xliff:g> lietotnes ir aktīvas</item>
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> lietotne ir aktīva</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> lietotnes ir aktīvas</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# lietotne ir aktīva}zero{# lietotnes ir aktīvas}one{# lietotne ir aktīva}other{# lietotnes ir aktīvas}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Jauna informācija"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktīvās lietotnes"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Šīs lietotnes ir aktīvas un darbojas, pat ja jūs tās neizmantojat. Tas uzlabo lietotņu funkcionalitāti, taču var arī ietekmēt akumulatora darbības ilgumu."</string>
@@ -970,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Signāls ir iestatīts"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera un mikrofons ir izslēgti"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# paziņojums}zero{# paziņojumu}one{# paziņojums}other{# paziņojumi}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Notiek apraidīšana"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vai apturēt lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> apraidīšanu?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ja sāksiet lietotnes <xliff:g id="SWITCHAPP">%1$s</xliff:g> apraidīšanu vai mainīsiet izvadi, pašreizējā apraide tiks apturēta"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Lietotnes <xliff:g id="SWITCHAPP">%1$s</xliff:g> apraide"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Izvades maiņa"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nezināms"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"hh:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
index eb210c24bb50..6e9264d2a347 100644
--- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Izslēgts"</item>
<item msgid="460891964396502657">"Ieslēgts"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Nav pieejams"</item>
+ <item msgid="8014986104355098744">"Izslēgts"</item>
+ <item msgid="5966994759929723339">"Ieslēgts"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 8624604c8f5b..85f9cd5329d0 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Уредот е заклучен"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Скенирање лице"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Испрати"</string>
- <string name="phone_label" msgid="5715229948920451352">"отвори телефон"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"отвори гласовна помош"</string>
- <string name="camera_label" msgid="8253821920931143699">"отвори камера"</string>
<string name="cancel" msgid="1089011503403416730">"Откажи"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Потврди"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Обиди се повторно"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Исклучувањето на сензорите е активно"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Избриши ги сите известувања."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">Уште <xliff:g id="NUMBER_1">%s</xliff:g> известување внатре.</item>
- <item quantity="other">Уште <xliff:g id="NUMBER_1">%s</xliff:g> известувања внатре.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Уште # известување внатре.}one{Уште # известување внатре.}other{Уште # известувања внатре.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Екранот е заклучен во ориентација на пејзаж."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Екранот е заклучен во ориентација на портрет."</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматско ротирање"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматско ротирање на екранот"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Заштитник на екран"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Пристап до камерата"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Пристап до микрофонот"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Дозволен"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Точка на пристап"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Се вклучува…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Вклучен штедач"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d уред</item>
- <item quantity="other">%d уреди</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# уред}one{# уред}other{# уреда}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Светилка"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Камерата е во употреба"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Мобилен интернет"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Допрете повторно"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Повлечете за да отворите"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Притиснете ја иконата за отклучување за да отворите"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Отклучено со лик. Притиснете ја иконата за отклучување за да отворите."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Отклучено со лик. Притиснете за да отворите."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Лицето е препознаено. Притиснете за да отворите."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Дали сакате да продолжите со сесијата?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни одново"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Да, продолжи"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Режим на гостин"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Користите режим на гостин"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ако додадете нов корисник, ќе излезете од режимот на гостин и ќе ги избришете сите апликации и податоци од тековната гостинска сесија."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Достигнато ограничување на корисник"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Може да додадете најмногу <xliff:g id="COUNT">%d</xliff:g> корисник.</item>
- <item quantity="other">Може да додадете најмногу <xliff:g id="COUNT">%d</xliff:g> корисници.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{]Може да се создаде само еден корисник.}one{Може да додадете најмногу # корисник}other{Може да додадете најмногу # корисници}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Да се отстрани корисникот?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Сите апликации и податоци од овој корисник ќе се избришат."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Отстрани"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Потсети ме"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Врати"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Одложено за <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d час</item>
- <item quantity="other">%d часа</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d минута</item>
- <item quantity="other">%d минути</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# час}=2{# часа}one{# час}other{# часа}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# минута}one{# минута}other{# минути}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Штедач на батерија"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Копче <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home-копче"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Известувања"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Батерија"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Слики од екранот"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Општи пораки"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Инстант апликации"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Поставување"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Капацитет"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Совети"</string>
<string name="instant_apps" msgid="8337185853050247304">"Инстант апликации"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"вклучување/исклучување"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Контроли за уредите"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Изберете апликација за да додадете контроли"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one">Додадена е <xliff:g id="NUMBER_1">%s</xliff:g> контрола.</item>
- <item quantity="other">Додадени се <xliff:g id="NUMBER_1">%s</xliff:g> контроли.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Додадена е # контрола.}one{Додадени се # контрола.}other{Додадени се # контроли.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Отстранета"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Омилена"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Омилена, позиција <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Додајте плочка"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не додавајте плочка"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Изберете корисник"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one">Активни се <xliff:g id="COUNT_1">%s</xliff:g> апликација</item>
- <item quantity="other">Активни се <xliff:g id="COUNT_1">%s</xliff:g> апликации</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{Активна е # апликација}one{Активни се # апликација}other{Активни се # апликации}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Нови информации"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активни апликации"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Овие апликации се активни и работат, дури и кога не ги користите. Ова ја подобрува нивната функционалност, но може да влијае и на траењето на батеријата."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Алармот е наместен"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камерата и микрофонот се исклучени"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# известување}one{# известување}other{# известувања}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Емитување"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Да се прекине емитувањето на <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ако емитувате на <xliff:g id="SWITCHAPP">%1$s</xliff:g> или го промените излезот, тековното емитување ќе запре"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Емитување на <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Променете излез"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Непознато"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
index 78be6dda9184..96c8a49ae0b6 100644
--- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Исклучен"</item>
<item msgid="460891964396502657">"Вклучен"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Недостапно"</item>
+ <item msgid="8014986104355098744">"Исклучено"</item>
+ <item msgid="5966994759929723339">"Вклучено"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 30aab4ef1f07..49a807545283 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"ഉപകരണം ലോക്ക് ചെയ്തു"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"മുഖം സ്കാൻ ചെയ്യുന്നു"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"അയയ്ക്കുക"</string>
- <string name="phone_label" msgid="5715229948920451352">"ഫോൺ തുറക്കുക"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"വോയ്‌സ് അസിസ്റ്റ് തുറക്കുക"</string>
- <string name="camera_label" msgid="8253821920931143699">"ക്യാമറ തുറക്കുക"</string>
<string name="cancel" msgid="1089011503403416730">"റദ്ദാക്കുക"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"സ്ഥിരീകരിക്കുക"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"വീണ്ടും ശ്രമിക്കുക"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"സെൻസറുകൾ ഓഫ് സജീവമാണ്"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"എല്ലാ വിവരങ്ങളും മായ്‌ക്കുക."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">ഉള്ളിൽ <xliff:g id="NUMBER_1">%s</xliff:g> അറിയിപ്പുകൾ കൂടിയുണ്ട്.</item>
- <item quantity="one">ഉള്ളിൽ <xliff:g id="NUMBER_0">%s</xliff:g> അറിയിപ്പ് കൂടിയുണ്ട്.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# അറിയിപ്പ് കൂടിയുണ്ട്.}other{# അറിയിപ്പുകൾ കൂടിയുണ്ട്.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"സ്‌ക്രീൻ ലാൻഡ്‌സ്‌കേപ്പ് ഓറിയന്റേഷനിൽ ലോക്കുചെയ്‌തു."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"സ്‌ക്രീൻ പോർട്രെയ്‌റ്റ് ഓറിയന്റേഷനിൽ ലോക്കുചെയ്‌തു."</string>
<string name="dessert_case" msgid="9104973640704357717">"ഡെസേർട്ട് കെയ്സ്"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"സ്‌ക്രീൻ സ്വയമേവ തിരിയൽ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"സ്‌ക്രീൻ സ്വയമേവ തിരിക്കുക"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ലൊക്കേഷൻ"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"സ്ക്രീൻ സേവർ"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"ക്യാമറ ആക്‌സസ്"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"മൈക്ക് ആക്‌സസ്"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ലഭ്യമാണ്"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"ഹോട്ട്‌സ്‌പോട്ട്"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ഓണാക്കുന്നു…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"ഡാറ്റ സേവർ ഓണാണ്"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d ഉപകരണങ്ങൾ</item>
- <item quantity="one">%d ഉപകരണം</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# ഉപകരണം}other{# ഉപകരണങ്ങൾ}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"ടോർച്ച്"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"ക്യാമറ ഉപയോഗത്തിലാണ്"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"മൊബൈൽ ഡാറ്റ"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"വീണ്ടും ടാപ്പ് ചെയ്യുക"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"തുറക്കാൻ മുകളിലോട്ട് സ്വൈപ്പ് ചെയ്യുക"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"തുറക്കാൻ അൺലോക്ക് ഐക്കൺ അമർത്തുക"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"മുഖം ഉപയോഗിച്ച് അൺലോക്ക് ചെയ്‌തു. തുറക്കാൻ അൺലോക്ക് ഐക്കൺ അമർത്തുക."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"മുഖം ഉപയോഗിച്ച് അൺലോക്ക് ചെയ്‌തു. തുറക്കാൻ അമർത്തുക."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"മുഖം തിരിച്ചറിഞ്ഞു. തുറക്കാൻ അമർത്തുക."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"നിങ്ങളുടെ സെഷൻ തുടരണോ?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"പുനരാംരംഭിക്കുക"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"അതെ, തുടരുക"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"അതിഥി മോഡ്"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"നിങ്ങൾ അതിഥി മോഡിലാണ്"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"പുതിയൊരു ഉപയോക്താവിനെ ചേർത്താൽ അതിഥി മോഡിൽ നിന്ന് പുറത്ത് കടക്കുകയും നിലവിലെ അതിഥി മോഡിലുള്ള എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കുകയും ചെയ്യും."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"ഉപയോക്തൃ പരിധി എത്തി"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">നിങ്ങൾക്ക് <xliff:g id="COUNT">%d</xliff:g> ഉപയോക്താക്കളെ വരെ ചേർക്കാനാവും.</item>
- <item quantity="one">ഒരു ഉപയോക്താവിന് മാത്രമേ സൃഷ്‌ടിക്കാനാവൂ.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{ഒരു ഉപയോക്താവിനെ മാത്രമേ സൃഷ്‌ടിക്കാനാകൂ.}other{നിങ്ങൾക്ക് # ഉപയോക്താക്കളെ വരെ ചേർക്കാം.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"ഉപയോക്താവിനെ ഇല്ലാതാക്കണോ?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"ഈ ഉപയോക്താവിന്റെ എല്ലാ ആപ്സും ഡാറ്റയും ഇല്ലാതാക്കും."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"നീക്കംചെയ്യുക"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"എന്നെ ഓർമ്മിപ്പിക്കുക"</string>
<string name="snooze_undo" msgid="2738844148845992103">"പഴയപടിയാക്കുക"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> സമയത്തേക്ക് സ്‌നൂസ് ‌ചെയ്‌തു"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d മണിക്കൂർ</item>
- <item quantity="one">%d മണിക്കൂർ</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d മിനിറ്റ്</item>
- <item quantity="one">%d മിനിറ്റ്</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# മണിക്കൂർ}=2{# മണിക്കൂർ}other{# മണിക്കൂർ}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# മിനിറ്റ്}other{# മിനിറ്റ്}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"ബാറ്ററി ലാഭിക്കൽ"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"ബട്ടൺ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"ഹോം"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"മുന്നറിയിപ്പുകൾ"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"ബാറ്ററി"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"സ്‌ക്രീൻഷോട്ടുകൾ"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"പൊതുവായ സന്ദേശങ്ങൾ"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"സജ്ജീകരണം"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"സ്റ്റോറേജ്"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"സൂചനകൾ"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"മാറ്റുക"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ഉപകരണ നിയന്ത്രണങ്ങൾ"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"നിയന്ത്രണങ്ങൾ ചേർക്കാൻ ആപ്പ് തിരഞ്ഞെടുക്കുക"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> നിയന്ത്രണങ്ങൾ ചേർത്തു.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> നിയന്ത്രണം ചേർത്തു.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# നിയന്ത്രണം ചേർത്തു.}other{# നിയന്ത്രണങ്ങൾ ചേർത്തു.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"നീക്കം ചെയ്‌തു"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"പ്രിയപ്പെട്ടതാക്കി"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"പ്രിയപ്പെട്ടതാക്കി, സ്ഥാനം <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ടൈൽ ചേർക്കുക"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ടൈൽ ചേർക്കരുത്"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ഉപയോക്താവിനെ തിരഞ്ഞെടുക്കൂ"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ആപ്പുകൾ സജീവമാണ്</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> ആപ്പ് സജീവമാണ്</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# ആപ്പ് സജീവമാണ്}other{# ആപ്പുകൾ സജീവമാണ്}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"പുതിയ വിവരങ്ങൾ"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"സജീവമായ ആപ്പുകൾ"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"നിങ്ങൾ ഉപയോഗിക്കാത്തപ്പോൾ പോലും ഈ ആപ്പുകൾ സജീവമായിരിക്കും, പ്രവർത്തിച്ചുകൊണ്ടിരിക്കുകയും ചെയ്യും. ഇത് അവയുടെ പ്രവർത്തനക്ഷമത മെച്ചപ്പെടുത്തുന്നു, എന്നാൽ ഇത് ബാറ്ററി ലൈഫിനെ ബാധിച്ചേക്കാനിടയുണ്ട്."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"അലാറം സജ്ജീകരിച്ചു"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ക്യാമറയും മൈക്കും ഓഫാണ്"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# അറിയിപ്പ്}other{# അറിയിപ്പുകൾ}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"പ്രക്ഷേപണം ചെയ്യുന്നു"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ബ്രോഡ്‌കാസ്റ്റ് ചെയ്യുന്നത് അവസാനിപ്പിക്കണോ?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"നിങ്ങൾ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ബ്രോഡ്‌കാസ്റ്റ് ചെയ്യുകയോ ഔട്ട്പുട്ട് മാറ്റുകയോ ചെയ്താൽ നിങ്ങളുടെ നിലവിലുള്ള ബ്രോഡ്‌കാസ്റ്റ് അവസാനിക്കും"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ബ്രോഡ്‌കാസ്റ്റ് ചെയ്യുക"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ഔട്ട്പുട്ട് മാറ്റുക"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"അജ്ഞാതം"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
index 12089288d134..7a078737aa96 100644
--- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"ഓഫാണ്"</item>
<item msgid="460891964396502657">"ഓണാണ്"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"ലഭ്യമല്ല"</item>
+ <item msgid="8014986104355098744">"ഓഫാണ്"</item>
+ <item msgid="5966994759929723339">"ഓണാണ്"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index b1455a76c470..c9bd0bf20243 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Төхөөрөмжийг түгжсэн"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Скан хийх нүүр царай"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Илгээх"</string>
- <string name="phone_label" msgid="5715229948920451352">"утас нээх"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"дуут туслахыг нээнэ"</string>
- <string name="camera_label" msgid="8253821920931143699">"камер нээх"</string>
<string name="cancel" msgid="1089011503403416730">"Цуцлах"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Баталгаажуулах"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Дахин оролдох"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Мэдрэгчийг унтраах идэвхтэй байна"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Бүх мэдэгдлийг цэвэрлэх."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">дотор бусад <xliff:g id="NUMBER_1">%s</xliff:g> мэдэгдэл байна.</item>
- <item quantity="one">дотор бусад <xliff:g id="NUMBER_0">%s</xliff:g> мэдэгдэл байна.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{дотор # мэдэгдэл байна.}other{дотор # мэдэгдэл байна.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Дэлгэц хэвтээ чиглэлд түгжигдсэн."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Дэлгэц босоо чиглэлээр түгжигдсэн."</string>
<string name="dessert_case" msgid="9104973640704357717">"Амттаны хайрцаг"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматаар эргэх"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Дэлгэцийг автоматаар эргүүлэх"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Байршил"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Дэлгэц амраагч"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Камерын хандалт"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Микрофоны хандалт"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Боломжтой"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Сүлжээний цэг"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Асааж байна…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Дата хэмнэгчийг асаасан"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d төхөөрөмж</item>
- <item quantity="one">%d төхөөрөмж</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# төхөөрөмж}other{# төхөөрөмж}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Гар чийдэн"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Камерыг ашиглаж байна"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Мобайл дата"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Дaхин товшино уу"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Нээхийн тулд дээш шударна уу"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Нээхийн тулд түгжээг тайлах дүрс тэмдэг дээр дараарай"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Царайгаар түгжээг тайлсан. Нээхийн тулд түгжээг тайлах дүрс тэмдэг дээр дараарай."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Царайгаар түгжээг тайлсан. Нээхийн тулд дарна уу."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Царайг таньсан. Нээхийн тулд дарна уу."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Та үргэлжлүүлэхийг хүсэж байна уу?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Дахин эхлүүлэх"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Тийм, үргэлжлүүлэх"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Зочны горим"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Та зочны горимд байна"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Шинэ хэрэглэгч нэмснээр зочны горимоос гаргах бөгөөд бүх апп болон өгөгдлийг одоогийн зочны харилцан үйлдлээс устгана."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Хэрэглэгчийн хязгаарт хүрсэн"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Та <xliff:g id="COUNT">%d</xliff:g> хүртэлх хэрэглэгч нэмэх боломжтой.</item>
- <item quantity="one">Зөвхөн нэг хэрэглэгч үүсгэх боломжтой.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Зөвхөн нэг хэрэглэгч үүсгэх боломжтой.}other{Та # хүртэлх хэрэглэгч нэмж болно.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Хэрэглэгчийг устгах уу?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Энэ хэрэглэгчийн бүх апп болон мэдээлэл устах болно."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Арилгах"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Надад сануулах"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Болих"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g>-д түр хойшлуулсан"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d цаг</item>
- <item quantity="one">%d цаг</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d минут</item>
- <item quantity="one">%d минут</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# цаг}=2{# цаг}other{# цаг}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# минут}other{# минут}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Батарей хэмнэгч"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> товчлуур"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Нүүр хуудас"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Сэрэмжлүүлэг"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Батарей"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Дэлгэцийн зураг дарах"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Энгийн мессеж"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Шуурхай апп"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Тохируулга"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Хадгалах сан"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Заавар"</string>
<string name="instant_apps" msgid="8337185853050247304">"Шуурхай апп"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"асаах/унтраах"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Төхөөрөмжийн хяналт"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Хяналтууд нэмэхийн тулд аппыг сонгоно уу"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> хяналтыг нэмлээ.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> хяналтыг нэмлээ.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# хяналт нэмсэн.}other{# хяналт нэмсэн.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Хассан"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Дуртай гэж тэмдэглэсэн"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"<xliff:g id="NUMBER">%d</xliff:g>-р байршилд дуртай гэж тэмдэглэсэн"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Хавтан нэмэх"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Хавтанг бүү нэм"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Хэрэглэгч сонгох"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> апп идэвхтэй байна</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> апп идэвхтэй байна</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# апп идэвхтэй байна}other{# апп идэвхтэй байна}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Шинэ мэдээлэл"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Идэвхтэй аппууд"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Та эдгээр аппыг ашиглаагүй байсан ч тэдгээр нь идэвхтэй бөгөөд ажиллаж байна. Энэ нь тэдгээрийн ажиллагааг сайжруулах ч батарейн ажиллах хугацаанд мөн нөлөөлж магадгүй."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Сэрүүлгийг тохируулсан"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камер болон микрофон унтраалттай байна"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# мэдэгдэл}other{# мэдэгдэл}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Нэвтрүүлэлт"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г нэвтрүүлэхээ зогсоох уу?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Хэрэв та <xliff:g id="SWITCHAPP">%1$s</xliff:g>-г нэвтрүүлсэн эсвэл гаралтыг өөрчилсөн бол таны одоогийн нэвтрүүлэлтийг зогсооно"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g>-г нэвтрүүлэх"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Гаралтыг өөрчлөх"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Тодорхойгүй"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"MMM d EEE"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
index 3748440058b6..776c4877d1ad 100644
--- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Унтраалттай"</item>
<item msgid="460891964396502657">"Асаалттай"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Боломжгүй"</item>
+ <item msgid="8014986104355098744">"Унтраалттай"</item>
+ <item msgid="5966994759929723339">"Асаалттай"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 64bb6fa55308..5922126dcebe 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"डिव्हाइस लॉक केले"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"चेहरा स्कॅन करत आहे"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"पाठवा"</string>
- <string name="phone_label" msgid="5715229948920451352">"फोन उघडा"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"व्हॉइस सहाय्य उघडा"</string>
- <string name="camera_label" msgid="8253821920931143699">"कॅमेरा उघडा"</string>
<string name="cancel" msgid="1089011503403416730">"रद्द करा"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"कंफर्म करा"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"पुन्हा प्रयत्न करा"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"सेन्सर बंद आहेत"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"सर्व सूचना साफ करा."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">आत आणखी <xliff:g id="NUMBER_1">%s</xliff:g> सूचना.</item>
- <item quantity="one">आत आणखी <xliff:g id="NUMBER_0">%s</xliff:g> सूचना.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{आतमध्ये आणखी # सूचना आहे.}other{आतमध्ये आणखी # सूचना आहेत.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"लॅंडस्केप ओरिएंटेशनमध्ये स्क्रीन लॉक केली आहे."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"पोर्ट्रेट अभिमुखतेमध्ये स्क्रीन लॉक केली आहे."</string>
<string name="dessert_case" msgid="9104973640704357717">"मिष्ठान्न प्रकरण"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ऑटो-रोटेट स्क्रीन"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"स्थान"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"स्क्रीन सेव्हर"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"कॅमेराचा अ‍ॅक्सेस"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"माइकचा ॲक्सेस"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"उपलब्ध आहे"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"हॉटस्पॉट"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"सुरू करत आहे…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"डेटा सेव्हर सुरू आहे"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d डिव्हाइस</item>
- <item quantity="one">%d डिव्हाइस</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# डिव्हाइस}other{# डिव्हाइस}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"फ्लॅशलाइट"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"कॅमेरा वापरात आहे"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"मोबाइल डेटा"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"पुन्हा टॅप करा"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"उघडण्यासाठी वर स्वाइप करा"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"उघडण्यासाठी अनलॉक करा आयकन दाबा"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"चेहऱ्याने अनलॉक केले. उघडण्यासाठी अनलॉक करा आयकन दाबा."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"चेहऱ्याने अनलॉक केले आहे. उघडण्यासाठी दाबा."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"चेहरा ओळखला आहे. उघडण्यासाठी दाबा."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"तुम्ही तुमचे सत्र सुरू ठेवू इच्छिता?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"येथून सुरू करा"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"होय, सुरू ठेवा"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"अतिथी मोड"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"तुम्ही अतिथी मोडमध्ये आहात"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"नवीन वापरकर्ता जोडल्याने अतिथी मोडमधून बाहेर पडाल आणि सध्याच्या अतिथी सत्रातील सर्व अ‍ॅप्स व डेटा हटवला जाईल."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"वापरकर्ता मर्यादा गाठली"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">तुम्ही <xliff:g id="COUNT">%d</xliff:g> वापरकर्त्यांपर्यंत जोडू शकता.</item>
- <item quantity="one">फक्त एक वापरकर्ता तयार केला जाऊ शकतो.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{फक्त एक वापरकर्ता तयार केला जाऊ शकतो.}other{तुम्ही कमाल # वापरकर्ते जोडू शकता.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"वापरकर्त्यास काढायचे?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"या वापरकर्त्याचे सर्व अ‍ॅप्स आणि डेटा काढून टाकला जाईल."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"काढा"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"मला आठवण करून द्या"</string>
<string name="snooze_undo" msgid="2738844148845992103">"पहिल्यासारखे करा"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> साठी स्नूझ करा"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other"> %d तास</item>
- <item quantity="one">%d तास</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other"> %d मिनिटे</item>
- <item quantity="one">%d मिनिट</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# तास}=2{# तास}other{# तास}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# मिनिट}other{# मिनिटे}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"बॅटरी सेव्हर"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"बटण <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"सूचना"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"बॅटरी"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"स्क्रीनशॉट"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"सर्वसाधारण मेसेज"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"सेटअप"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"स्टोरेज"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"सूचना"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"टॉगल करा"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"डिव्हाइस नियंत्रणे"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"नियंत्रणे जोडण्यासाठी ॲप निवडा"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> नियंत्रणे जोडली.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> नियंत्रण जोडले.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# नियंत्रण जोडले आहे.}other{# नियंत्रणे जोडली आहेत.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"काढून टाकले"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"आवडले"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"आवडले, स्थान <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"टाइल जोडा"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"टाइल जोडू नका"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"वापरकर्ता निवडा"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> अ‍ॅप्स अ‍ॅक्टिव्ह आहेत</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> अ‍ॅप अ‍ॅक्टिव्ह आहे</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# अ‍ॅप अ‍ॅक्टिव्ह आहे}other{# अ‍ॅप्स अ‍ॅक्टिव्ह आहेत}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"नवीन माहिती"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"अ‍ॅक्टिव्ह ॲप्स"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"तुम्ही वापरत नसतानाही ही अ‍ॅप्स अ‍ॅक्टिव्ह असून रन होत आहेत. यामुळे त्यांची कार्यक्षमता सुधारते, पण त्याचा बॅटरी लाइफवरदेखील परिणाम होऊ शकतो."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"अलार्म सेट केला"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"कॅमेरा आणि माइक बंद आहेत"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# सूचना}other{# सूचना}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ब्रॉडकास्ट करत आहे"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> चे प्रसारण थांबवायचे आहे का?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"तुम्ही <xliff:g id="SWITCHAPP">%1$s</xliff:g> चे प्रसारण केल्यास किंवा आउटपुट बदलल्यास, तुमचे सध्याचे प्रसारण बंद होईल"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> चे प्रसारण करा"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"आउटपूट बदला"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"अज्ञात"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
index a6a3873f95d7..f75f0d097998 100644
--- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"बंद आहे"</item>
<item msgid="460891964396502657">"सुरू आहे"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"उपलब्ध नाही"</item>
+ <item msgid="8014986104355098744">"बंद आहे"</item>
+ <item msgid="5966994759929723339">"सुरू आहे"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index db64dd05e3b8..ac6f3deb8e53 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Peranti dikunci"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Mengimbas wajah"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Hantar"</string>
- <string name="phone_label" msgid="5715229948920451352">"buka telefon"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"buka bantuan suara"</string>
- <string name="camera_label" msgid="8253821920931143699">"buka kamera"</string>
<string name="cancel" msgid="1089011503403416730">"Batal"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Sahkan"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Cuba lagi"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Penderia dimatikan aktif"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Padamkan semua pemberitahuan."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> lagi pemberitahuan di dalam.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> lagi pemberitahuan di dalam.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# lagi pemberitahuan dalam kumpulan.}other{# lagi pemberitahuan dalam kumpulan.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Skrin dikunci dalam orientasi landskap."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Skrin dikunci dalam orientasi potret."</string>
<string name="dessert_case" msgid="9104973640704357717">"Bekas Pencuci Mulut"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autoputar"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Autoputar skrin"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Penyelamat skrin"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Akses kamera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Akses mikrofon"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tersedia"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Tempat liputan"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Menghidupkan…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Penjimat Data dihidupkan"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d peranti</item>
- <item quantity="one">%d peranti</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# peranti}other{# peranti}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lampu Suluh"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera sedang digunakan"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Data mudah alih"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Ketik sekali lagi"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Leret ke atas untuk buka"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Tekan ikon buka kunci untuk buka"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Dibuka kunci dengan wajah. Tekan ikon buka kunci untuk buka."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Dibuka kunci dengan wajah. Tekan untuk membuka."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Wajah dicam. Tekan untuk membuka."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Adakah anda ingin meneruskan sesi anda?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulakan semula"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ya, teruskan"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Mod tetamu"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Anda dalam mod tetamu"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Tindakan menambahkan pengguna baharu akan menyebabkan anda keluar daripada mod tetamu dan memadamkan semua apl dan data daripada sesi tetamu semasa."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Had pengguna dicapai"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Anda boleh menambah sehingga <xliff:g id="COUNT">%d</xliff:g> pengguna.</item>
- <item quantity="one">Hanya satu pengguna yang boleh dibuat.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Hanya satu pengguna boleh dibuat.}other{Anda boleh menambahkan sehingga # pengguna}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Alih keluar pengguna?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Semua apl dan data pengguna ini akan dipadamkan."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Alih keluar"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Ingatkan saya"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Buat asal"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Ditunda selama <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d jam</item>
- <item quantity="one">%d jam</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minit</item>
- <item quantity="one">%d minit</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# jam}=2{# jam}other{# jam}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minit}other{# minit}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Penjimat Bateri"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Butang <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Skrin Utama"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Makluman"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Bateri"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Tangkapan skrin"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Mesej Am"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Apl Segera"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Persediaan"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Storan"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Pembayang"</string>
<string name="instant_apps" msgid="8337185853050247304">"Apl Segera"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"togol"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kawalan peranti"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Pilih apl untuk menambahkan kawalan"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> kawalan ditambah.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> kawalan ditambah.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kawalan ditambah.}other{# kawalan ditambah.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Dialih keluar"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Digemari"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Digemari, kedudukan <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tambahkan jubin"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Jangan tambah jubin"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pilih pengguna"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apl aktif</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> apl aktif</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# apl aktif}other{# apl aktif}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Maklumat baharu"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apl aktif"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Apl ini aktif dan berfungsi walaupun anda tidak menggunakannya. Ini meningkatkan kefungsian apl tetapi mungkin akan memberikan kesan kepada hayat bateri."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Penggera ditetapkan"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera dan mikrofon dimatikan"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# pemberitahuan}other{# pemberitahuan}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Menyiarkan"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hentikan siaran <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jika anda siarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g> atau tukarkan output, siaran semasa anda akan berhenti"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Siarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Tukar output"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Tidak diketahui"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
index cfd831a1d94b..9fa7ab51d064 100644
--- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Mati"</item>
<item msgid="460891964396502657">"Hidup"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Tidak tersedia"</item>
+ <item msgid="8014986104355098744">"Mati"</item>
+ <item msgid="5966994759929723339">"Hidup"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index fb60b95e1c62..09de7451064f 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"စက်ပစ္စည်းကို လော့ခ်ချထားသည်"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"မျက်နှာ စကင်ဖတ်နေသည်"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ပို့ရန်"</string>
- <string name="phone_label" msgid="5715229948920451352">"ဖုန်းကို ဖွင့်ရန်"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"အသံ အကူအညီအား ဖွင့်ရန်"</string>
- <string name="camera_label" msgid="8253821920931143699">"ကင်မရာ ဖွင့်ရန်"</string>
<string name="cancel" msgid="1089011503403416730">"မလုပ်တော့"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"အတည်ပြုပါ"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"ထပ်စမ်းကြည့်ရန်"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"အာရုံခံစနစ်များ ပိတ်ထားသည်"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"အကြောင်းကြားချက်အားလုံးကို ထုတ်ပစ်သည်။"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">အတွင်းတွင် အကြောင်းကြားချက် နောက်ထပ် <xliff:g id="NUMBER_1">%s</xliff:g> ခုရှိပါသည်။</item>
- <item quantity="one">အတွင်းတွင် အကြောင်းကြားချက် နောက်ထပ် <xliff:g id="NUMBER_0">%s</xliff:g> ခုရှိပါသည်။</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{အထဲတွင် နောက်ထပ်အကြောင်းကြားချက် # ခု ရှိသည်။}other{အထဲတွင် နောက်ထပ်အကြောင်းကြားချက် # ခု ရှိသည်။}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"ဖန်သားပြင် အနေအထားက အလျားလိုက်အဖြစ် ပုံသေ လုပ်ထားပါသည်"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"ဖန်သားပြင် အနေအထားက ဒေါင်လိုက်အဖြစ် ပုံသေ လုပ်ထားပါသည်"</string>
<string name="dessert_case" msgid="9104973640704357717">"မုန့်ထည့်သော ပုံး"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"အော်တို-လည်"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"မျက်နှာပြင်အား အလိုအလျောက်လှည့်ခြင်း"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"တည်နေရာ"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"စကရင်ချွေတာစနစ်"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"ကင်မရာသုံးခွင့်"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"မိုက်သုံးခွင့်"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ရနိုင်သည်"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"ဟော့စပေါ့"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ဖွင့်နေသည်…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"\'ဒေတာချွေတာမှု\' ဖွင့်ထားသည်"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">စက် %d ခု</item>
- <item quantity="one">စက် %d ခု</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{စက် # ခု}other{စက် # ခု}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"ဖလက်ရှ်မီး"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"ကင်မရာကို သုံးနေသည်"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"မိုဘိုင်းဒေတာ"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"ထပ်တို့ပါ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ဖွင့်ရန် အပေါ်သို့ပွတ်ဆွဲပါ"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"ဖွင့်ရန် လော့ခ်ဖွင့်သင်္ကေတကို နှိပ်ပါ"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"မျက်နှာပြ လော့ခ်ဖွင့်ထားသည်။ လော့ခ်ဖွင့်သင်္ကေတ နှိပ်၍ဝင်ပါ။"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"မျက်နှာဖြင့် ဖွင့်ထားသည်။ ဖွင့်ရန် နှိပ်ပါ။"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"မျက်နှာ မှတ်မိသည်။ ဖွင့်ရန် နှိပ်ပါ။"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"သင်၏ စက်ရှင်ကို ဆက်လုပ်လိုပါသလား။"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ပြန်စပါ"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ဆက်လုပ်ပါ"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"ဧည့်သည်မုဒ်"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"သင်သည် ဧည့်သည်မုဒ်တွင် ဖြစ်သည်"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"အသုံးပြုသူ အသစ်ထည့်ခြင်းက ဧည့်သည်မုဒ်မှ ထွက်သွားမည်ဖြစ်ပြီး လက်ရှိဧည့်သည် စက်ရှင်မှ အက်ပ်နှင့် ဒေတာအားလုံးကို ဖျက်လိုက်ပါမည်။"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"အသုံးပြုသူ အကန့်အသတ် ပြည့်သွားပါပြီ"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">အသုံးပြုသူ <xliff:g id="COUNT">%d</xliff:g> ဦးအထိ ထည့်နိုင်သည်။</item>
- <item quantity="one">အသုံးပြုသူ တစ်ဦးသာ ထည့်နိုင်သည်။</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{အသုံးပြုသူတစ်ဦးသာ ထည့်နိုင်သည်။}other{အသုံးပြုသူ # ယောက်အထိ ထည့်နိုင်သည်။}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"သုံးစွဲသူကိုဖယ်ရှားမည်လား?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"ဤအသုံးပြုသူ၏ ဒေတာနှင့် အပ်ဖ်များအားလုံး ဖျက်လိုက်ပါမည်"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"ဖယ်ရှားရန်"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"ကျွန်ုပ်ကို သတိပေးပါ"</string>
<string name="snooze_undo" msgid="2738844148845992103">"နောက်ပြန်ရန်"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> ဆိုင်းငံ့ရန်"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d နာရီ</item>
- <item quantity="one">%d နာရီ</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d မိနစ်</item>
- <item quantity="one">%d မိနစ်</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# နာရီ}=2{# နာရီ}other{# နာရီ}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# မိနစ်}other{# မိနစ်}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"ဘက်ထရီ အားထိန်း"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"ခလုတ် <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"ပင်မ"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"သတိပေးချက်များ"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"ဘက်ထရီ"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"မျက်နှာပြင်ဓာတ်ပုံများ"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"အထွေထွေ မက်ဆေ့ဂျ်များ"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"စနစ်ထည့်ရန်"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"သိုလှောင်ခန်း"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"အရိပ်အမြွက်များ"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ပြောင်းရန်"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"စက်ထိန်းစနစ်"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"ထိန်းချုပ်မှုများထည့်ရန် အက်ပ်ရွေးခြင်း"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">ခလုတ် <xliff:g id="NUMBER_1">%s</xliff:g> ခု ထည့်လိုက်သည်။</item>
- <item quantity="one">ခလုတ် <xliff:g id="NUMBER_0">%s</xliff:g> ခု ထည့်လိုက်သည်။</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{ထိန်းချုပ်ခလုတ် # ခု ထည့်ထားသည်။}other{ထိန်းချုပ်ခလုတ် # ခု ထည့်ထားသည်။}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"ဖယ်ရှားထားသည်"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"အကြိုက်ဆုံးတွင် ထည့်ထားသည်"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"အကြိုက်ဆုံးတွင် ထည့်ထားသည်၊ အဆင့် <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"အကွက်ငယ် ထည့်ရန်"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"အကွက်ငယ် မထည့်ပါ"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"အသုံးပြုသူ ရွေးခြင်း"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other">အက်ပ် <xliff:g id="COUNT_1">%s</xliff:g> ခု ပွင့်နေသည်</item>
- <item quantity="one">အက်ပ် <xliff:g id="COUNT_0">%s</xliff:g> ခု ပွင့်နေသည်</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{အက်ပ် # ခု ပွင့်နေသည်}other{အက်ပ် # ခု ပွင့်နေသည်}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"အချက်အလက်သစ်"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ပွင့်နေသည့်အက်ပ်များ"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"အသုံးမပြုချိန်တွင်လည်း ဤအက်ပ်များက ပွင့်နေပြီး လုပ်ဆောင်နေပါသည်။ ၎င်းက လုပ်ဆောင်ချက်ကို ပိုမိုကောင်းမွန်စေသော်လည်း ဘက်ထရီ သက်တမ်းတိုစေနိုင်ပါသည်။"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"နိုးစက် သတ်မှတ်ထားသည်"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ကင်မရာနှင့် မိုက် ပိတ်ထားသည်"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{အကြောင်းကြားချက် # ခု}other{အကြောင်းကြားချက် # ခု}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ထုတ်လွှင့်ခြင်း"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ထုတ်လွှင့်ခြင်းကို ရပ်မလား။"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ကို ထုတ်လွှင့်သောအခါ (သို့) အထွက်ကို ပြောင်းသောအခါ သင့်လက်ရှိထုတ်လွှင့်ခြင်း ရပ်သွားမည်"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ထုတ်လွှင့်ခြင်း"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"အထွက်ကို ပြောင်းခြင်း"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"မသိ"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE၊ MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml
index 665af20761c7..493a7f0977c6 100644
--- a/packages/SystemUI/res/values-my/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"ပိတ်"</item>
<item msgid="460891964396502657">"ဖွင့်"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"မရနိုင်ပါ"</item>
+ <item msgid="8014986104355098744">"ပိတ်"</item>
+ <item msgid="5966994759929723339">"ဖွင့်"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 4f532ec82c8b..8b435c2fd43d 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Enheten er låst"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skanning av ansikt"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Send"</string>
- <string name="phone_label" msgid="5715229948920451352">"åpne telefonen"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"åpne talehjelp"</string>
- <string name="camera_label" msgid="8253821920931143699">"åpne kamera"</string>
<string name="cancel" msgid="1089011503403416730">"Avbryt"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Bekreft"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Prøv på nytt"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"«Sensorene er av» er aktiv"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Fjern alle varslinger."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> andre varsler i gruppen.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> annet varsel i gruppen.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# annet varsel i gruppen.}other{# andre varsler i gruppen.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Skjermen er låst i liggende retning."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Skjermen er låst i stående retning."</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessertmonter"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotér automatisk"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotér skjermen automatisk"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Sted"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Skjermsparer"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameratilgang"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofontilgang"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tilgjengelig"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Wifi-sone"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Slår på …"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Datasparing er på"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d enheter</item>
- <item quantity="one">%d enhet</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# enhet}other{# enheter}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lommelykt"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kameraet er i bruk"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobildata"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Trykk igjen"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Sveip opp for å åpne"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Trykk på lås opp-ikonet for å åpne"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Låst opp med ansiktet. Trykk på lås opp-ikonet for å fortsette"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Låst opp med ansiktet. Trykk for å åpne."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Ansiktet er gjenkjent. Trykk for å åpne."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsette økten?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start på nytt"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, fortsett"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Gjestemodus"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Du er i gjestemodus"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Hvis du legger til en ny bruker, avsluttes gjestemodus, og alle apper og data fra den gjeldende gjesteøkten slettes."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Grensen for antall brukere er nådd"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Du kan legge til opptil <xliff:g id="COUNT">%d</xliff:g> brukere.</item>
- <item quantity="one">Du kan bare opprette én bruker.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Du kan bare opprette én bruker.}other{Du kan legge til opptil # brukere.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Vil du fjerne brukeren?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Alle apper og data som tilhører denne brukeren, blir slettet."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Fjern"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Minn meg på det"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Angre"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Slumrer i <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d timer</item>
- <item quantity="one">%d time</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minutter</item>
- <item quantity="one">%d minutt</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# time}=2{# timer}other{# timer}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minutt}other{# minutter}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Batterisparing"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g>-knappen"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Startskjerm"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Varsler"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Batteri"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Skjermdumper"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Generelle meldinger"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Konfigurering"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Lagring"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Hint"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"slå av/på"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Enhetsstyring"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Velg en app for å legge til kontroller"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> kontroller er lagt til.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> kontroll er lagt til.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontroll er lagt til.}other{# kontroller er lagt til.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Fjernet"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Favoritt"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Favoritt, posisjon <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Legg til brikke"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ikke legg til brikke"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Velg bruker"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apper er aktive</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> app er aktiv</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app er aktiv}other{# apper er aktive}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Ny informasjon"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktive apper"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Disse appene er aktive og kjører – selv når du ikke bruker dem. Dette forbedrer funksjonaliteten deres, men det kan også påvirke batterilevetiden."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmen er stilt inn"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera og mikrofon er av"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# varsel}other{# varsler}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Kringkaster"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vil du stoppe kringkastingen av <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Hvis du kringkaster <xliff:g id="SWITCHAPP">%1$s</xliff:g> eller endrer utgangen, stopper den nåværende kringkastingen din"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Kringkast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Endre utgang"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Ukjent"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d. MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"t:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"tt:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
index a28b817b765b..6fa902a662ff 100644
--- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Av"</item>
<item msgid="460891964396502657">"På"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Utilgjengelig"</item>
+ <item msgid="8014986104355098744">"Av"</item>
+ <item msgid="5966994759929723339">"På"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 896a4f4785e0..05bb2868125c 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"यन्त्र लक गरिएको छ"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"अनुहार स्क्यान गर्दै"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"पठाउनुहोस्"</string>
- <string name="phone_label" msgid="5715229948920451352">"फोन खोल्नुहोस्"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"आवाज सहायता खोल्नुहोस्"</string>
- <string name="camera_label" msgid="8253821920931143699">"क्यामेरा खोल्नुहोस्"</string>
<string name="cancel" msgid="1089011503403416730">"रद्द गर्नुहोस्"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"पुष्टि गर्नुहोस्"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"फेरि प्रयास गर्नुहोस्"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"सेन्सर निष्क्रिय नामक सुविधा सक्रिय छ"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"सबै सूचनाहरू हटाउनुहोस्।"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">भित्र थप <xliff:g id="NUMBER_1">%s</xliff:g> सूचनाहरू छन्।</item>
- <item quantity="one">भित्र थप <xliff:g id="NUMBER_0">%s</xliff:g> सूचना छ।</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{समूहभित्र थप # सूचना छ।}other{समूहभित्र थप # वटा सूचना छन्।}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"स्क्रिनलाई ल्यान्डस्केप अवस्थामा बन्द गरिएको छ।"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"स्क्रिन पोर्टेट अभिमूखमा लक गरिएको छ।"</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"अटो रोटेट"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रिन स्वतःघुम्ने"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"लोकेसन"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"स्क्रिन सेभर"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"क्यामेरा प्रयोग गर्ने अनुमति"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"माइक प्रयोग गर्ने अनुमति"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"उपलब्ध छ"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"हटस्पट"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"सक्रिय गर्दै…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"डेटा सेभर सक्रिय छ"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d यन्त्रहरू</item>
- <item quantity="one">%d यन्त्र</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# डिभाइस}other{# वटा डिभाइस}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"फ्ल्यासलाइट"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"क्यामेरा प्रयोग भइरहेको छ"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"मोबाइल डेटा"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"फेरि ट्याप गर्नुहोस्"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"खोल्न माथितिर स्वाइप गर्नुहोस्"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"खोल्न अनलक आइकनमा थिच्नुहोस्"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"अनुहार प्रयोग गरी अनलक गरियो। खोल्न अनलक आइकनमा थिच्नुहोस्।"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"अनुहार प्रयोग गरी अनलक गरियो। डिभाइस खोल्न थिच्नुहोस्।"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"अनुहार पहिचान गरियो। डिभाइस खोल्न थिच्नुहोस्।"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"तपाईं आफ्नो सत्र जारी गर्न चाहनुहुन्छ?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"सुरु गर्नुहोस्"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"हो, जारी राख्नुहोस्"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"अतिथि मोड"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"तपाईं अतिथि मोड चलाउँदै हुनुहुन्छ"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"तपाईंले नयाँ प्रयोगकर्ता थप्नुभयो भने तपाईं अतिथि मोडबाट बाहिरिनु हुने छ र हालको अतिथि सत्रका सबै एप तथा डेटा मेटिने छ।"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"प्रयोगकर्ताको सीमा पुग्यो"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">तपाईं अधिकतम <xliff:g id="COUNT">%d</xliff:g> प्रयोगहरू मात्र थप्न सक्नुहुन्छ।</item>
- <item quantity="one">एउटा प्रयोगकर्ता मात्र सिर्जना गर्न सकिन्छ।</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{एउटा प्रोफाइल मात्र बनाउन सकिन्छ।}other{तपाईं बढीमा # जना प्रयोगकर्ता सामेल गराउन सक्नुहुन्छ।}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"प्रयोगकर्ता हटाउन चाहनुहुन्छ?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"यस प्रयोगकर्ताको सबै एपहरू तथा डेटा हटाइने छ।"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"हटाउनुहोस्"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"मलाई सम्झाउनुहोस्"</string>
<string name="snooze_undo" msgid="2738844148845992103">"अन्डू गर्नुहोस्"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> का लागि स्नुज गरियो"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d घन्टा</item>
- <item quantity="one">%d घन्टा</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d मिनेट</item>
- <item quantity="one">%d मिनेट</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# घण्टा}=2{# घण्टा}other{# घण्टा}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# मिनेट}other{# मिनेट}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"ब्याट्री सेभर"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> बटन"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"सतर्कताहरू"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"ब्याट्री"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"स्क्रिनशटहरू"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"सामान्य सन्देशहरू"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"सेटअप गर्नुहोस्"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"भण्डारण"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"सङ्केतहरू"</string>
<string name="instant_apps" msgid="8337185853050247304">"तात्कालिक एपहरू"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"टगल गर्नुहोस्"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"डिभाइस नियन्त्रण गर्ने विजेटहरू"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"कन्ट्रोल थप्नु पर्ने एप छान्नुहोस्"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> वटा नियन्त्र थपियो।</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> नियन्त्र थपियो</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# कन्ट्रोल हालियो।}other{# वटा कन्ट्रोल हालियो।}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"हटाइएको"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"मनपराइएको"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"मन पराइएका कुराहरूको <xliff:g id="NUMBER">%d</xliff:g> औँ स्थानमा"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"टाइल हाल्नुहोस्"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"टाइल नहाल्नुहोस्"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"प्रयोगकर्ता चयन गर्नु…"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> वटा एप सक्रिय छन्</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> एप सक्रिय छ</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# एप सक्रिय छ}other{# वटा एप सक्रिय छन्}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"नयाँ जानकारी"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"सक्रिय एपहरू"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"यी एपहरू प्रयोग नगरेका बेला पनि सक्रिय र चालु अवस्थामा रहन्छन्। यसले एपहरूलाई अझ राम्ररी काम गर्न सक्ने बनाउँछ। तर यसका कारणले ब्याट्रीको आयु भने घट्न सक्छ।"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"अलार्म सेट गरिएको छ"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"क्यामेरा र माइक अफ छन्"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# वटा सूचना}other{# वटा सूचनाहरू}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"प्रसारण गरिँदै छ"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ब्रोडकास्ट गर्न छाड्ने हो?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"तपाईंले <xliff:g id="SWITCHAPP">%1$s</xliff:g> ब्रोडकास्ट गर्नुभयो वा आउटपुट परिवर्तन गर्नुभयो भने तपाईंको हालको ब्रोडकास्ट रोकिने छ"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ब्रोडकास्ट गर्नुहोस्"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"आउटपुट परिवर्तन गर्नुहोस्"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"अज्ञात"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
index 80edf29f4985..17193bafd9a3 100644
--- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"अफ छ"</item>
<item msgid="460891964396502657">"अन छ"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"अनुपलब्ध"</item>
+ <item msgid="8014986104355098744">"अफ"</item>
+ <item msgid="5966994759929723339">"अन"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 58c2ea5f82a4..2c91600cc5e5 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Apparaat vergrendeld"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Gezicht scannen"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Verzenden"</string>
- <string name="phone_label" msgid="5715229948920451352">"telefoon openen"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"spraakassistent openen"</string>
- <string name="camera_label" msgid="8253821920931143699">"camera openen"</string>
<string name="cancel" msgid="1089011503403416730">"Annuleren"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Bevestigen"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Opnieuw proberen"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"\'Sensoren uit\' actief"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Alle meldingen wissen."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">Nog <xliff:g id="NUMBER_1">%s</xliff:g> meldingen in deze groep.</item>
- <item quantity="one">Nog <xliff:g id="NUMBER_0">%s</xliff:g> melding in deze groep.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Nog # melding in deze groep.}other{Nog # meldingen in deze groep.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Het scherm is nu vergrendeld in liggende stand."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Het scherm is nu vergrendeld in staande stand."</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessertshowcase"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatisch draaien"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Scherm automatisch draaien"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Locatie"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screensaver"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Cameratoegang"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Microfoontoegang"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Beschikbaar"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Aanzetten…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Databesparing staat aan"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d apparaten</item>
- <item quantity="one">%d apparaat</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# apparaat}other{# apparaten}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Zaklamp"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Camera in gebruik"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobiele data"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Tik nog een keer"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swipe omhoog om te openen"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Druk op het ontgrendelicoon om te openen"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Ontgrendeld via gezicht. Druk op het ontgrendelicoon."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Ontgrendeld via gezicht. Druk om te openen."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Gezicht herkend. Druk om te openen."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Wil je doorgaan met je sessie?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Opnieuw starten"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, doorgaan"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Gastmodus"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Je gebruikt de gastmodus"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Als je een nieuwe gebruiker toevoegt, wordt de gastmodus afgesloten en worden alle apps en gegevens van de huidige gastsessie verwijderd."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Gebruikerslimiet bereikt"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Je kunt maximaal <xliff:g id="COUNT">%d</xliff:g> gebruikers toevoegen.</item>
- <item quantity="one">Er kan maar één gebruiker worden gemaakt.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Er kan maar 1 gebruiker worden gemaakt.}other{Je kunt maximaal # gebruikers toevoegen.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Gebruiker verwijderen?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Alle apps en gegevens van deze gebruiker worden verwijderd."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Verwijderen"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Herinneren"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Ongedaan maken"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Snoozefunctie <xliff:g id="TIME_AMOUNT">%1$s</xliff:g> actief"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d uur</item>
- <item quantity="one">%d uur</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minuten</item>
- <item quantity="one">%d minuut</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# uur}=2{# uur}other{# uur}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuut}other{# minuten}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Batterijbesparing"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Knop <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Meldingen"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Batterij"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Algemene berichten"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant-apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Instellen"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Opslag"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Hints"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant-apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"schakelen"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Apparaatbediening"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Kies de app waaraan je bedieningselementen wilt toevoegen"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> bedieningselementen toegevoegd.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> bedieningselement toegevoegd.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# bedieningselement toegevoegd.}other{# bedieningselementen toegevoegd.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Verwijderd"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Gemarkeerd als favoriet"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Gemarkeerd als favoriet, positie <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tegel toevoegen"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Tegel niet toevoegen"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Gebruiker selecteren"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apps zijn actief</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> app is actief</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app is actief}other{# apps zijn actief}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nieuwe informatie"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Actieve apps"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Deze apps zijn actief, ook als je ze niet gebruikt. Dit verbetert de functionaliteit, maar kan ook van invloed zijn op de batterijduur."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Wekker gezet"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Camera en microfoon staan uit"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# melding}other{# meldingen}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Uitzending"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Uitzending van <xliff:g id="APP_NAME">%1$s</xliff:g> stopzetten?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Als je <xliff:g id="SWITCHAPP">%1$s</xliff:g> uitzendt of de uitvoer wijzigt, wordt je huidige uitzending gestopt"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> uitzenden"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Uitvoer wijzigen"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Onbekend"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d mmm"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"u:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
index c4603b22157c..fbccd78eeb8f 100644
--- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Uit"</item>
<item msgid="460891964396502657">"Aan"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Niet beschikbaar"</item>
+ <item msgid="8014986104355098744">"Uit"</item>
+ <item msgid="5966994759929723339">"Aan"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 1c6d86b91aba..f30f7d13d0f7 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"ଡିଭାଇସ୍ ଲକ୍ ହୋଇଯାଇଛି"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ଫେସ୍ ସ୍କାନିଙ୍ଗ କରାଯାଉଛି"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ପଠାନ୍ତୁ"</string>
- <string name="phone_label" msgid="5715229948920451352">"ଫୋନ୍‌ ଖୋଲନ୍ତୁ"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"ଭଏସ୍‍ ସହାୟକ ଖୋଲନ୍ତୁ"</string>
- <string name="camera_label" msgid="8253821920931143699">"କ୍ୟାମେରା ଖୋଲନ୍ତୁ"</string>
<string name="cancel" msgid="1089011503403416730">"ବାତିଲ କରନ୍ତୁ"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"ନିଶ୍ଚିତ କରନ୍ତୁ"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"\'ସେନ୍‌ସର୍ ବନ୍ଦ\' ସକ୍ରିୟ ଅଛି"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"ସମସ୍ତ ବିଜ୍ଞପ୍ତି ଖାଲି କରନ୍ତୁ।"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">ଭିତରେ ଆଉ <xliff:g id="NUMBER_1">%s</xliff:g>ଟି ଅଧିକ ବିଜ୍ଞପ୍ତି ରହିଛି।</item>
- <item quantity="one">ଭିତରେ ଆଉ <xliff:g id="NUMBER_0">%s</xliff:g>ଟି ଅଧିକ ବିଜ୍ଞପ୍ତି ରହିଛି।</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{ଭିତରେ #ଟି ଅଧିକ ବିଜ୍ଞପ୍ତି ଅଛି।}other{ଭିତରେ #ଟି ଅଧିକ ବିଜ୍ଞପ୍ତି ଅଛି।}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"ଲ୍ୟାଣ୍ଡସ୍କେପ୍‌ ଅବସ୍ଥାରେ ସ୍କ୍ରୀନ୍‍କୁ ଲକ୍‌ କରାଯାଇଛି।"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"ପୋର୍ଟ୍ରେଟ୍‍ ଅବସ୍ଥାରେ ସ୍କ୍ରୀନ୍‍କୁ ଲକ୍‌ କରାଯାଇଛି।"</string>
<string name="dessert_case" msgid="9104973640704357717">"ଡେଜର୍ଟ କେସ୍‌"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ଅଟୋ-ରୋଟେଟ୍"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ଅଟୋ-ରୋଟେଟ୍ ସ୍କ୍ରିନ୍"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ଲୋକେସନ୍‍"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ସ୍କ୍ରିନ ସେଭର"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"କ୍ୟାମେରା ଆକ୍ସେସ୍"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"ମାଇକ୍ ଆକ୍ସେସ୍"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ଉପଲବ୍ଧ"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"ହଟସ୍ପଟ୍‌"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ଚାଲୁ ହେଉଛି…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"ଡାଟା ସେଭର୍‌ ଅନ୍‌ ଅଛି"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d ଡିଭାଇସ୍‌ଗୁଡ଼ିକ</item>
- <item quantity="one">%d ଡିଭାଇସ୍</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{#ଟି ଡିଭାଇସ}other{#ଟି ଡିଭାଇସ}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"ଫ୍ଲାସ୍‍ଲାଇଟ୍"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"କ୍ୟାମେରା ବ୍ୟବହାରରେ ଅଛି"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"ମୋବାଇଲ୍‌ ଡାଟା"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"ପୁଣି ଟାପ୍ କରନ୍ତୁ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ଖୋଲିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"ଖୋଲିବାକୁ ଅନଲକ ଆଇକନ ଦବାନ୍ତୁ"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ଫେସ ମାଧ୍ୟମରେ ଅନଲକ କରାଯାଇଛି। ଖୋଲିବାକୁ ଅନଲକ ଆଇକନ ଦବାନ୍ତୁ।"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ଫେସ ମାଧ୍ୟମରେ ଅନଲକ କରାଯାଇଛି। ଖୋଲିବାକୁ ଦବାନ୍ତୁ।"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ଫେସ ଚିହ୍ନଟ କରାଯାଇଛି। ଖୋଲିବାକୁ ଦବାନ୍ତୁ।"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"ଆପଣ ନିଜର ସେସନ୍ ଜାରି ରଖିବାକୁ ଚାହାଁନ୍ତି କି?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ଆରମ୍ଭ କରନ୍ତୁ"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ହଁ, ଜାରି ରଖନ୍ତୁ"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"ଅତିଥି ମୋଡ"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"ଆପଣ ଅତିଥି ମୋଡରେ ଅଛନ୍ତି"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ଜଣେ ନୂଆ ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଗ କରିବା ଦ୍ୱାରା ଅତିଥି ମୋଡରୁ ବାହାରି ଯିବ ଏବଂ ବର୍ତ୍ତମାନର ଅତିଥି ସେସନରୁ ସମସ୍ତ ଆପ ଓ ଡାଟା ଡିଲିଟ ହୋଇଯିବ।"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"ଉପଯୋଗକର୍ତ୍ତା ସୀମାରେ ପହଞ୍ଚିଛି"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">କେବଳ <xliff:g id="COUNT">%d</xliff:g> ଉପଯୋଗକର୍ତ୍ତା ହିଁ ତିଆରି କରିହେବ।</item>
- <item quantity="one">କେବଳ ଜଣେ ଉପଯୋଗକର୍ତ୍ତା ହିଁ ତିଆରି କରିହେବ।</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{କେବଳ ଜଣେ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରାଯାଇପାରିବ।}other{କେବଳ # ଜଣ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରାଯାଇପାରିବ।}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"ୟୁଜରଙ୍କୁ ବାହାର କରିବେ?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"ଏହି ୟୁଜରଙ୍କ ସମସ୍ତ ଆପ୍‍ ଓ ଡାଟା ଡିଲିଟ୍‍ ହେବ।"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"କାଢ଼ି ଦିଅନ୍ତୁ"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"ମୋତେ ରିମାଇଣ୍ଡର୍ କରନ୍ତୁ"</string>
<string name="snooze_undo" msgid="2738844148845992103">"ପୂର୍ବବତ୍ କରନ୍ତୁ"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> ପାଇଁ ସ୍ନୁଜ୍‍ କରାଗଲା"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d ଘଣ୍ଟା</item>
- <item quantity="one">%d ଘଣ୍ଟା</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d ମିନିଟ୍‍</item>
- <item quantity="one">%d ମିନିଟ୍‍</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ଘଣ୍ଟା}=2{# ଘଣ୍ଟା}other{# ଘଣ୍ଟା}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# ମିନିଟ}other{# ମିନିଟ}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"ବ୍ୟାଟେରୀ ସେଭର୍"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"ବଟନ୍‍ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"ହୋମ୍‌"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"ଆଲର୍ଟଗୁଡ଼ିକ"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"ବ୍ୟାଟେରୀ"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"ସ୍କ୍ରୀନଶଟ୍‍"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"ସାଧାରଣ ମେସେଜ୍"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"ସେଟଅପ"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"ଷ୍ଟୋରେଜ୍‌"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"ହିଣ୍ଟ"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ଟୋଗଲ୍ କରନ୍ତୁ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ଡିଭାଇସ୍ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକୁ ଯୋଗ କରିବାକୁ ଆପ୍ ବାଛନ୍ତୁ"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g>ଟି ନିୟନ୍ତ୍ରଣ ଯୋଗ କରାଯାଇଛି।</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g>ଟି ନିୟନ୍ତ୍ରଣ ଯୋଗ କରାଯାଇଛି।</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{#ଟି ନିୟନ୍ତ୍ରଣ ଯୋଗ କରାଯାଇଛି।}other{#ଟି ନିୟନ୍ତ୍ରଣ ଯୋଗ କରାଯାଇଛି।}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"କାଢ଼ି ଦିଆଯାଇଛି"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"ପସନ୍ଦ କରାଯାଇଛି"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"ପସନ୍ଦ କରାଯାଇଛି, ସ୍ଥିତି <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ଟାଇଲ୍ ଯୋଗ କରନ୍ତୁ"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ଟାଇଲ୍ ଯୋଗ କର ନାହିଁ"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ଉପଯୋଗକର୍ତ୍ତା ଚୟନ କର"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g>ଟି ଆପ ସକ୍ରିୟ ଅଛି</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g>ଟି ଆପ ସକ୍ରିୟ ଅଛି</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{#ଟି ଆପ ସକ୍ରିୟ ଅଛି}other{#ଟି ଆପ ସକ୍ରିୟ ଅଛି}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"ନୂଆ ସୂଚନା"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ସକ୍ରିୟ ଆପଗୁଡ଼ିକ"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ଆପଣ ଏହି ଆପ୍ସକୁ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ ସେଗୁଡ଼ିକ ସକ୍ରିୟ ରହିଥାଏ ଏବଂ ଚାଲୁଥାଏ। ଏହା ସେଗୁଡ଼ିକର କାର୍ଯ୍ୟକ୍ଷମତାକୁ ଉନ୍ନତ କରେ, କିନ୍ତୁ ଏହା ମଧ୍ୟ ବ୍ୟାଟେରୀ ଲାଇଫକୁ ପ୍ରଭାବିତ କରିପାରେ।"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ଆଲାରାମ ସେଟ"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"କ୍ୟାମେରା ଏବଂ ମାଇକ ବନ୍ଦ ଅଛି"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{#ଟି ବିଜ୍ଞପ୍ତି}other{#ଟି ବିଜ୍ଞପ୍ତି}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ବ୍ରଡକାଷ୍ଟ କରୁଛି"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବ୍ରଡକାଷ୍ଟ କରିବା ବନ୍ଦ କରିବେ?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ଯଦି ଆପଣ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ବ୍ରଡକାଷ୍ଟ କରନ୍ତି କିମ୍ବା ଆଉଟପୁଟ ବଦଳାନ୍ତି, ତେବେ ଆପଣଙ୍କ ବର୍ତ୍ତମାନର ବ୍ରଡକାଷ୍ଟ ବନ୍ଦ ହୋଇଯିବ"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ବ୍ରଡକାଷ୍ଟ କରନ୍ତୁ"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ଆଉଟପୁଟ ବଦଳାନ୍ତୁ"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ଅଜଣା"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml
index 2d9fb84790cd..acaa3fb6f6a8 100644
--- a/packages/SystemUI/res/values-or/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"ବନ୍ଦ ଅଛି"</item>
<item msgid="460891964396502657">"ଚାଲୁ ଅଛି"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"ଉପଲବ୍ଧ ନାହିଁ"</item>
+ <item msgid="8014986104355098744">"ବନ୍ଦ ଅଛି"</item>
+ <item msgid="5966994759929723339">"ଚାଲୁ ଅଛି"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 29e82182f94e..d41e43c3c935 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"ਡੀਵਾਈਸ ਲਾਕ ਹੈ"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ਚਿਹਰਾ ਸਕੈਨ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ਭੇਜੋ"</string>
- <string name="phone_label" msgid="5715229948920451352">"ਫ਼ੋਨ ਖੋਲ੍ਹੋ"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"ਅਵਾਜ਼ੀ ਸਹਾਇਕ ਖੋਲ੍ਹੋ"</string>
- <string name="camera_label" msgid="8253821920931143699">"ਕੈਮਰਾ ਖੋਲ੍ਹੋ"</string>
<string name="cancel" msgid="1089011503403416730">"ਰੱਦ ਕਰੋ"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"ਤਸਦੀਕ ਕਰੋ"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"\'ਸੈਂਸਰ ਬੰਦ ਕਰੋ\' ਨੂੰ ਕਿਰਿਆਸ਼ੀਲ ਕਰੋ"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਹਟਾਓ।"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">ਅੰਦਰ <xliff:g id="NUMBER_1">%s</xliff:g> ਹੋਰ ਸੂਚਨਾਵਾਂ।</item>
- <item quantity="other">ਅੰਦਰ <xliff:g id="NUMBER_1">%s</xliff:g> ਹੋਰ ਸੂਚਨਾਵਾਂ।</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{ਗਰੁੱਪ ਵਿੱਚ # ਹੋਰ ਸੂਚਨਾ ਹੈ।}one{ਗਰੁੱਪ ਵਿੱਚ # ਹੋਰ ਸੂਚਨਾ ਹੈ।}other{ਗਰੁੱਪ ਵਿੱਚ # ਹੋਰ ਸੂਚਨਾਵਾਂ ਹਨ।}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"ਸਕ੍ਰੀਨ ਲੈਂਡਸਕੇਪ ਅਨੁਕੂਲਨ ਵਿੱਚ ਲਾਕ ਕੀਤੀ ਹੈ।"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"ਸਕ੍ਰੀਨ ਪੋਰਟਰੇਟ ਅਨੁਕੂਲਨ ਵਿੱਚ ਲਾਕ ਕੀਤੀ ਗਈ ਹੈ।"</string>
<string name="dessert_case" msgid="9104973640704357717">"ਡੈਜ਼ਰਟ ਕੇਸ"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ਸਵੈ-ਘੁਮਾਓ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ਸਕ੍ਰੀਨ ਨੂੰ ਆਪਣੇ ਆਪ ਘੁੰਮਾਓ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ਟਿਕਾਣਾ"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ਸਕ੍ਰੀਨ ਸੇਵਰ"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"ਕੈਮਰਾ ਪਹੁੰਚ"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"ਮਾਈਕ ਪਹੁੰਚ"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ਉਪਲਬਧ"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"ਹੌਟਸਪੌਟ"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"ਡਾਟਾ ਸੇਵਰ ਚਾਲੂ ਹੈ"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d ਡੀਵਾਈਸ</item>
- <item quantity="other">%d ਡੀਵਾਈਸਾਂ</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# ਡੀਵਾਈਸ}one{# ਡੀਵਾਈਸ}other{# ਡੀਵਾਈਸ}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"ਫਲੈਸ਼ਲਾਈਟ"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"ਕੈਮਰਾ ਵਰਤੋਂ ਵਿੱਚ ਹੈ"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"ਮੋਬਾਈਲ ਡਾਟਾ"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"ਦੁਬਾਰਾ ਟੈਪ ਕਰੋ"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"ਖੋਲ੍ਹਣ ਲਈ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"ਖੋਲ੍ਹਣ ਲਈ \'ਅਣਲਾਕ ਕਰੋ\' ਪ੍ਰਤੀਕ ਨੂੰ ਦਬਾਓ"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ਚਿਹਰੇ ਰਾਹੀਂ ਅਣਲਾਕ ਕੀਤਾ ਗਿਆ। ਖੋਲ੍ਹਣ ਲਈ \'ਅਣਲਾਕ ਕਰੋ\' ਪ੍ਰਤੀਕ ਨੂੰ ਦਬਾਓ।"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ਚਿਹਰੇ ਰਾਹੀਂ ਅਣਲਾਕ ਕੀਤਾ ਗਿਆ। ਖੋਲ੍ਹਣ ਲਈ ਦਬਾਓ।"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ਚਿਹਰੇ ਦੀ ਪਛਾਣ ਹੋਈ। ਖੋਲ੍ਹਣ ਲਈ ਦਬਾਓ।"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਸੈਸ਼ਨ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ਹਾਂ, ਜਾਰੀ ਰੱਖੋ"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"ਮਹਿਮਾਨ ਮੋਡ"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"ਤੁਸੀਂ ਮਹਿਮਾਨ ਮੋਡ ਵਿੱਚ ਹੋ"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ਨਵੇਂ ਵਰਤੋਂਕਾਰ ਨੂੰ ਸ਼ਾਮਲ ਕਰਨ ਨਾਲ ਮੌਜੂਦਾ ਮਹਿਮਾਨ ਮੋਡ ਚਲਾ ਜਾਵੇਗਾ ਅਤੇ ਮੌਜੂਦਾ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਦੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਮਿਟ ਜਾਵੇਗਾ।"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰਨ ਦੀ ਸੀਮਾ ਪੂਰੀ ਹੋਈ"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">ਤੁਸੀਂ <xliff:g id="COUNT">%d</xliff:g> ਤੱਕ ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰ ਸਕਦੇ ਹੋ।</item>
- <item quantity="other">ਤੁਸੀਂ <xliff:g id="COUNT">%d</xliff:g> ਤੱਕ ਵਰਤੋਂਕਾਰਾਂ ਨੂੰ ਸ਼ਾਮਲ ਕਰ ਸਕਦੇ ਹੋ।</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{ਸਿਰਫ਼ ਇੱਕ ਵਰਤੋਂਕਾਰ ਹੀ ਬਣਾਇਆ ਜਾ ਸਕਦਾ ਹੈ।}one{ਤੁਸੀਂ # ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰ ਸਕਦੇ ਹੋ।}other{ਤੁਸੀਂ # ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰ ਸਕਦੇ ਹੋ।}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"ਕੀ ਵਰਤੋਂਕਾਰ ਹਟਾਉਣਾ ਹੈ?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"ਇਸ ਉਪਭੋਗਤਾ ਦੇ ਸਾਰੇ ਐਪਸ ਅਤੇ ਡਾਟਾ ਮਿਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"ਹਟਾਓ"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"ਮੈਨੂੰ ਯਾਦ ਕਰਵਾਓ"</string>
<string name="snooze_undo" msgid="2738844148845992103">"ਅਣਕੀਤਾ ਕਰੋ"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> ਲਈ ਸਨੂਜ਼ ਕੀਤਾ ਗਿਆ"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one"> %d ਘੰਟਾ</item>
- <item quantity="other">%d ਘੰਟੇ</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d ਮਿੰਟ</item>
- <item quantity="other"> %d ਮਿੰਟ</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ਘੰਟਾ}=2{# ਘੰਟੇ}one{# ਘੰਟਾ}other{# ਘੰਟੇ}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# ਮਿੰਟ}one{# ਮਿੰਟ}other{# ਮਿੰਟ}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"ਬੈਟਰੀ ਸੇਵਰ"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"ਬਟਨ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"ਸੁਚੇਤਨਾਵਾਂ"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"ਬੈਟਰੀ"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"ਆਮ ਸੁਨੇਹੇ"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"ਸੈੱਟਅੱਪ ਕਰੋ"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"ਸਟੋਰੇਜ"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"ਸੰਕੇਤ"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ਟੌਗਲ ਕਰੋ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ਡੀਵਾਈਸ ਕੰਟਰੋਲ"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਐਪ ਚੁਣੋ"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ।</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕੀਤੇ ਗਏ।</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ।}one{# ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ।}other{# ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕੀਤੇ ਗਏ।}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"ਹਟਾਇਆ ਗਿਆ"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"ਮਨਪਸੰਦ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"ਮਨਪਸੰਦ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ, ਸਥਾਨ <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ਟਾਇਲ ਸ਼ਾਮਲ ਨਾ ਕਰੋ"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ਵਰਤੋਂਕਾਰ ਚੁਣੋ"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> ਐਪ ਕਿਰਿਆਸ਼ੀਲ ਹੈ</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ਐਪਾਂ ਕਿਰਿਆਸ਼ੀਲ ਹਨ</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# ਐਪ ਕਿਰਿਆਸ਼ੀਲ ਹੈ}one{# ਐਪ ਕਿਰਿਆਸ਼ੀਲ ਹੈ}other{# ਐਪਾਂ ਕਿਰਿਆਸ਼ੀਲ ਹਨ}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"ਨਵੀਂ ਜਾਣਕਾਰੀ"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ਕਿਰਿਆਸ਼ੀਲ ਐਪਾਂ"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ਇਹ ਐਪਾਂ ਕਿਰਿਆਸ਼ੀਲ ਹਨ ਅਤੇ ਚੱਲ ਰਹੀਆਂ ਹਨ, ਭਾਵੇਂ ਤੁਸੀਂ ਇਨ੍ਹਾਂ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕਰ ਰਹੇ। ਇਸ ਨਾਲ ਇਨ੍ਹਾਂ ਦੀ ਪ੍ਰਕਾਰਜਾਤਮਕਤਾ ਬਿਹਤਰ ਹੁੰਦੀ ਹੈ ਪਰ ਬੈਟਰੀ ਲਾਈਫ਼ ਵੀ ਪ੍ਰਭਾਵਿਤ ਹੋ ਸਕਦੀ ਹੈ।"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ਅਲਾਰਮ ਸੈੱਟ ਹੈ"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ਕੈਮਰਾ ਅਤੇ ਮਾਈਕ ਬੰਦ ਹਨ"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ਸੂਚਨਾ}one{# ਸੂਚਨਾ}other{# ਸੂਚਨਾਵਾਂ}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ਪ੍ਰਸਾਰਨ"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਦੇ ਪ੍ਰਸਾਰਨ ਨੂੰ ਰੋਕਣਾ ਹੈ?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ਜੇ ਤੁਸੀਂ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ਦਾ ਪ੍ਰਸਾਰਨ ਕਰਦੇ ਹੋ ਜਾਂ ਆਊਟਪੁੱਟ ਬਦਲਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਪ੍ਰਸਾਰਨ ਰੁਕ ਜਾਵੇਗਾ"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ਦਾ ਪ੍ਰਸਾਰਨ ਕਰੋ"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ਆਊਟਪੁੱਟ ਬਦਲੋ"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ਅਗਿਆਤ"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
index 737b2b60ac60..9653b923224a 100644
--- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"ਬੰਦ"</item>
<item msgid="460891964396502657">"ਚਾਲੂ"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"ਉਪਲਬਧ ਨਹੀਂ"</item>
+ <item msgid="8014986104355098744">"ਬੰਦ"</item>
+ <item msgid="5966994759929723339">"ਚਾਲੂ"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 57526e783cb1..36bbbc4ccbc0 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Urządzenie zablokowane"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skanowanie twarzy"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Wyślij"</string>
- <string name="phone_label" msgid="5715229948920451352">"otwórz telefon"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"otwórz pomoc głosową"</string>
- <string name="camera_label" msgid="8253821920931143699">"otwórz aparat"</string>
<string name="cancel" msgid="1089011503403416730">"Anuluj"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potwierdź"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Spróbuj jeszcze raz"</string>
@@ -205,12 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Wyłączenie czujników aktywne"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Usuń wszystkie powiadomienia."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="few">Jeszcze <xliff:g id="NUMBER_1">%s</xliff:g> powiadomienia w grupie.</item>
- <item quantity="many">Jeszcze <xliff:g id="NUMBER_1">%s</xliff:g> powiadomień w grupie.</item>
- <item quantity="other">Jeszcze <xliff:g id="NUMBER_1">%s</xliff:g> powiadomienia w grupie.</item>
- <item quantity="one">Jeszcze <xliff:g id="NUMBER_0">%s</xliff:g> powiadomienie w grupie.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Jeszcze # powiadomienie w grupie.}few{Jeszcze # powiadomienia w grupie.}many{Jeszcze # powiadomień w grupie.}other{Jeszcze # powiadomienia w grupie.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Ekran jest zablokowany w orientacji poziomej."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Ekran jest zablokowany w orientacji pionowej."</string>
<string name="dessert_case" msgid="9104973640704357717">"Półka ze słodkościami"</string>
@@ -228,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autoobracanie"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Autoobracanie ekranu"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokalizacja"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Wygaszacz ekranu"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Dostęp do aparatu"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Dostęp do mikrofonu"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Odblokowany"</string>
@@ -257,12 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Włączam…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Włączono Oszczędzanie danych"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="few">%d urządzenia</item>
- <item quantity="many">%d urządzeń</item>
- <item quantity="other">%d urządzenia</item>
- <item quantity="one">%d urządzenie</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# urządzenie}few{# urządzenia}many{# urządzeń}other{# urządzenia}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Latarka"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Aparat w użyciu"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobilna transmisja danych"</string>
@@ -319,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Kliknij jeszcze raz"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Przesuń w górę, by otworzyć"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Aby otworzyć, kliknij ikonę odblokowywania"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Odblokowano skanem twarzy. Aby otworzyć, kliknij ikonę odblokowywania."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Odblokowano rozpoznawaniem twarzy. Naciśnij, by otworzyć."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Twarz rozpoznana. Naciśnij, by otworzyć."</string>
@@ -355,13 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcesz kontynuować sesję?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Rozpocznij nową"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Tak, kontynuuj"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Tryb gościa"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Jesteś w trybie gościa"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Dodanie nowego użytkownika spowoduje zamknięcie trybu gościa. Wszystkie aplikacje i dane z obecnej sesji gościa zostaną usunięte."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Osiągnięto limit użytkowników"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="few">Możesz dodać maksymalnie <xliff:g id="COUNT">%d</xliff:g> użytkowników.</item>
- <item quantity="many">Możesz dodać maksymalnie <xliff:g id="COUNT">%d</xliff:g> użytkowników.</item>
- <item quantity="other">Możesz dodać maksymalnie <xliff:g id="COUNT">%d</xliff:g> użytkownika.</item>
- <item quantity="one">Można utworzyć tylko jednego użytkownika.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Można utworzyć tylko 1 użytkownika.}few{Możesz dodać maksymalnie # użytkowników}many{Możesz dodać maksymalnie # użytkowników}other{Możesz dodać maksymalnie # użytkownika}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Usunąć użytkownika?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Wszystkie aplikacje i dane tego użytkownika zostaną usunięte."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Usuń"</string>
@@ -547,18 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Przypomnij"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Cofnij"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Odłożono na <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="few">%d godziny</item>
- <item quantity="many">%d godzin</item>
- <item quantity="other">%d godziny</item>
- <item quantity="one">%d godzina</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="few">%d minuty</item>
- <item quantity="many">%d minut</item>
- <item quantity="other">%d minuty</item>
- <item quantity="one">]%d minuta</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# godzina}=2{# godziny}few{# godziny}many{# godzin}other{# godziny}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuta}few{# minuty}many{# minut}other{# minuty}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Oszczędzanie baterii"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Przycisk <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -707,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alerty"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Zrzuty ekranu"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Wiadomości"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Aplikacje błyskawiczne"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Konfiguracja"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Pamięć wewnętrzna"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Wskazówki"</string>
<string name="instant_apps" msgid="8337185853050247304">"Aplikacje błyskawiczne"</string>
@@ -781,12 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"przełącz"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Sterowanie urządzeniami"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Wybierz aplikację, do której chcesz dodać elementy sterujące"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="few">Dodano <xliff:g id="NUMBER_1">%s</xliff:g> elementy sterujące</item>
- <item quantity="many">Dodano <xliff:g id="NUMBER_1">%s</xliff:g> elementów sterujących</item>
- <item quantity="other">Dodano <xliff:g id="NUMBER_1">%s</xliff:g> elementu sterującego</item>
- <item quantity="one">Dodano <xliff:g id="NUMBER_0">%s</xliff:g> element sterujący</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Dodano # element sterujący.}few{Dodano # elementy sterujące.}many{Dodano # elementów sterujących.}other{Dodano # elementu sterującego.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Usunięto"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Dodano do ulubionych"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Dodano do ulubionych, pozycja <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -943,12 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj kafelek"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nie dodawaj kafelka"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Wybierz użytkownika"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> aplikacje są aktywne</item>
- <item quantity="many"><xliff:g id="COUNT_1">%s</xliff:g> aplikacji jest aktywnych</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplikacji jest aktywne</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplikacja jest aktywna</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikacja jest aktywna}few{# aplikacje są aktywne}many{# aplikacji jest aktywnych}other{# aplikacji jest aktywne}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nowa informacja"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktywne aplikacje"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Te aplikacje są aktywne i działają, nawet gdy ich nie używasz. Zwiększa to ich funkcjonalność, ale może również pogarszać żywotność baterii."</string>
@@ -977,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm ustawiony"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Aparat i mikrofon są wyłączone"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# powiadomienie}few{# powiadomienia}many{# powiadomień}other{# powiadomienia}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmisja"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zatrzymaj transmisję aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jeśli transmitujesz aplikację <xliff:g id="SWITCHAPP">%1$s</xliff:g> lub zmieniasz dane wyjściowe, Twoja obecna transmisja zostanie zakończona"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmisja aplikacji <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Zmień dane wyjściowe"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Brak informacji"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
index c1a4059f6393..50650986c517 100644
--- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Wyłączony"</item>
<item msgid="460891964396502657">"Włączony"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Brak dostępu"</item>
+ <item msgid="8014986104355098744">"Wyłączono"</item>
+ <item msgid="5966994759929723339">"Włączono"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 280ac19b7669..0cb7420785ba 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Verificando rosto"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
- <string name="phone_label" msgid="5715229948920451352">"abrir telefone"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"abrir assistência de voz"</string>
- <string name="camera_label" msgid="8253821920931143699">"abrir câmera"</string>
<string name="cancel" msgid="1089011503403416730">"Cancelar"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmar"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Tentar novamente"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"A opção \"Sensores desativados\" está ativa"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Limpar todas as notificações."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"Mais <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">Mais <xliff:g id="NUMBER_1">%s</xliff:g> notificações no grupo.</item>
- <item quantity="other">Mais <xliff:g id="NUMBER_1">%s</xliff:g> notificações no grupo.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Mais # notificação no grupo.}one{Mais # notificação no grupo.}other{Mais # notificações no grupo.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"A tela está bloqueada na orientação paisagem."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"A tela está bloqueada na orientação retrato."</string>
<string name="dessert_case" msgid="9104973640704357717">"Mostruário de sobremesas"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Giro automático da tela"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Protetor de tela"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Acesso à câmera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Acesso ao microfone"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponível"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Ponto de acesso"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Ativando…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Economia de dados ativada"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d dispositivo</item>
- <item quantity="other">%d dispositivos</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# dispositivo}one{# dispositivo}other{# dispositivos}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lanterna"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Câmera em uso"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Dados móveis"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Toque novamente"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Deslize para cima para abrir"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Pressione o ícone de desbloqueio para abrir"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Desbloqueado pelo rosto. Toque no ícone do cadeado para abrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Desbloqueado pelo rosto. Pressione para abrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Rosto reconhecido. Pressione para abrir."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sim, continuar"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Modo visitante"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Você está no modo visitante"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ao adicionar um novo usuário, o dispositivo vai sair do modo visitante e excluir todos os apps e dados da Sessão de visitante atual."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Limite de usuários atingido"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">É possível adicionar até <xliff:g id="COUNT">%d</xliff:g> usuário.</item>
- <item quantity="other">É possível adicionar até <xliff:g id="COUNT">%d</xliff:g> usuários.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Você só pode criar 1 usuário.}one{Você pode adicionar até # usuário.}other{Você pode adicionar até # usuários.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Remover usuário?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Todos os apps e dados deste usuário serão excluídos."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Remover"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Lembrete"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Desfazer"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Adiada para <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d hora</item>
- <item quantity="other">%d horas</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d minuto</item>
- <item quantity="other">%d minutos</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hora}=2{# horas}one{# hora}other{# horas}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuto}one{# minuto}other{# minuto}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Economia de bateria"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Botão <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alertas"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturas de tela"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Mensagens gerais"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Configurar"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Armazenamento"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Dicas"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"alternar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controles do dispositivo"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Escolha um app para adicionar controles"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> controle adicionado.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> controles adicionados.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# controle adicionado.}one{# controle adicionado.}other{# controles adicionados.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Removido"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Adicionado como favorito"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Adicionado como favorito (posição <xliff:g id="NUMBER">%d</xliff:g>)"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adicionar bloco"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Não adicionar bloco"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecionar usuário"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> app está ativo</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apps estão ativos</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está ativo}one{# apps está ativo}other{# apps estão ativos}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova informação"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativos"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também pode afetar a duração da bateria."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarme definido"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"A câmera e o microfone estão desativados"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificação}one{# notificação}other{# notificações}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmitindo"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Interromper a transmissão do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se você transmitir o app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou mudar a saída, a transmissão atual será interrompida"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmitir <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Mudar saída"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconhecido"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d de MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
index abf8749e84d7..bc7efe6a8682 100644
--- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Desativado"</item>
<item msgid="460891964396502657">"Ativado"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Indisponível"</item>
+ <item msgid="8014986104355098744">"Desativado"</item>
+ <item msgid="5966994759929723339">"Ativado"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index c49ddcbb4458..9702b9759793 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"A analisar o rosto…"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
- <string name="phone_label" msgid="5715229948920451352">"abrir telemóvel"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"abrir assistente de voz"</string>
- <string name="camera_label" msgid="8253821920931143699">"abrir câmara"</string>
<string name="cancel" msgid="1089011503403416730">"Cancelar"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmar"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Tentar novamente"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensores desativados ativo"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Limpar todas as notificações."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">Mais <xliff:g id="NUMBER_1">%s</xliff:g> notificações no grupo.</item>
- <item quantity="one">Mais <xliff:g id="NUMBER_0">%s</xliff:g> notificação no grupo.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Mais # notificação no grupo.}other{Mais # notificações no grupo.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"O ecrã está bloqueado na orientação horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"O ecrã está bloqueado na orientação vertical."</string>
<string name="dessert_case" msgid="9104973640704357717">"Vitrina de sobremesas"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotação auto."</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rodar o ecrã automaticamente"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Proteção de ecrã"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Acesso câmara"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Ac. microfone"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponível"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Zona Wi-Fi"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"A ativar..."</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Poup. dados ativada"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d dispositivos</item>
- <item quantity="one">%d dispositivo</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# dispositivo}other{# dispositivos}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lanterna"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Câmara em utilização"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Dados móveis"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Toque novamente"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Deslize rapidamente para cima para abrir"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Prima o ícone de desbloqueio para abrir"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Desbloqueio com a face. Prima ícone de desbloqueio p/ abrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Desbloqueado com o rosto. Prima para abrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Rosto reconhecido. Prima para abrir."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Pretende continuar a sessão?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sim, continuar"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Modo convidado"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Está no modo convidado"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ao adicionar um novo utilizador, o modo convidado é fechado e todas as apps e dados da sessão de convidado atual são eliminados."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Limite de utilizadores alcançado"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Pode adicionar até <xliff:g id="COUNT">%d</xliff:g> utilizadores.</item>
- <item quantity="one">Apenas é possível criar um utilizador.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Só é possível criar um utilizador.}other{É possível adicionar até # utilizadores.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Remover o utilizador?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Serão eliminados todos os dados e todas as aplicações deste utilizador."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Remover"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Lembrar-me"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Anular"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Suspensa por <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d horas</item>
- <item quantity="one">%d hora</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minutos</item>
- <item quantity="one">%d minuto</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hora}=2{# horas}other{# horas}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuto}other{# minutos}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Poupança de bateria"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Botão <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Início"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alertas"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturas de ecrã"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Mensagens gerais"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Apps instantâneas"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Configuração"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Armazenamento"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Sugestões"</string>
<string name="instant_apps" msgid="8337185853050247304">"Apps instantâneas"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ativar/desativar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controlos de dispositivos"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Escolha uma app para adicionar controlos"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> controlos adicionados.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> controlo adicionado.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# controlo adicionado.}other{# controlos adicionados.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Removido"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Adicionado aos favoritos"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Adicionados aos favoritos, posição <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adicionar mosaico"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Não adicion. mosaico"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecione utilizador"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apps estão ativas</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> app está ativa</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está ativa}other{# apps estão ativas}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Novas informações"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativas"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Estas apps estão ativas e a funcionar, mesmo quando não as está a usar. Isto melhora a sua funcionalidade, mas também afeta a autonomia da bateria."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarme definido"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"A câmara e o microfone estão desativados"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificação}other{# notificações}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"A transmitir"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Interromper a transmissão da app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se transmitir a app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou alterar a saída, a sua transmissão atual é interrompida"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmita a app <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Altere a saída"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconhecida"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d de MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
index c8e557b38f9b..0a0102032c6a 100644
--- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Desativado"</item>
<item msgid="460891964396502657">"Ativado"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Indisponível"</item>
+ <item msgid="8014986104355098744">"Desativado"</item>
+ <item msgid="5966994759929723339">"Ativado"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 280ac19b7669..0cb7420785ba 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispositivo bloqueado"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Verificando rosto"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
- <string name="phone_label" msgid="5715229948920451352">"abrir telefone"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"abrir assistência de voz"</string>
- <string name="camera_label" msgid="8253821920931143699">"abrir câmera"</string>
<string name="cancel" msgid="1089011503403416730">"Cancelar"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmar"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Tentar novamente"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"A opção \"Sensores desativados\" está ativa"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Limpar todas as notificações."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"Mais <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">Mais <xliff:g id="NUMBER_1">%s</xliff:g> notificações no grupo.</item>
- <item quantity="other">Mais <xliff:g id="NUMBER_1">%s</xliff:g> notificações no grupo.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Mais # notificação no grupo.}one{Mais # notificação no grupo.}other{Mais # notificações no grupo.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"A tela está bloqueada na orientação paisagem."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"A tela está bloqueada na orientação retrato."</string>
<string name="dessert_case" msgid="9104973640704357717">"Mostruário de sobremesas"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Giro automático da tela"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Protetor de tela"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Acesso à câmera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Acesso ao microfone"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponível"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Ponto de acesso"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Ativando…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Economia de dados ativada"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d dispositivo</item>
- <item quantity="other">%d dispositivos</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# dispositivo}one{# dispositivo}other{# dispositivos}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lanterna"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Câmera em uso"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Dados móveis"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Toque novamente"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Deslize para cima para abrir"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Pressione o ícone de desbloqueio para abrir"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Desbloqueado pelo rosto. Toque no ícone do cadeado para abrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Desbloqueado pelo rosto. Pressione para abrir."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Rosto reconhecido. Pressione para abrir."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sim, continuar"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Modo visitante"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Você está no modo visitante"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ao adicionar um novo usuário, o dispositivo vai sair do modo visitante e excluir todos os apps e dados da Sessão de visitante atual."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Limite de usuários atingido"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">É possível adicionar até <xliff:g id="COUNT">%d</xliff:g> usuário.</item>
- <item quantity="other">É possível adicionar até <xliff:g id="COUNT">%d</xliff:g> usuários.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Você só pode criar 1 usuário.}one{Você pode adicionar até # usuário.}other{Você pode adicionar até # usuários.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Remover usuário?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Todos os apps e dados deste usuário serão excluídos."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Remover"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Lembrete"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Desfazer"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Adiada para <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d hora</item>
- <item quantity="other">%d horas</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d minuto</item>
- <item quantity="other">%d minutos</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hora}=2{# horas}one{# hora}other{# horas}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuto}one{# minuto}other{# minuto}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Economia de bateria"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Botão <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alertas"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturas de tela"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Mensagens gerais"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Configurar"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Armazenamento"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Dicas"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"alternar"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Controles do dispositivo"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Escolha um app para adicionar controles"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> controle adicionado.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> controles adicionados.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# controle adicionado.}one{# controle adicionado.}other{# controles adicionados.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Removido"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Adicionado como favorito"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Adicionado como favorito (posição <xliff:g id="NUMBER">%d</xliff:g>)"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adicionar bloco"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Não adicionar bloco"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecionar usuário"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> app está ativo</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> apps estão ativos</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está ativo}one{# apps está ativo}other{# apps estão ativos}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova informação"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativos"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também pode afetar a duração da bateria."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarme definido"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"A câmera e o microfone estão desativados"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificação}one{# notificação}other{# notificações}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmitindo"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Interromper a transmissão do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se você transmitir o app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou mudar a saída, a transmissão atual será interrompida"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmitir <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Mudar saída"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconhecido"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d de MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
index abf8749e84d7..bc7efe6a8682 100644
--- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Desativado"</item>
<item msgid="460891964396502657">"Ativado"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Indisponível"</item>
+ <item msgid="8014986104355098744">"Desativado"</item>
+ <item msgid="5966994759929723339">"Ativado"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 2be95705514a..99d2f06b2970 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispozitiv blocat"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanarea chipului"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Trimiteți"</string>
- <string name="phone_label" msgid="5715229948920451352">"deschideți telefonul"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"deschideți asistentul vocal"</string>
- <string name="camera_label" msgid="8253821920931143699">"deschideți camera foto"</string>
<string name="cancel" msgid="1089011503403416730">"Anulați"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmați"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Încercați din nou"</string>
@@ -205,11 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Dezactivarea senzorilor este activă"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Ștergeți toate notificările."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="few">Încă <xliff:g id="NUMBER_1">%s</xliff:g> notificări în grup.</item>
- <item quantity="other">Încă <xliff:g id="NUMBER_1">%s</xliff:g> de notificări în grup.</item>
- <item quantity="one">Încă <xliff:g id="NUMBER_0">%s</xliff:g> notificare în grup.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Încă # notificare în grup.}few{Încă # notificări în grup.}other{Încă # de notificări în grup.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Ecranul este blocat în orientarea de tip peisaj."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Ecranul este blocat în orientarea de tip portret."</string>
<string name="dessert_case" msgid="9104973640704357717">"Vitrina cu dulciuri"</string>
@@ -227,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotire automată"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotirea automată a ecranului"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Locație"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screensaver"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Acces la cameră"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Acces la microfon"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponibil"</string>
@@ -256,11 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Se activează..."</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Economizor date activat"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="few">%d dispozitive</item>
- <item quantity="other">%d de dispozitive</item>
- <item quantity="one">%d dispozitiv</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# dispozitiv}few{# dispozitive}other{# de dispozitive}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Lanternă"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Se folosește camera foto"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Date mobile"</string>
@@ -317,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Atingeți din nou"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Glisați în sus pentru a deschide"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Apăsați pictograma de deblocare pentru a deschide"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"S-a deblocat cu ajutorul feței. Apăsați pictograma de deblocare pentru a deschide"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"S-a deblocat cu ajutorul feței. Apăsați pentru a deschide."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Chipul a fost recunoscut. Apăsați pentru a deschide."</string>
@@ -353,12 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Vreți să continuați sesiunea?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Începeți din nou"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, continuați"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Modul pentru invitați"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Folosiți modul pentru invitați"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Dacă adăugați un utilizator nou, veți ieși din modul pentru invitați și se vor șterge toate aplicațiile și datele din sesiunea pentru invitați actuală."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Ați atins limita de utilizatori"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="few">Puteți adăuga maximum <xliff:g id="COUNT">%d</xliff:g> utilizatori.</item>
- <item quantity="other">Puteți adăuga maximum <xliff:g id="COUNT">%d</xliff:g> de utilizatori.</item>
- <item quantity="one">Poate fi creat doar un utilizator.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Se poate crea doar un utilizator.}few{Puteți adăuga până la # utilizatori.}other{Puteți adăuga până la # de utilizatori.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Eliminați utilizatorul?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Toate aplicațiile și datele acestui utilizator vor fi șterse."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Eliminați"</string>
@@ -544,16 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Reamintește-mi"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Anulați"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Amânată <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="few">%d ore</item>
- <item quantity="other">%d de ore</item>
- <item quantity="one">%d oră</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="few">%d minute</item>
- <item quantity="other">%d de minute</item>
- <item quantity="one">%d minut</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# oră}=2{# ore}few{# ore}other{# de ore}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minut}few{# minute}other{# de minute}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Economisirea bateriei"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Butonul <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"La început"</string>
@@ -702,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Alerte"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Baterie"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturi de ecran"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Mesaje generale"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Aplicații instantanee"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Configurarea"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Stocare"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Indicii"</string>
<string name="instant_apps" msgid="8337185853050247304">"Aplicații instantanee"</string>
@@ -776,11 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"Activați / dezactivați"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Comenzile dispozitivelor"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Alegeți aplicația pentru a adăuga comenzi"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="few">S-au adăugat <xliff:g id="NUMBER_1">%s</xliff:g> comenzi.</item>
- <item quantity="other">S-au adăugat <xliff:g id="NUMBER_1">%s</xliff:g> de comenzi.</item>
- <item quantity="one">S-a adăugat <xliff:g id="NUMBER_0">%s</xliff:g> comandă.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{S-a adăugat # comandă.}few{S-au adăugat # comenzi.}other{S-au adăugat # de comenzi.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Eliminată"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Marcată ca preferată"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Marcată ca preferată, poziția <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -937,11 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adăugați un card"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nu adăugați un card"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Alegeți utilizatorul"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> aplicații sunt active</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> de aplicații sunt active</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplicație este activă</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplicație este activă}few{# aplicații sunt active}other{# de aplicații sunt active}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Informații noi"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplicații active"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Aceste aplicații sunt active și rulează, chiar dacă nu le folosiți. Astfel, funcțiile lor sunt îmbunătățite, dar autonomia bateriei poate fi afectată."</string>
@@ -970,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmă setată"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Camera și microfonul sunt dezactivate"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificare}few{# notificări}other{# de notificări}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Se difuzează"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Opriți difuzarea <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Dacă difuzați <xliff:g id="SWITCHAPP">%1$s</xliff:g> sau schimbați rezultatul, difuzarea actuală se va opri"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Difuzați <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Schimbați rezultatul"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Necunoscută"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EE, z LLL"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
index 1ad0a75f39ba..7b7bb3ac11d8 100644
--- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Dezactivat"</item>
<item msgid="460891964396502657">"Activat"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Indisponibil"</item>
+ <item msgid="8014986104355098744">"Dezactivat"</item>
+ <item msgid="5966994759929723339">"Activat"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 6b9e54ea2d5a..817fdc0e275d 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Устройство заблокировано"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Сканирование лица"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Отправить"</string>
- <string name="phone_label" msgid="5715229948920451352">"Открыть телефон."</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"включить аудиоподсказки"</string>
- <string name="camera_label" msgid="8253821920931143699">"Открыть камеру."</string>
<string name="cancel" msgid="1089011503403416730">"Отмена"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Подтвердить"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Повторить попытку"</string>
@@ -205,12 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Датчики отключены"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Удалить все уведомления"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">Ещё <xliff:g id="NUMBER_1">%s</xliff:g> уведомление.</item>
- <item quantity="few">Ещё <xliff:g id="NUMBER_1">%s</xliff:g> уведомления.</item>
- <item quantity="many">Ещё <xliff:g id="NUMBER_1">%s</xliff:g> уведомлений.</item>
- <item quantity="other">Ещё <xliff:g id="NUMBER_1">%s</xliff:g> уведомления.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Доступно ещё # уведомление.}one{Доступно ещё # уведомление.}few{Доступны ещё # уведомления.}many{Доступно ещё # уведомлений.}other{Доступно ещё # уведомления.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Выбрана только альбомная ориентация экрана."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Выбрана только книжная ориентация экрана."</string>
<string name="dessert_case" msgid="9104973640704357717">"Коробка со сладостями"</string>
@@ -228,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоповорот"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоповорот экрана"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Геолокация"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Заставка"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Доступ к камере"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Доступ к микрофону"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Доступно"</string>
@@ -257,12 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Точка доступа"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Включение…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Экономия трафика вкл."</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d устройство</item>
- <item quantity="few">%d устройства</item>
- <item quantity="many">%d устройств</item>
- <item quantity="other">%d устройства</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# устройство}one{# устройство}few{# устройства}many{# устройств}other{# устройства}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Фонарик"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Используется камера"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Мобильный интернет"</string>
@@ -319,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Нажмите ещё раз"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Проведите вверх, чтобы открыть"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Нажмите на значок разблокировки."</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Сканирование выполнено. Нажмите на значок разблокировки."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Разблокировано сканированием лица. Нажмите, чтобы открыть."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Лицо распознано. Нажмите, чтобы открыть."</string>
@@ -355,13 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Продолжить сеанс?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Начать заново"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Да, продолжить"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Гостевой режим"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Включен гостевой режим"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Добавив нового пользователя, вы выйдете из гостевого режима. Все приложения и данные в текущем гостевом сеансе будут удалены."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Достигнут лимит"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Можно добавить не более <xliff:g id="COUNT">%d</xliff:g> пользователя.</item>
- <item quantity="few">Можно добавить не более <xliff:g id="COUNT">%d</xliff:g> пользователей.</item>
- <item quantity="many">Можно добавить не более <xliff:g id="COUNT">%d</xliff:g> пользователей.</item>
- <item quantity="other">Можно добавить не более <xliff:g id="COUNT">%d</xliff:g> пользователя.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Можно создать только одного пользователя.}one{Вы можете добавить до # пользователя.}few{Вы можете добавить до # пользователей.}many{Вы можете добавить до # пользователей.}other{Вы можете добавить до # пользователя.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Удалить аккаунт?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Все приложения и данные этого пользователя будут удалены."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Удалить"</string>
@@ -547,18 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Добавить напоминание"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Отменить"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Отложено на <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d час</item>
- <item quantity="few">%d часа</item>
- <item quantity="many">%d часов</item>
- <item quantity="other">%d часа</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d минута</item>
- <item quantity="few">%d минуты</item>
- <item quantity="many">%d минут</item>
- <item quantity="other">%d минуты</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# час}=2{# часа}one{# час}few{# часа}many{# часов}other{# часа}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# минута}one{# минута}few{# минуты}many{# минут}other{# минуты}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Режим энергосбережения"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Главный экран"</string>
@@ -707,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Оповещения"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Батарея"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Скриншоты"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Сообщения"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Приложения с мгновенным запуском"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Настройка"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Хранилище"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Подсказки"</string>
<string name="instant_apps" msgid="8337185853050247304">"Приложения с мгновенным запуском"</string>
@@ -781,12 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"включить или отключить"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Управление устройствами"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Чтобы добавить виджеты управления, выберите приложение"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one">Добавлен <xliff:g id="NUMBER_1">%s</xliff:g> элемент управления.</item>
- <item quantity="few">Добавлено <xliff:g id="NUMBER_1">%s</xliff:g> элемента управления.</item>
- <item quantity="many">Добавлено <xliff:g id="NUMBER_1">%s</xliff:g> элементов управления.</item>
- <item quantity="other">Добавлено <xliff:g id="NUMBER_1">%s</xliff:g> элемента управления.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Добавлен # элемент управления.}one{Добавлен # элемент управления.}few{Добавлено # элемента управления.}many{Добавлено # элементов управления.}other{Добавлено # элемента управления.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Удалено"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Добавлено в избранное"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Добавлено в избранное на позицию <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -943,12 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Добавить параметр"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не добавлять"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Выберите профиль"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one">Активно <xliff:g id="COUNT_1">%s</xliff:g> приложение</item>
- <item quantity="few">Активно <xliff:g id="COUNT_1">%s</xliff:g> приложения</item>
- <item quantity="many">Активно <xliff:g id="COUNT_1">%s</xliff:g> приложений</item>
- <item quantity="other">Активно <xliff:g id="COUNT_1">%s</xliff:g> приложения</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# приложение активно}one{# приложение активно}few{# приложения активны}many{# приложений активно}other{# приложения активно}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Новая информация"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активные приложения"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Эти приложения работают и остаются активными, даже когда вы их не используете. Это дает дополнительные возможности, но может сократить время работы от батареи."</string>
@@ -977,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Будильник установлен"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камера и микрофон отключены"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# уведомление}one{# уведомление}few{# уведомления}many{# уведомлений}other{# уведомления}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Трансляция"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Остановить трансляцию \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Если вы начнете транслировать \"<xliff:g id="SWITCHAPP">%1$s</xliff:g>\" или смените целевое устройство, текущая трансляция прервется."</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Транслировать \"<xliff:g id="SWITCHAPP">%1$s</xliff:g>\""</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Транслировать на другое устройство"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Неизвестно"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"d MMM EEEE"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
index e1ee39fd79ee..6255bd8ee26d 100644
--- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Отключен"</item>
<item msgid="460891964396502657">"Включен"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Недоступно"</item>
+ <item msgid="8014986104355098744">"Отключено"</item>
+ <item msgid="5966994759929723339">"Включено"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index be3a8ac039a3..8c7b85cbe287 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"උපාංගය අගුලු දමා ඇත"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"මුහුණ ස්කෑන් කිරීම"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"යවන්න"</string>
- <string name="phone_label" msgid="5715229948920451352">"දුරකථනය විවෘත කරන්න"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"විවෘත හඬ සහාය"</string>
- <string name="camera_label" msgid="8253821920931143699">"කැමරාව විවෘත කරන්න"</string>
<string name="cancel" msgid="1089011503403416730">"අවලංගු කරන්න"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"තහවුරු කරන්න"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"නැවත උත්සාහ කරන්න"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"සංවේදක ක්‍රියාවිරහිතය සක්‍රියයි"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"සියලු දැනුම්දීම් හිස් කරන්න."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">ඇතුළත තව දැනුම්දීම් <xliff:g id="NUMBER_1">%s</xliff:g>ක් ඇත.</item>
- <item quantity="other">ඇතුළත තව දැනුම්දීම් <xliff:g id="NUMBER_1">%s</xliff:g>ක් ඇත.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{ඇතුළත තව # දැනුම්දීමක් ඇත.}one{ඇතුළත තව දැනුම්දීම් #ක් ඇත.}other{ඇතුළත තව දැනුම්දීම් #ක් ඇත.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"තිරය තිරස් දිශානතියෙහි අගුළු දමා ඇත."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"තිරය සිරස් දිශානතිය තුළ අගුළු වැටී ඇත."</string>
<string name="dessert_case" msgid="9104973640704357717">"අතුරුපස අවස්තාව"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ස්වයංක්‍රීය කරකැවීම"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ස්වයංක්‍රීයව-භ්‍රමණය වන තිරය"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ස්ථානය"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"තිර සුරැකුම"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"කැමරා ප්‍රවේශය"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"මයික් ප්‍රවේශය"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"තිබේ"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"හොට්ස්පොට්"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ක්‍රියාවිරහිත කරමින්…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"දත්ත සුරැකුම ක්‍රියාත්මකයි"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">උපාංග %d</item>
- <item quantity="other">උපාංග %d</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# උපාංගයක්}one{උපාංග #ක්}other{උපාංග #ක්}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"සැණෙළි ආලෝකය"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"කැමරාව භාවිතයේ ඇත"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"ජංගම දත්ත"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"නැවත තට්ටු කරන්න"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"විවෘත කිරීමට ස්වයිප් කරන්න"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"විවෘත කිරීමට අගුලු හැරීමේ නිරූපකය ඔබන්න"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"මුහුණ මගින් අගුලු හරින ලදි. විවෘත කිරීමට අගුලු හැරීමේ නිරූපකය ඔබන්න."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"මුහුණ මගින් අගුලු හරින ලදි. විවෘත කිරීමට ඔබන්න."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"මුහුණ හඳුනා ගන්නා ලදි. විවෘත කිරීමට ඔබන්න."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"ඔබගේ සැසිය දිගටම කරගෙන යෑමට ඔබට අවශ්‍යද?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"යළි මුල සිට අරඹන්න"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ඔව්, දිගටම කරගෙන යන්න"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"ආගන්තුක ප්‍රකාරය"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"ඔබ ආගන්තුක ප්‍රකාරයේ සිටී"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"නව පරිශීලකයෙකු එක් කිරීම ආගන්තුක මාදිලියෙන් පිටවී වත්මන් ආගන්තුක සැසියෙන් සියලුම යෙදුම් සහ දත්ත මකනු ඇත."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"පරිශීලක සීමාවට ළඟා විය"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">ඔබට පරිශීලකයින් <xliff:g id="COUNT">%d</xliff:g>ක් දක්වා එක් කළ හැකිය.</item>
- <item quantity="other">ඔබට පරිශීලකයින් <xliff:g id="COUNT">%d</xliff:g>ක් දක්වා එක් කළ හැකිය.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{එක් පරිශීලකයෙක් පමණක් තැනීම කළ හැක.}one{ඔබට පරිශීලකයන් #ක් දක්වා එක් කළ හැක.}other{ඔබට පරිශීලකයන් #ක් දක්වා එක් කළ හැක.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"පරිශීලකයා ඉවත් කරන්නද?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"මෙම පරිශීලකයාගේ සියලු යෙදුම් සහ දත්ත මකනු ඇත."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"ඉවත් කරන්න"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"මට මතක් කරන්න"</string>
<string name="snooze_undo" msgid="2738844148845992103">"පසුගමනය කරන්න"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g>ක් මදක් නතර කරන ලදී"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">පැය %d</item>
- <item quantity="other">පැය %d</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">මිනිත්තු %d</item>
- <item quantity="other">මිනිත්තු %d</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{පැය #ක්}=2{පැය #ක්}one{පැය #ක්}other{පැය #ක්}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{මිනිත්තු #ක්}one{මිනිත්තු #ක්}other{මිනිත්තු #ක්}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"බැටරි සුරැකුම"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> බොත්තම"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home යතුර"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"ඇඟවීම්"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"බැටරිය"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"තිර රු"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"පොදු පණිවිඩ"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"ක්ෂණික යෙදුම්"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"පිහිටුවීම"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"ගබඩාව"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"ඉඟි"</string>
<string name="instant_apps" msgid="8337185853050247304">"ක්ෂණික යෙදුම්"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ටොගල් කරන්න"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"උපාංග පාලන"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"පාලන එක් කිරීමට යෙදුම තෝරා ගන්න"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one">පාලන <xliff:g id="NUMBER_1">%s</xliff:g>ක් එක් කරන ලදී.</item>
- <item quantity="other">පාලන <xliff:g id="NUMBER_1">%s</xliff:g>ක් එක් කරන ලදී.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# පාලනයක් එක් කර ඇත.}one{පාලන #ක් එක් කර ඇත.}other{පාලන #ක් එක් කර ඇත.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"ඉවත් කළා"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"ප්‍රියතම කළා"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"ප්‍රියතම කළා, තත්ත්ව <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ටයිල් එක් කරන්න"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ටයිල් එක් නොකරන්න"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"පරිශීලක තෝරන්න"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one">යෙදුම් <xliff:g id="COUNT_1">%s</xliff:g>ක් සක්‍රියයි</item>
- <item quantity="other">යෙදුම් <xliff:g id="COUNT_1">%s</xliff:g>ක් සක්‍රියයි</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# යෙදුමක් සක්‍රියයි}one{යෙදුම් #ක් සක්‍රියයි}other{යෙදුම් #ක් සක්‍රියයි}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"නව තොරතුරු"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"සක්‍රිය යෙදුම්"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ඔබ මේවා භාවිත නොකරමින් සිටින විට පවා මෙම යෙදුම් ක්‍රියාකාරීව සහ ධාවනය වෙමින් පවතියි. මෙය මේවායේ කාර්යය වැඩිදියුණු කරයි, නමුත් බැටරි ආයු කාලයට ද බලපා හැකි ය."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"සීනුව සකසන ලදි"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"කැමරාව සහ මයික් ක්‍රියාවිරහිතයි"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{දැනුම්දීම් #ක්}one{දැනුම්දීම් #ක්}other{දැනුම්දීම් #ක්}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"විකාශනය කරමින්"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> විකාශනය කිරීම නවත්වන්නද?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ඔබ <xliff:g id="SWITCHAPP">%1$s</xliff:g> විකාශනය කළහොත් හෝ ප්‍රතිදානය වෙනස් කළහොත්, ඔබගේ වත්මන් විකාශනය නවතිනු ඇත."</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> විකාශනය"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ප්‍රතිදානය වෙනස් කරන්න"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"නොදනී"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml
index 8a16acbb7c57..327e0b92c967 100644
--- a/packages/SystemUI/res/values-si/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"ක්‍රියාවිරහිතයි"</item>
<item msgid="460891964396502657">"ක්‍රියාත්මකයි"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"නොමැත"</item>
+ <item msgid="8014986104355098744">"ක්‍රියාවිරහිතයි"</item>
+ <item msgid="5966994759929723339">"ක්‍රියාත්මකයි"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 7f2e71c17201..4bdd20601559 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Zariadenie je uzamknuté"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Skenovanie tváre"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Odoslať"</string>
- <string name="phone_label" msgid="5715229948920451352">"otvoriť telefón"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"otvoriť hlasového asistenta"</string>
- <string name="camera_label" msgid="8253821920931143699">"spustiť fotoaparát"</string>
<string name="cancel" msgid="1089011503403416730">"Zrušiť"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potvrdiť"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Skúsiť znova"</string>
@@ -205,12 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Funkcia Senzory sú vypnuté je aktívna"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Vymazať všetky upozornenia."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="few">Skupina obsahuje ešte <xliff:g id="NUMBER_1">%s</xliff:g> upozornenia.</item>
- <item quantity="many">Skupina obsahuje ešte <xliff:g id="NUMBER_1">%s</xliff:g> upozornenia.</item>
- <item quantity="other">Skupina obsahuje ešte <xliff:g id="NUMBER_1">%s</xliff:g> upozornení.</item>
- <item quantity="one">Skupina obsahuje ešte <xliff:g id="NUMBER_0">%s</xliff:g> upozornenie.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# ďalšie upozornenie v skupine.}few{# ďalšie upozornenia v skupine.}many{# more notifications inside.}other{# ďalších upozornení v skupine.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Obrazovka je uzamknutá v orientácii na šírku."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Obrazovka je uzamknutá v orientácii na výšku."</string>
<string name="dessert_case" msgid="9104973640704357717">"Pult s dezertami"</string>
@@ -228,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatické otáčanie"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otáčanie obrazovky"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Šetrič obrazovky"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Prístup ku kamere"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Prístup k mikrofónu"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"K dispozícii"</string>
@@ -257,12 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Zapína sa…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Šetrič dát – zapnutý"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="few">%d zariadenia</item>
- <item quantity="many">%d zariadenia</item>
- <item quantity="other">%d zariadení</item>
- <item quantity="one">%d zariadenie</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# zariadenie}few{# zariadenia}many{# devices}other{# zariadení}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Baterka"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Fotoaparát sa používa"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobilné dáta"</string>
@@ -319,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Klepnite znova"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Otvorte potiahnutím prstom nahor"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Otvorte klepnutím na ikonu odomknutia"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Odomknuté tvárou. Otvorte klepnutím na ikonu odomknutia."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Odomknuté tvárou. Otvorte stlačením."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Tvár bola rozpoznaná. Otvorte stlačením."</string>
@@ -355,13 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relácii pokračovať?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začať odznova"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Áno, pokračovať"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Režim pre hostí"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Ste v režime pre hostí"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ak pridáte nového používatelia, ukončí sa režim pre hostí a odstránia sa všetky aplikácie a údaje z aktuálnej relácie hosťa."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Dosiahnutý limit počtu používateľov"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="few">Môžete pridať maximálne <xliff:g id="COUNT">%d</xliff:g> používateľov.</item>
- <item quantity="many">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item>
- <item quantity="other">Môžete pridať maximálne <xliff:g id="COUNT">%d</xliff:g> používateľov.</item>
- <item quantity="one">Môžete vytvoriť iba jedného používateľa.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Môžete vytvoriť iba jedného používateľa.}few{Môžete pridať až # používateľov.}many{You can add up to # users.}other{Môžete pridať až # používateľov.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Odstrániť používateľa?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Všetky aplikácie a údaje tohto používateľa budú odstránené."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Odstrániť"</string>
@@ -547,18 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Pripomenúť"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Späť"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Stlmené na <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="few">%d hodiny</item>
- <item quantity="many">%d hodiny</item>
- <item quantity="other">%d hodín</item>
- <item quantity="one">%d hodina</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="few">%d minúty</item>
- <item quantity="many">%d minúty</item>
- <item quantity="other">%d minút</item>
- <item quantity="one">%d minúta</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# hodina}=2{# hodiny}few{# hodiny}many{# hodiny}other{# hodín}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minúta}few{# minúty}many{# minúty}other{# minút}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Šetrič batérie"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Tlačidlo <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Domov"</string>
@@ -707,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Upozornenia"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Batéria"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Snímky obrazovky"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Všeobecné správy"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Okamžité aplikácie"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Nastavenie"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Úložisko"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Tipy"</string>
<string name="instant_apps" msgid="8337185853050247304">"Okamžité aplikácie"</string>
@@ -781,12 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"prepínač"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Ovládanie zariadení"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Vyberte aplikáciu, ktorej ovládače si chcete pridať"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="few">Boli pridané <xliff:g id="NUMBER_1">%s</xliff:g> ovládacie prvky.</item>
- <item quantity="many"><xliff:g id="NUMBER_1">%s</xliff:g> controls added.</item>
- <item quantity="other">Bolo pridaných <xliff:g id="NUMBER_1">%s</xliff:g> ovládacích prvkov.</item>
- <item quantity="one">Bol pridaný <xliff:g id="NUMBER_0">%s</xliff:g> ovládací prvok.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Bol pridaný # ovládací prvok.}few{Boli pridané # ovládacie prvky.}many{# controls added.}other{Bolo pridaných # ovládacích prvkov.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Odstránené"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Pridané medzi obľúbené"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Pridané medzi obľúbené, pozícia <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -943,12 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Pridať kartu"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepridať kartu"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Vyberte používateľa"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> aplikácie sú aktívne</item>
- <item quantity="many"><xliff:g id="COUNT_1">%s</xliff:g> apps are active</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplikácií je aktívnych</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplikácia je aktívna</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikácia je aktívna}few{# aplikácie sú aktívne}many{# apps are active}other{# aplikácií je aktívnych}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nové informácie"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktívne aplikácie"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Tieto aplikácie sú spustené a aktívne, aj keď ich nepoužívate. Zlepšuje to ich funkčnosť, ale môže to mať vplyv aj na výdrž batérie."</string>
@@ -977,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Budík je nastavený"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera a mikrofón sú vypnuté"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# upozornenie}few{# upozornenia}many{# notifications}other{# upozornení}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Vysiela"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Chcete zastaviť vysielanie aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ak vysielate aplikáciu <xliff:g id="SWITCHAPP">%1$s</xliff:g> alebo zmeníte výstup, aktuálne vysielanie bude zastavené"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Vysielanie aplikácie <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Zmena výstupu"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Neznáme"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/tiles_states_strings.xml b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
index dcdfb3a901b5..3cbde1c9574c 100644
--- a/packages/SystemUI/res/values-sk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Vypnuté"</item>
<item msgid="460891964396502657">"Zapnuté"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Nedostupné"</item>
+ <item msgid="8014986104355098744">"Vypnuté"</item>
+ <item msgid="5966994759929723339">"Zapnuté"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index dbb09427151a..c1afc12ccc54 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Naprava je zaklenjena."</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Optično branje obraza"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Pošlji"</string>
- <string name="phone_label" msgid="5715229948920451352">"odpri telefon"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"odpri glasovnega pomočnika"</string>
- <string name="camera_label" msgid="8253821920931143699">"odpri fotoaparat"</string>
<string name="cancel" msgid="1089011503403416730">"Prekliči"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potrdite"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Poskusi znova"</string>
@@ -205,12 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Izklop za tipala je aktiven"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Izbriši vsa obvestila."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"in <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">Notri je še <xliff:g id="NUMBER_1">%s</xliff:g> obvestilo.</item>
- <item quantity="two">Notri sta še <xliff:g id="NUMBER_1">%s</xliff:g> obvestili.</item>
- <item quantity="few">Notri so še <xliff:g id="NUMBER_1">%s</xliff:g> obvestila.</item>
- <item quantity="other">Notri je še <xliff:g id="NUMBER_1">%s</xliff:g> obvestil.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Notri je še # obvestilo.}one{Notri je še # obvestilo.}two{Notri sta še # obvestili.}few{Notri so še # obvestila.}other{Notri je še # obvestil.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Zaslon je zaklenjen v ležeči usmerjenosti."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Zaslon je zaklenjen v pokončni usmerjenosti."</string>
<string name="dessert_case" msgid="9104973640704357717">"Vitrina za sladice"</string>
@@ -228,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Samodejno sukanje"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Samodejno sukanje zaslona"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Ohranjevalnik zaslona"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Dostop do fotoaparata"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Dostop do mikrofona"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Na voljo"</string>
@@ -257,12 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Dostopna točka"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Vklapljanje …"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Varč. s pod. je vkl."</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d naprava</item>
- <item quantity="two">%d napravi</item>
- <item quantity="few">%d naprave</item>
- <item quantity="other">%d naprav</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# naprava}one{# naprava}two{# napravi}few{# naprave}other{# naprav}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Svetilka"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Fotoaparat je v uporabi"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Prenos podatkov v mobilnem omrežju"</string>
@@ -319,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Znova se dotaknite možnosti"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Povlecite navzgor, da odprete"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Za odpiranje pritisnite ikono za odklepanje."</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Odklenjeno z obrazom. Za odpiranje pritisnite ikono za odklepanje."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Odklenjeno z obrazom. Pritisnite za odpiranje."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Obraz je prepoznan. Pritisnite za odpiranje."</string>
@@ -355,13 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite nadaljevati sejo?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začni znova"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, nadaljuj"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Način za goste"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Ste v načinu za goste"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Če dodate novega uporabnika, se bo način za goste zaprl, aplikacije in podatki v trenutni seji gosta pa bodo izbrisani."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Omejitev uporabnikov je dosežena"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Dodate lahko do <xliff:g id="COUNT">%d</xliff:g> uporabnika.</item>
- <item quantity="two">Dodate lahko do <xliff:g id="COUNT">%d</xliff:g> uporabnika.</item>
- <item quantity="few">Dodate lahko do <xliff:g id="COUNT">%d</xliff:g> uporabnike.</item>
- <item quantity="other">Dodate lahko do <xliff:g id="COUNT">%d</xliff:g> uporabnikov.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Ustvariti je mogoče samo enega uporabnika.}one{Dodate lahko največ # uporabnika.}two{Dodate lahko največ # uporabnika.}few{Dodate lahko največ # uporabnike.}other{Dodate lahko največ # uporabnikov.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Želite odstraniti uporabnika?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Vse aplikacije in podatki tega uporabnika bodo izbrisani."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Odstrani"</string>
@@ -547,18 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Opomni me"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Razveljavi"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Preloženo za <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d ura</item>
- <item quantity="two">%d uri</item>
- <item quantity="few">%d ure</item>
- <item quantity="other">%d ur</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d minuta</item>
- <item quantity="two">%d minuti</item>
- <item quantity="few">%d minute</item>
- <item quantity="other">%d minut</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ura}=2{# uri}one{# ura}two{# uri}few{# ure}other{# ur}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuta}one{# minuta}two{# minuti}few{# minute}other{# minut}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Varčevanje z energijo baterije"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Gumb <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Začetek"</string>
@@ -707,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Opozorila"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Baterija"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Posnetki zaslona"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Splošna sporočila"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Nenamestljive aplikacije"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Nastavitev"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Shramba"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Namigi"</string>
<string name="instant_apps" msgid="8337185853050247304">"Nenamestljive aplikacije"</string>
@@ -781,12 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"preklop"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrolniki naprave"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Izberite aplikacijo za dodajanje kontrolnikov"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> kontrolnik dodan.</item>
- <item quantity="two"><xliff:g id="NUMBER_1">%s</xliff:g> kontrolnika dodana.</item>
- <item quantity="few"><xliff:g id="NUMBER_1">%s</xliff:g> kontrolniki dodani.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> kontrolnikov dodanih.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontrolnik je dodan.}one{# kontrolnik je dodan.}two{# kontrolnika sta dodana.}few{# kontrolniki so dodani.}other{# kontrolnikov je dodanih.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Odstranjeno"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Dodano med priljubljene"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Dodano med priljubljene, položaj <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -943,12 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj ploščico"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne dodaj ploščice"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Izberite uporabnika"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> aplikacija je aktivna</item>
- <item quantity="two"><xliff:g id="COUNT_1">%s</xliff:g> aplikaciji sta aktivni</item>
- <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> aplikacije so aktivne</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplikacij je aktivnih</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikacija je aktivna.}one{# aplikacija je aktivna.}two{# aplikaciji sta aktivni.}few{# aplikacije so aktivne.}other{# aplikacij je aktivnih.}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nove informacije"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivne aplikacije"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Te aplikacije so aktivne in se izvajajo, tudi ko jih ne uporabljate. To sicer izboljša njihovo delovanje, vendar lahko hkrati vpliva na čas delovanja baterije."</string>
@@ -977,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm je nastavljen."</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Fotoaparat in mikrofon sta izklopljena."</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# obvestilo}one{# obvestilo}two{# obvestili}few{# obvestila}other{# obvestil}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Oddajanje"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Želite ustaviti oddajanje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Če oddajate aplikacijo <xliff:g id="SWITCHAPP">%1$s</xliff:g> ali spremenite izhod, bo trenutno oddajanje ustavljeno."</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Oddajaj aplikacijo <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Sprememba izhoda"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Neznano"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d. MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
index f1ebee4f8f92..e720819b4b60 100644
--- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Izklopljeno"</item>
<item msgid="460891964396502657">"Vklopljeno"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Ni na voljo"</item>
+ <item msgid="8014986104355098744">"Izklopljeno"</item>
+ <item msgid="5966994759929723339">"Vklopljeno"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index c3c638aeb35f..c3cdf21857c3 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Pajisja është e kyçur"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Po skanon fytyrën"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Dërgo"</string>
- <string name="phone_label" msgid="5715229948920451352">"hap telefonin"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"hap ndihmën zanore"</string>
- <string name="camera_label" msgid="8253821920931143699">"hap kamerën"</string>
<string name="cancel" msgid="1089011503403416730">"Anulo"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Konfirmo"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Provo përsëri"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Çaktivizimi i sensorëve aktiv"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Pastro të gjitha njoftimet."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> njoftime të tjera në brendësi.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> njoftim tjetër në brendësi.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# njoftim tjetër në brendësi.}other{# njoftime të tjera në brendësi.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Ekrani është i kyçur në orientimin horizontal."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Ekrani është i kyçur në orientimin vertikal."</string>
<string name="dessert_case" msgid="9104973640704357717">"\"Kutia e ëmbëlsirës\""</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rrotullim automatik"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rrotullimi automatik i ekranit"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Vendndodhja"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Mbrojtësi i ekranit"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Qasja te kamera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Qasja te mikrofoni"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"E disponueshme"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Qasje në zona publike interneti"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Po aktivizohet…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Kursyesi i të dhënave është aktiv"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d pajisje</item>
- <item quantity="one">%d pajisje</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# pajisje}other{# pajisje}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Elektriku"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera në përdorim"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Të dhënat celulare"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Trokit sërish"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Rrëshqit lart për ta hapur"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Shtyp ikonën e shkyçjes për ta hapur"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"U shkyç me fytyrë. Shtyp ikonën e shkyçjes për ta hapur."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"U shkyç me fytyrë. Shtyp për ta hapur."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Fytyra u njoh. Shtyp për ta hapur."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Dëshiron ta vazhdosh sesionin tënd?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Fillo nga e para"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Po, vazhdo"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Modaliteti \"vizitor\""</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Je në modalitetin \"vizitor\""</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Shtimi i një përdoruesi të ri do të të nxjerrë nga modaliteti \"vizitor\" dhe do të fshijë të gjitha aplikacionet dhe të dhënat nga sesioni aktual për vizitorë."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"U arrit kufiri i përdoruesve"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Mund të shtosh deri në <xliff:g id="COUNT">%d</xliff:g> përdorues.</item>
- <item quantity="one">Mund të krijohet vetëm një përdorues.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Mund të krijohet vetëm një përdorues.}other{Mund të shtosh deri në # përdorues.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Të hiqet ky përdorues?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Të gjitha aplikacionet dhe të dhënat e këtij përdoruesi do të fshihen."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Hiqe"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Më kujto"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Zhbëj"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"U shty për <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d orë</item>
- <item quantity="one">%d orë</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minuta</item>
- <item quantity="one">%d minutë</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# orë}=2{# orë}other{# orë}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minutë}other{# minuta}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Kursyesi i baterisë"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Butoni <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Kreu"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Sinjalizimet"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Pamje ekrani"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Mesazhe të përgjithshme"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Aplikacionet e çastit"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Konfigurimi"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Hapësira ruajtëse"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Sugjerimet"</string>
<string name="instant_apps" msgid="8337185853050247304">"Aplikacionet e çastit"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"aktivizo/çaktivizo"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Kontrollet e pajisjes"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Zgjidh aplikacionin për të shtuar kontrollet"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">U shtuan <xliff:g id="NUMBER_1">%s</xliff:g> kontrolle.</item>
- <item quantity="one">U shtua <xliff:g id="NUMBER_0">%s</xliff:g> kontroll.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{U shtua # kontroll.}other{U shtuan # kontrolle.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"E hequr"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"E shtuar te të preferuarat"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"E shtuar te të preferuarat, pozicioni <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Shto një pllakëz"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Mos e shto pllakëzën"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Zgjidh përdoruesin"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> aplikacione janë aktive</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> aplikacion është aktiv</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikacion është aktiv}other{# aplikacione janë aktive}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Informacion i ri"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplikacionet aktive"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Këto aplikacione janë aktive dhe funksionojnë edhe kur nuk i përdor ato. Kjo përmirëson funksionalitetin e tyre, por mund të ndikojë edhe te kohëzgjatja e baterisë."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmi është caktuar"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera dhe mikrofoni janë joaktivë"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# njoftim}other{# njoftime}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Po transmeton"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Të ndalohet transmetimi i <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Nëse transmeton <xliff:g id="SWITCHAPP">%1$s</xliff:g> ose ndryshon daljen, transmetimi yt aktual do të ndalojë"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmeto <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Ndrysho daljen"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"I panjohur"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
index e24abf64c6f8..7a09f2450354 100644
--- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Joaktiv"</item>
<item msgid="460891964396502657">"Aktiv"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Nuk ofrohet"</item>
+ <item msgid="8014986104355098744">"Joaktiv"</item>
+ <item msgid="5966994759929723339">"Aktiv"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index a06931cb95e1..ef5993bd9fdd 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Уређај је закључан"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Скенирање лица"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Пошаљи"</string>
- <string name="phone_label" msgid="5715229948920451352">"отвори телефон"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"отвори гласовну помоћ"</string>
- <string name="camera_label" msgid="8253821920931143699">"отвори камеру"</string>
<string name="cancel" msgid="1089011503403416730">"Откажи"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Потврди"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Пробај поново"</string>
@@ -205,11 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Сензори су искључени"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Обриши сва обавештења."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"и још <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">Још <xliff:g id="NUMBER_1">%s</xliff:g> обавештење у групи.</item>
- <item quantity="few">Још <xliff:g id="NUMBER_1">%s</xliff:g> обавештења у групи.</item>
- <item quantity="other">Још <xliff:g id="NUMBER_1">%s</xliff:g> обавештења у групи.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Унутра је још # обавештење.}one{Унутра је још # обавештење.}few{Унутра су још # обавештења.}other{Унутра је још # обавештења.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Екран је закључан у хоризонталном положају."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Екран је закључан у вертикалном положају."</string>
<string name="dessert_case" msgid="9104973640704357717">"Витрина са посластицама"</string>
@@ -227,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Аутоматска ротација"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Аутоматско ротирање екрана"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Чувар екрана"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Приступ камери"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Приступ микрофону"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Доступно"</string>
@@ -256,11 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Хотспот"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Укључује се..."</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Уштеда података је укључена"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d уређај</item>
- <item quantity="few">%d уређаја</item>
- <item quantity="other">%d уређаја</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# уређај}one{# уређај}few{# уређаја}other{# уређаја}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Лампа"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Користи се камера"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Мобилни подаци"</string>
@@ -317,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Додирните поново"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Превуците нагоре да бисте отворили"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Притисните икону откључавања да бисте отворили."</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Откључано је лицем. Притисните икону откључавања да бисте отворили."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Откључано је лицем. Притисните да бисте отворили."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Лице је препознато. Притисните да бисте отворили."</string>
@@ -353,12 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Желите ли да наставите сесију?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни из почетка"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Да, настави"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Режим госта"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Користите режим госта"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Додавањем новог корисника изаћи ћете из режима госта и избрисаћете све апликације и податке из актуелне сесије госта."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Достигнут максимални број корисника"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Можете да додате највише <xliff:g id="COUNT">%d</xliff:g> корисника.</item>
- <item quantity="few">Можете да додате највише <xliff:g id="COUNT">%d</xliff:g> корисника.</item>
- <item quantity="other">Можете да додате највише <xliff:g id="COUNT">%d</xliff:g> корисника.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Можете да направите само једног корисника.}one{Можете да додате највише # корисника.}few{Можете да додате највише # корисника.}other{Можете да додате највише # корисника.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Желите ли да уклоните корисника?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Све апликације и подаци овог корисника ће бити избрисани."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Уклони"</string>
@@ -544,16 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Подсети ме"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Опозови"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Одложено је за <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d сат</item>
- <item quantity="few">%d сата</item>
- <item quantity="other">%d сати</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d минут</item>
- <item quantity="few">%d минута</item>
- <item quantity="other">%d минута</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# сат}=2{# сата}one{# сат}few{# сата}other{# сати}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# минут}one{# минут}few{# минута}other{# минута}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Уштеда батерије"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Дугме <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Тастер Почетна"</string>
@@ -702,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Обавештења"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Батерија"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Снимци екрана"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Опште поруке"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Инстант апликације"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Подешавање"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Меморијски простор"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Савети"</string>
<string name="instant_apps" msgid="8337185853050247304">"Инстант апликације"</string>
@@ -776,11 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"укључите/искључите"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Контроле уређаја"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Одаберите апликацију за додавање контрола"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> контрола је додата.</item>
- <item quantity="few"><xliff:g id="NUMBER_1">%s</xliff:g> контроле су додате.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> контрола је додато.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# контрола је додата.}one{# контрола је додата.}few{# контроле су додате.}other{# контрола је додато.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Уклоњено"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Означено је као омиљено"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Означено је као омиљено, <xliff:g id="NUMBER">%d</xliff:g>. позиција"</string>
@@ -937,11 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Додај плочицу"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не додај плочицу"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Изаберите корисника"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> апликација је активна</item>
- <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> апликације су активне</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> апликација је активно</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# апликација је активна}one{# апликација је активна}few{# апликације су активне}other{# апликација је активно}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Нове информације"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активне апликације"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ове апликације су активне и раде чак и када их не користите. То им побољшава функционалност, али може да утиче и на трајање батерије."</string>
@@ -970,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Аларм је подешен"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камера и микрофон су искључени"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# обавештење}one{# обавештење}few{# обавештења}other{# обавештења}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Емитовање"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Желите да зауставите емитовање апликације <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ако емитујете апликацију <xliff:g id="SWITCHAPP">%1$s</xliff:g> или промените излаз, актуелно емитовање ће се зауставити"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Емитујте апликацију <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Промените излаз"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Непознато"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"ДДД, д. МММ"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"с:мин"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"ч:мин"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
index 0cef5a66c232..dace491993ba 100644
--- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Искључено"</item>
<item msgid="460891964396502657">"Укључено"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Недоступно"</item>
+ <item msgid="8014986104355098744">"Искључено"</item>
+ <item msgid="5966994759929723339">"Укључено"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 3c6d61d3e378..20bc29fc77ce 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Enheten är låst"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Registrerar ansikte"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Skicka"</string>
- <string name="phone_label" msgid="5715229948920451352">"öppna mobilen"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"öppna röstassistenten"</string>
- <string name="camera_label" msgid="8253821920931143699">"öppna kameran"</string>
<string name="cancel" msgid="1089011503403416730">"Avbryt"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Bekräfta"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Försök igen"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensorer har inaktiverats"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Ta bort alla meddelanden."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"<xliff:g id="NUMBER">%s</xliff:g> till"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> fler aviseringar i gruppen.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> till avisering i gruppen.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# till avisering i gruppen.}other{# till aviseringar i gruppen.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Bildskärmens riktning är nu låst i liggande format."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Bildskärmens riktning är nu låst i stående format."</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessertdisken"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotera automatiskt"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotera skärmen automatiskt"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Plats"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Skärmsläckare"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameraåtkomst"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonåtkomst"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tillgänglig"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Surfzon"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Aktiverar …"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Databesparing på"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d enheter</item>
- <item quantity="one">%d enhet</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# enhet}other{# enheter}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Ficklampa"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kameran används"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobildata"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Tryck igen"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Öppna genom att svepa uppåt"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Tryck på ikonen lås upp för att öppna"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Upplåst med ansiktslås. Tryck på ikonen lås upp för att öppna."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Upplåst med ansiktslås. Tryck för att öppna."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Ansiktet har identifierats. Tryck för att öppna."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Vill du fortsätta sessionen?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Börja om"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, fortsätt"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Gästläge"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Du är i gästläge"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Om du lägger till en ny användare avslutas gästläget och alla appar och all data från den aktuella gästsessionen raderas."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Användargränsen har nåtts"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Det går att lägga till upp till <xliff:g id="COUNT">%d</xliff:g> användare.</item>
- <item quantity="one">Det går bara att skapa en användare.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Högst en användare kan skapas.}other{Du kan lägga till upp till # användare.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Vill du ta bort användaren?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Alla appar och all data som tillhör den här användaren raderas."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Ta bort"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Påminn mig"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Ångra"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Snoozad i <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d timmar</item>
- <item quantity="one">%d timme</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d minuter</item>
- <item quantity="one">%d minut</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# timme}=2{# timmar}other{# timmar}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minut}other{# minuter}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Batterisparläge"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Knappen <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Start"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Aviseringar"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Batteri"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Skärmbilder"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Allmänna meddelanden"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Konfigurering"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Lagring"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Tips"</string>
<string name="instant_apps" msgid="8337185853050247304">"Snabbappar"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"aktivera och inaktivera"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Enhetsstyrning"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Välj en app om du vill lägga till snabbkontroller"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> kontroller har lagts till.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> kontroll har lagts till.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontroll har lagts till.}other{# kontroller har lagts till.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Har tagits bort"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Har lagts till som favorit"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Har lagts till som favorit, plats <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Lägg till ruta"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Lägg inte till ruta"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Välj användare"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> appar är aktiva</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> app är aktiv</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app är aktiv}other{# appar är aktiva}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Ny information"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktiva appar"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Dessa appar är aktiva och körs även när du inte använder dem. Detta hjälper dem att fungera bättre men batteritiden kan påverkas."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmet är aktiverat"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kameran och mikrofonen är avstängda"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# avisering}other{# aviseringar}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Sänder"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vill du sluta sända från <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Om en utsändning från <xliff:g id="SWITCHAPP">%1$s</xliff:g> pågår eller om du byter ljudutgång avbryts den nuvarande utsändningen"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Sänd från <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Byt ljudutgång"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Okänt"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h.mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk.mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
index 410a6bc4645f..9e69b00adf64 100644
--- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Av"</item>
<item msgid="460891964396502657">"På"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Inte tillgängligt"</item>
+ <item msgid="8014986104355098744">"Av"</item>
+ <item msgid="5966994759929723339">"På"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 385f535d5397..418b88eede96 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Kifaa kimefungwa"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Inachanganua uso"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Tuma"</string>
- <string name="phone_label" msgid="5715229948920451352">"fungua simu"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"fungua mapendekezo ya sauti"</string>
- <string name="camera_label" msgid="8253821920931143699">"fungua kamera"</string>
<string name="cancel" msgid="1089011503403416730">"Ghairi"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Thibitisha"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Jaribu tena"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Kipengele cha kuzima vitambuzi kimewashwa"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Futa arifa zote."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">Kuna arifa <xliff:g id="NUMBER_1">%s</xliff:g> zaidi katika kikundi.</item>
- <item quantity="one">Kuna arifa <xliff:g id="NUMBER_0">%s</xliff:g> zaidi katika kikundi.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Kuna arifa # zaidi ndani ya kikundi.}other{Kuna arifa # zaidi ndani ya kikundi.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Skrini imefungwa sasa katika uelekezo wa mandhari."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Skrini imefungwa katika uelekeo wa picha."</string>
<string name="dessert_case" msgid="9104973640704357717">"Sanduku la Vitindamlo"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Zungusha kiotomatiki"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Skrini ijizungushe kiotomatiki"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Mahali"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Taswira ya skrini"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Ufikiaji wa kamera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Ufikiaji wa maikrofoni"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Unapatikana"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Mtandaopepe"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Inawasha..."</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Kiokoa Data kimewashwa"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">Vifaa %d</item>
- <item quantity="one">Kifaa %d</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{Kifaa #}other{Vifaa #}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Tochi"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera inatumika"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Data ya simu"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Gusa tena"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Telezesha kidole juu ili ufungue"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Bonyeza aikoni ya kufungua ili ufungue"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Imefunguliwa kwa kutumia uso wako. Bonyeza aikoni ya kufungua ili ufungue."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Imefunguliwa kwa kutumia uso wako. Bonyeza ili ufungue."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Uso umetambuliwa. Bonyeza ili ufungue."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Je, unataka kuendelea na kipindi chako?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Anza upya"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ndiyo, endelea"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Matumizi ya wageni"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Unatumia hali ya wageni"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Kuongeza mtumiaji mpya kutaondoa matumizi ya wageni yaliyopo na kufuta programu na data kutoka kwenye kipindi cha mgeni cha sasa."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Umefikia kima cha juu cha watumiaji"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Unaruhusiwa kuongeza hadi watumiaji <xliff:g id="COUNT">%d</xliff:g>.</item>
- <item quantity="one">Unaruhusiwa kuongeza mtumiaji mmoja pekee.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Unaweza kuweka mtumiaji mmoja pekee.}other{Unaweza kuweka hadi watumiaji #.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Je, ungependa kuondoa mtumiaji?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Programu na data yote ya mtumiaji huyu itafutwa."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Ondoa"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Nikumbushe"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Tendua"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Imeahirishwa kwa <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">Saa %d</item>
- <item quantity="one">Saa %d</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">Dakika %d</item>
- <item quantity="one">Dakika %d</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{Saa #}=2{Saa #}other{Saa #}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{Dakika #}other{Dakika #}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Kiokoa Betri"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Kitufe cha <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Mwanzo"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Arifa"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Betri"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Picha za skrini"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Ujumbe wa Jumla"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Programu zinazofunguka papo hapo"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Weka mipangilio"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Hifadhi"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Vidokezo"</string>
<string name="instant_apps" msgid="8337185853050247304">"Programu Zinazofunguka Papo Hapo"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"geuza"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Vidhibiti vya vifaa"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Chagua programu ili uweke vidhibiti"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">Umeweka vidhibiti <xliff:g id="NUMBER_1">%s</xliff:g>.</item>
- <item quantity="one">Umeweka kidhibiti <xliff:g id="NUMBER_0">%s</xliff:g>.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Umeweka kidhibiti #.}other{Umeweka vidhibiti #.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Kimeondolewa"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Kimewekwa kwenye vipendwa"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Kimewekwa kwenye vipendwa, nafasi ya <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Kiongeze"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Kisiongezwe"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Chagua mtumiaji"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other">Programu <xliff:g id="COUNT_1">%s</xliff:g> zinatumika</item>
- <item quantity="one">Programu <xliff:g id="COUNT_0">%s</xliff:g> inatumika</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{Programu # inatumika}other{Programu # zinatumika}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Maelezo mapya"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Programu zinazotumika"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Programu hizi zinaendelea kufanya kazi, hata wakati huzitumii. Hali hii huboresha utendaji wa programu hizo, lakini pia inaweza kuathiri muda wa matumizi ya betri."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Kengele imewekwa"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera na maikrofoni zimezimwa"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{Arifa #}other{Arifa #}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Inaarifu"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Ungependa kusimamisha utangazaji kwenye <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ikiwa unatangaza kwenye <xliff:g id="SWITCHAPP">%1$s</xliff:g> au unabadilisha maudhui, tangazo lako la sasa litasimamishwa"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Tangaza kwenye <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Badilisha maudhui"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Haijulikani"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"saa:dk"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:dk"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
index 79d29ab7b11e..2f765ef5320a 100644
--- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Imezimwa"</item>
<item msgid="460891964396502657">"Imewashwa"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Haipatikani"</item>
+ <item msgid="8014986104355098744">"Imezimwa"</item>
+ <item msgid="5966994759929723339">"Imewashwa"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 6c42073b8805..d638c9d5742e 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -47,6 +47,8 @@
<dimen name="lockscreen_shade_max_over_scroll_amount">32dp</dimen>
+ <dimen name="status_view_margin_horizontal">8dp</dimen>
+
<!-- Distance that the full shade transition takes in order to complete by tapping on a button
like "expand". -->
<dimen name="lockscreen_shade_transition_by_tap_distance">200dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
index 589d12f95f45..347cf2965c77 100644
--- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
@@ -21,5 +21,10 @@
<dimen name="keyguard_status_view_bottom_margin">40dp</dimen>
<dimen name="bouncer_user_switcher_y_trans">20dp</dimen>
+ <!-- qs_tiles_page_horizontal_margin should be margin / 2, otherwise full space between two
+ pages is margin * 2, and that makes tiles page not appear immediately after user swipes to
+ the side -->
+ <dimen name="qs_tiles_page_horizontal_margin">32dp</dimen>
+
<dimen name="qqs_layout_padding_bottom">16dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index f45f106d391d..868c003d99a5 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -25,6 +25,8 @@
<dimen name="status_bar_header_height_keyguard">56dp</dimen>
+ <dimen name="status_view_margin_horizontal">24dp</dimen>
+
<dimen name="qs_media_session_height_expanded">251dp</dimen>
<dimen name="qs_content_horizontal_padding">40dp</dimen>
<dimen name="qs_horizontal_margin">40dp</dimen>
diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
index 2abc9e3d6119..a0bf072ed27c 100644
--- a/packages/SystemUI/res/values-sw720dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
@@ -25,9 +25,16 @@
<dimen name="keyguard_status_view_bottom_margin">80dp</dimen>
<dimen name="bouncer_user_switcher_y_trans">90dp</dimen>
+ <dimen name="large_screen_shade_header_left_padding">24dp</dimen>
<dimen name="qqs_layout_padding_bottom">40dp</dimen>
<dimen name="notification_panel_margin_horizontal">80dp</dimen>
<dimen name="notification_side_paddings">40dp</dimen>
+
+ <!-- qs_tiles_page_horizontal_margin should be margin / 2, otherwise full space between two
+ pages is margin * 2, and that makes tiles page not appear immediately after user swipes to the
+ side -->
+ <dimen name="qs_tiles_page_horizontal_margin">60dp</dimen>
+
<dimen name="notification_section_divider_height">16dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 934800e30c61..34cb0cf9de5b 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"சாதனம் பூட்டப்பட்டுள்ளது"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"முகத்தை ஸ்கேன் செய்கிறது"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"அனுப்பு"</string>
- <string name="phone_label" msgid="5715229948920451352">"ஃபோனைத் திற"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"குரல் உதவியைத் திற"</string>
- <string name="camera_label" msgid="8253821920931143699">"கேமராவைத் திற"</string>
<string name="cancel" msgid="1089011503403416730">"ரத்துசெய்"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"உறுதிப்படுத்துக"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"மீண்டும் முயல்க"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"’சென்சார்கள் ஆஃப்’ செயலில் உள்ளது"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"எல்லா அறிவிப்புகளையும் அழி."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">உள்ளே மேலும் <xliff:g id="NUMBER_1">%s</xliff:g> அறிவிப்புகள் உள்ளன.</item>
- <item quantity="one">உள்ளே மேலும் <xliff:g id="NUMBER_0">%s</xliff:g> அறிவிப்பு உள்ளது.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{குழுவில் மேலும் # அறிவிப்பு உள்ளது.}other{குழுவில் மேலும் # அறிவிப்புகள் உள்ளன.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"நிலத்தோற்ற திசையமைப்பில் திரைப் பூட்டப்பட்டுள்ளது."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"உருவப்பட திசையமைப்பில் திசை பூட்டப்பட்டுள்ளது."</string>
<string name="dessert_case" msgid="9104973640704357717">"இனிப்பு வடிவங்கள்"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"தானாகச் சுழற்று"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"திரையைத் தானாகச் சுழற்று"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"இருப்பிடம்"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ஸ்கிரீன் சேவர்"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"கேமரா அணுகல்"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"மைக் அணுகல்"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"கிடைக்கிறது"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"ஹாட்ஸ்பாட்"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ஆன் செய்கிறது…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"டேட்டா சேவர்: ஆன்"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d சாதனங்கள்</item>
- <item quantity="one">%d சாதனம்</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# சாதனம்}other{# சாதனங்கள்}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"டார்ச் லைட்"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"கேமரா உபயோகத்திலுள்ளது"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"மொபைல் டேட்டா"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"மீண்டும் தட்டவும்"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"திறப்பதற்கு மேல் நோக்கி ஸ்வைப் செய்யவும்"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"திறக்க, அன்லாக் ஐகானை அழுத்தவும்"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"முகம் மூலம் அன்லாக் செய்யப்பட்டது. திறக்க, அன்லாக் ஐகானை அழுத்துக."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"முகம் மூலம் அன்லாக் செய்யப்பட்டது. திறக்க அழுத்தவும்."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"முகம் அங்கீகரிக்கப்பட்டது. திறக்க அழுத்தவும்."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"உங்கள் அமர்வைத் தொடர விருப்பமா?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"மீண்டும் தொடங்கு"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"தொடரவும்"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"கெஸ்ட் பயன்முறை"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"கெஸ்ட் பயன்முறையில் உள்ளீர்கள்"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"புதிய பயனரைச் சேர்த்தால் கெஸ்ட் பயன்முறையில் இருந்து வெளியேற்றப்படுவீர்கள். மேலும் தற்போதைய கெஸ்ட் அமர்வின் ஆப்ஸ் மற்றும் தரவு அனைத்தும் நீக்கப்படும்."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"பயனர் வரம்பை அடைந்துவிட்டீர்கள்"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> பயனர்கள் வரை சேர்க்க முடியும்.</item>
- <item quantity="one">ஒரு பயனரை மட்டுமே சேர்க்க முடியும்.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{ஒரு பயனரை மட்டுமே சேர்க்க முடியும்.}other{# பயனர்கள் வரை சேர்க்கலாம்.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"பயனரை அகற்றவா?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"இந்தப் பயனரின் எல்லா பயன்பாடுகளும் தரவும் நீக்கப்படும்."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"அகற்று"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"எனக்கு நினைவூட்டு"</string>
<string name="snooze_undo" msgid="2738844148845992103">"செயல்தவிர்"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"உறக்கநிலையில் வைத்திருந்த நேரம்: <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d மணிநேரம்</item>
- <item quantity="one">%d மணிநேரம்</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d நிமிடங்கள்</item>
- <item quantity="one">%d நிமிடம்</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# மணிநேரம்}=2{# மணிநேரம்}other{# மணிநேரம்}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# நிமிடம்}other{# நிமிடங்கள்}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"பேட்டரி சேமிப்பு"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> பட்டன்"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"ஹோம்"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"விழிப்பூட்டல்கள்"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"பேட்டரி"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"ஸ்கிரீன் ஷாட்டுகள்"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"பொதுச் செய்திகள்"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"இன்ஸ்டண்ட் ஆப்ஸ்"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"அமைவு"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"சேமிப்பிடம்"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"குறிப்புகள்"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"நிலைமாற்று"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"சாதனக் கட்டுப்பாடுகள்"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"கட்டுப்பாடுகளைச் சேர்க்க வேண்டிய ஆப்ஸைத் தேர்ந்தெடுங்கள்"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> கட்டுப்பாடுகள் சேர்க்கப்பட்டன.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> கட்டுப்பாடு சேர்க்கப்பட்டது.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# கட்டுப்பாடு சேர்க்கப்பட்டது.}other{# கட்டுப்பாடுகள் சேர்க்கப்பட்டன.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"அகற்றப்பட்டது"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"பிடித்தவற்றில் சேர்க்கப்பட்டது"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"பிடித்தவற்றில் சேர்க்கப்பட்டது, நிலை <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"கட்டத்தைச் சேர்"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"கட்டத்தை சேர்க்காதே"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"பயனரைத் தேர்வுசெய்க"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ஆப்ஸ் செயலிலுள்ளன</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> ஆப்ஸ் செயலிலுள்ளது</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# ஆப்ஸ் செயலிலுள்ளது}other{# ஆப்ஸ் செயலிலுள்ளன}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"புதிய தகவல்கள்"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"செயலிலுள்ள ஆப்ஸ்"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"இந்த ஆப்ஸை நீங்கள் பயன்படுத்தாதபோதும் அவை செயலில் இருப்பதோடு இயங்கிக் கொண்டிருக்கும். இது அவற்றின் செயல்பாட்டை மேம்படுத்தும். ஆனால், அதே சமயம் பேட்டரி ஆயுளைக் குறைக்கக்கூடும்."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"அலாரம் அமைக்கப்பட்டுள்ளது"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"கேமராவும் மைக்கும் ஆஃப் செய்யப்பட்டுள்ளன"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# அறிவிப்பு}other{# அறிவிப்புகள்}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ஒலிபரப்புதல்"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் ஒலிபரப்பப்படுவதை நிறுத்தவா?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"நீங்கள் <xliff:g id="SWITCHAPP">%1$s</xliff:g> ஆப்ஸை ஒலிபரப்பினாலோ அவுட்புட்டை மாற்றினாலோ உங்களின் தற்போதைய ஒலிபரப்பு நிறுத்தப்படும்"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ஆப்ஸை ஒலிபரப்பு"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"அவுட்புட்டை மாற்று"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"தெரியவில்லை"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
index 52fca126922e..41f64125753c 100644
--- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"முடக்கப்பட்டுள்ளது"</item>
<item msgid="460891964396502657">"இயக்கப்பட்டுள்ளது"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"கிடைக்கவில்லை"</item>
+ <item msgid="8014986104355098744">"முடக்கப்பட்டுள்ளது"</item>
+ <item msgid="5966994759929723339">"இயக்கப்பட்டுள்ளது"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 2ee16a76242d..720c909b5223 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"పరికరం లాక్ చేయబడింది"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"ముఖాన్ని స్కాన్ చేస్తోంది"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"పంపు"</string>
- <string name="phone_label" msgid="5715229948920451352">"ఫోన్‌ను తెరువు"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"వాయిస్ అసిస్టెంట్‌ను తెరువు"</string>
- <string name="camera_label" msgid="8253821920931143699">"కెమెరాను తెరవండి"</string>
<string name="cancel" msgid="1089011503403416730">"రద్దు చేయండి"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"నిర్ధారించు"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"మళ్లీ ప్రయత్నించు"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"సెన్సార్‌లు ఆఫ్ యాక్టివ్‌లో ఉంది"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"అన్ని నోటిఫికేషన్‌లను క్లియర్ చేయండి."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">లోపల మరో <xliff:g id="NUMBER_1">%s</xliff:g> నోటిఫికేషన్‌లు ఉన్నాయి.</item>
- <item quantity="one">లోపల మరో <xliff:g id="NUMBER_0">%s</xliff:g> నోటిఫికేషన్ ఉంది.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{లోపల మరో # నోటిఫికేషన్ ఉంది.}other{లోపల మరో # నోటిఫికేషన్‌లు ఉన్నాయి.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"స్క్రీన్ ల్యాండ్‌స్కేప్ దృగ్విన్యాసంలో లాక్ చేయబడుతుంది."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"స్క్రీన్ పోర్ట్రెయిట్ దృగ్విన్యాసంలో లాక్ చేయబడుతుంది."</string>
<string name="dessert_case" msgid="9104973640704357717">"డెజర్ట్ కేస్"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ఆటో-రొటేట్‌"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"స్క్రీన్ ఆటో-రొటేట్‌"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"లొకేషన్"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"స్క్రీన్ సేవర్"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"కెమెరా యాక్సెస్"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"మైక్ యాక్సెస్"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"అందుబాటులో ఉంది"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"హాట్‌స్పాట్"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ఆన్ చేస్తోంది…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"డేటా సేవర్ ఆన్‌లో ఉంది"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d పరికరాలు</item>
- <item quantity="one">%d పరికరం</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# పరికరం}other{# పరికరాలు}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"ఫ్లాష్‌లైట్"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"కెమెరా ఉపయోగంలో ఉంది"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"మొబైల్ డేటా"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"మళ్లీ ట్యాప్ చేయండి"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"తెరవడానికి, పైకి స్వైప్ చేయండి"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"తెరవడానికి అన్‌లాక్ చిహ్నాన్ని నొక్కండి"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ముఖం ద్వారా అన్‌లాక్ చేయబడింది. తెరవడానికి అన్‌లాక్ చిహ్నాన్ని నొక్కండి."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ముఖం ద్వారా అన్‌లాక్ చేయబడింది. తెరవడానికి నొక్కండి."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ముఖం గుర్తించబడింది. తెరవడానికి నొక్కండి."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"మీరు మీ సెషన్‌ని కొనసాగించాలనుకుంటున్నారా?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"మొదటి నుండి ప్రారంభించు"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"అవును, కొనసాగించు"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"గెస్ట్ మోడ్"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"మీరు గెస్ట్ మోడ్‌లో ఉన్నారు"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"కొత్త యూజర్‌ను జోడించడం వలన గెస్ట్ మోడ్ నుండి వైదొలుగుతుంది. అలాగే ప్రస్తుత గెస్ట్ సెషన్ నుండి అన్ని యాప్‌లతో పాటు మొత్తం డేటా తొలగించబడుతుంది."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"వినియోగదారు పరిమితిని చేరుకున్నారు"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">మీరు <xliff:g id="COUNT">%d</xliff:g> వినియోగదారుల వరకు జోడించవచ్చు.</item>
- <item quantity="one">ఒక్క వినియోగదారుని మాత్రమే సృష్టించవచ్చు.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{ఒక యూజర్‌ను మాత్రమే క్రియేట్ చేయవచ్చు.}other{మీరు గరిష్టంగా # మంది యూజర్‌లను జోడించవచ్చు.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"వినియోగదారుని తీసివేయాలా?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"ఈ వినియోగదారుకు సంబంధించిన అన్ని యాప్‌లు మరియు డేటా తొలగించబడతాయి."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"తీసివేయి"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"నాకు గుర్తు చేయి"</string>
<string name="snooze_undo" msgid="2738844148845992103">"చర్య రద్దు చేయండి"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> వరకు తాత్కాలికంగా ఆపివేయబడింది"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d గంటలు</item>
- <item quantity="one">%d గంట</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d నిమిషాలు</item>
- <item quantity="one">%d నిమిషం</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# గంట}=2{# గంటలు}other{# గంటలు}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# నిమిషం}other{# నిమిషాలు}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"బ్యాటరీ సేవర్"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"బటన్ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"అలర్ట్‌లు"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"బ్యాటరీ"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"స్క్రీన్‌షాట్‌లు"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"సాధారణ మెసేజ్‌లు"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"ఇన్‌స్టంట్ యాప్‌లు"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"సెటప్ చేయండి"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"స్టోరేజ్"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"సూచనలు"</string>
<string name="instant_apps" msgid="8337185853050247304">"ఇన్‌స్టంట్ యాప్‌లు"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"టోగుల్ చేయి"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"డివైజ్ కంట్రోల్స్"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"కంట్రోల్స్‌ను యాడ్ చేయడానికి యాప్‌ను ఎంచుకోండి"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> కంట్రోల్‌లు యాడ్ అయ్యాయి.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> కంట్రోల్ యాడ్ అయింది.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# కంట్రోల్ జోడించబడింది.}other{# కంట్రోల్స్ జోడించబడ్డాయి.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"తీసివేయబడింది"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"ఇష్టమైనదిగా గుర్తు పెట్టబడింది"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"<xliff:g id="NUMBER">%d</xliff:g>వ స్థానంలో ఇష్టమైనదిగా గుర్తు పెట్టబడింది"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"టైల్‌ను జోడించండి"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"టైల్‌ను జోడించవద్దు"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"యూజర్‌ను ఎంచుకోండి"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> యాప్‌లు యాక్టివ్‌గా ఉన్నాయి</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> యాప్ యాక్టివ్‌గా ఉంది</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# యాప్ యాక్టివ్‌గా ఉంది}other{# యాప్‌లు యాక్టివ్‌గా ఉన్నాయి}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"కొత్త సమాచారం"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"యాక్టివ్‌గా ఉన్న యాప్‌లు"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"మీరు వాటిని ఉపయోగించనప్పటికీ, ఈ యాప్‌లు యాక్టివ్‌గా ఉంటాయి, రన్ అవుతాయి. ఇది వారి ఫంక్షనాలిటీని మెరుగుపరుస్తుంది, అయితే ఇది బ్యాటరీ జీవితకాలాన్ని కూడా ప్రభావితం చేయవచ్చు."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"అలారం సెట్ చేశాను"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"కెమెరా, మైక్ ఆఫ్‌లో ఉన్నాయి"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# నోటిఫికేషన్}other{# నోటిఫికేషన్‌లు}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ప్రసారం చేస్తోంది"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రసారం చేయడాన్ని ఆపివేయాలా?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"మీరు <xliff:g id="SWITCHAPP">%1$s</xliff:g> ప్రసారం చేస్తే లేదా అవుట్‌పుట్‌ను మార్చినట్లయితే, మీ ప్రస్తుత ప్రసారం ఆగిపోతుంది"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ప్రసారం చేయండి"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"అవుట్‌పుట్‌ను మార్చండి"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"తెలియదు"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml
index 60997928082e..44ba47781ae7 100644
--- a/packages/SystemUI/res/values-te/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"ఆఫ్"</item>
<item msgid="460891964396502657">"ఆన్"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"అందుబాటులో లేరు"</item>
+ <item msgid="8014986104355098744">"ఆఫ్"</item>
+ <item msgid="5966994759929723339">"ఆన్"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 94f6c3952e06..375bd394bb4c 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -22,7 +22,7 @@
<resources>
<!-- SystemUIFactory component -->
<string name="config_systemUIFactoryComponent" translatable="false">
- com.android.systemui.tv.TvSystemUIFactory
+ com.android.systemui.tv.TvSystemUIInitializer
</string>
<!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. -->
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 2d003db1a039..8958be0ea95a 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"อุปกรณ์ถูกล็อก"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"กำลังสแกนใบหน้า"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ส่ง"</string>
- <string name="phone_label" msgid="5715229948920451352">"เปิดโทรศัพท์"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"เปิดตัวช่วยเสียง"</string>
- <string name="camera_label" msgid="8253821920931143699">"เปิดกล้อง"</string>
<string name="cancel" msgid="1089011503403416730">"ยกเลิก"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"ยืนยัน"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"ลองอีกครั้ง"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"\"ปิดเซ็นเซอร์\" เปิดใช้งานอยู่"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"ล้างการแจ้งเตือนทั้งหมด"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">มีการแจ้งเตือนอีก <xliff:g id="NUMBER_1">%s</xliff:g> รายการด้านใน</item>
- <item quantity="one">มีการแจ้งเตือนอีก <xliff:g id="NUMBER_0">%s</xliff:g> รายการด้านใน</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{มีการแจ้งเตือนอีก # รายการด้านใน}other{มีการแจ้งเตือนอีก # รายการด้านใน}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวนอน"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวตั้ง"</string>
<string name="dessert_case" msgid="9104973640704357717">"ชั้นแสดงของหวาน"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"หมุนอัตโนมัติ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"หมุนหน้าจออัตโนมัติ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ตำแหน่ง"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"โปรแกรมรักษาหน้าจอ"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"สิทธิ์เข้าถึงกล้อง"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"สิทธิ์เข้าถึงไมโครโฟน"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"พร้อมให้ใช้งาน"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"ฮอตสปอต"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"กำลังเปิด..."</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"เปิดการประหยัดเน็ตอยู่"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">อุปกรณ์ %d เครื่อง</item>
- <item quantity="one">อุปกรณ์ %d เครื่อง</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{อุปกรณ์ # เครื่อง}other{อุปกรณ์ # เครื่อง}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"ไฟฉาย"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"ใช้กล้องอยู่"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"เน็ตมือถือ"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"แตะอีกครั้ง"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"เลื่อนขึ้นเพื่อเปิด"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"กดไอคอนปลดล็อกเพื่อเปิด"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ปลดล็อกด้วยใบหน้าแล้ว กดไอคอนปลดล็อกเพื่อเปิด"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ปลดล็อกด้วยใบหน้าแล้ว กดเพื่อเปิด"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"จดจำใบหน้าได้ กดเพื่อเปิด"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"คุณต้องการอยู่ในเซสชันต่อไปไหม"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"เริ่มต้นใหม่"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ใช่ ดำเนินการต่อ"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"โหมดผู้ใช้ชั่วคราว"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"คุณอยู่ในโหมดผู้ใช้ชั่วคราว"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"การเพิ่มผู้ใช้ใหม่จะเป็นการออกจากโหมดผู้ใช้ชั่วคราว และจะลบแอปและข้อมูลทั้งหมดจากเซสชันผู้ใช้ชั่วคราวในปัจจุบัน"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"ถึงขีดจำกัดผู้ใช้แล้ว"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">คุณเพิ่มผู้ใช้ได้สูงสุด <xliff:g id="COUNT">%d</xliff:g> คน</item>
- <item quantity="one">สร้างผู้ใช้ได้เพียง 1 คนเท่านั้น</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{สร้างผู้ใช้ได้เพียง 1 คนเท่านั้น}other{คุณเพิ่มผู้ใช้ได้สูงสุด # คน}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"นำผู้ใช้ออกใช่ไหม"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"แอปและข้อมูลทั้งหมดของผู้ใช้นี้จะถูกลบ"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"นำออก"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"เตือนฉัน"</string>
<string name="snooze_undo" msgid="2738844148845992103">"เลิกทำ"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"ปิดเสียงเตือนชั่วคราวไว้เป็นเวลา <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d ชั่วโมง</item>
- <item quantity="one">%d ชั่วโมง</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d นาที</item>
- <item quantity="one">%d นาที</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# ชั่วโมง}=2{# ชั่วโมง}other{# ชั่วโมง}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# นาที}other{# นาที}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"โหมดประหยัดแบตเตอรี่"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"ปุ่ม <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"การแจ้งเตือน"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"แบตเตอรี"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"ภาพหน้าจอ"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"ข้อความทั่วไป"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"ตั้งค่า"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"พื้นที่เก็บข้อมูล"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"คำแนะนำ"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant App"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"สลับ"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"ระบบควบคุมอุปกรณ์"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"เลือกแอปเพื่อเพิ่มตัวควบคุม"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">เพิ่มตัวควบคุม <xliff:g id="NUMBER_1">%s</xliff:g> ตัวแล้ว</item>
- <item quantity="one">เพิ่มตัวควบคุม <xliff:g id="NUMBER_0">%s</xliff:g> ตัวแล้ว</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{เพิ่มตัวควบคุม # ตัวแล้ว}other{เพิ่มตัวควบคุม # ตัวแล้ว}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"นำออกแล้ว"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"ตั้งเป็นรายการโปรดแล้ว"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"ตั้งเป็นรายการโปรดแล้ว โดยอยู่ลำดับที่ <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"เพิ่มองค์ประกอบ"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ไม่เพิ่มองค์ประกอบ"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"เลือกผู้ใช้"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other">ทำงานอยู่ <xliff:g id="COUNT_1">%s</xliff:g> แอป</item>
- <item quantity="one">ทำงานอยู่ <xliff:g id="COUNT_0">%s</xliff:g> แอป</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{ทำงานอยู่ # แอป}other{ทำงานอยู่ # แอป}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"ข้อมูลใหม่"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"แอปที่ทำงานอยู่"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"แอปเหล่านี้กำลังทำงานแม้ว่าคุณจะไม่ได้ใช้งานอยู่ก็ตาม วิธีนี้ช่วยให้ฟังก์ชันการทำงานของแอปมีประสิทธิภาพมากขึ้น แต่ก็อาจส่งผลต่ออายุการใช้งานแบตเตอรี่ด้วย"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ตั้งปลุกแล้ว"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"กล้องและไมค์ปิดอยู่"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{การแจ้งเตือน # รายการ}other{การแจ้งเตือน # รายการ}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"กำลังออกอากาศ"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"หยุดการออกอากาศ <xliff:g id="APP_NAME">%1$s</xliff:g> ไหม"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"หากคุณออกอากาศ <xliff:g id="SWITCHAPP">%1$s</xliff:g> หรือเปลี่ยนแปลงเอาต์พุต การออกอากาศในปัจจุบันจะหยุดลง"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"ออกอากาศ <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"เปลี่ยนเอาต์พุต"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ไม่ทราบ"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"HH:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml
index 4565f35fbef2..9cd060f2cabf 100644
--- a/packages/SystemUI/res/values-th/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"ปิด"</item>
<item msgid="460891964396502657">"เปิด"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"ไม่พร้อมใช้งาน"</item>
+ <item msgid="8014986104355098744">"ปิด"</item>
+ <item msgid="5966994759929723339">"เปิด"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 5e2fb53c5e09..134005c85545 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Naka-lock ang device"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Sina-scan ang mukha"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Ipadala"</string>
- <string name="phone_label" msgid="5715229948920451352">"buksan ang telepono"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"buksan ang voice assist"</string>
- <string name="camera_label" msgid="8253821920931143699">"buksan ang camera"</string>
<string name="cancel" msgid="1089011503403416730">"Kanselahin"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Kumpirmahin"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Subukang muli"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Aktibo ang i-off ang mga sensor"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"I-clear ang lahat ng notification."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">May <xliff:g id="NUMBER_1">%s</xliff:g> pang notification sa loob.</item>
- <item quantity="other">May <xliff:g id="NUMBER_1">%s</xliff:g> pang notification sa loob.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{May # pang notification sa loob.}one{May # pang notification sa loob.}other{May # pang notification sa loob.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Naka-lock ang screen sa pahigang oryentasyon."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Naka-lock ang screen sa patayong oryentasyon."</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"I-auto rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Awtomatikong i-rotate ang screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasyon"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screen saver"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Access sa camera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Access sa mic"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Available"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Ino-on…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Na-on ang Data Saver"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d device</item>
- <item quantity="other">%d na device</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# device}one{# device}other{# na device}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Flashlight"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Ginagamit na camera"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobile data"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"I-tap ulit"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Mag-swipe pataas para buksan"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Pindutin ang icon ng unlock para buksan"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Na-unlock gamit ang mukha. Pindutin ang icon ng unlock para buksan."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Na-unlock gamit ang mukha. Pindutin para buksan."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Nakilala ang mukha. Pindutin para buksan."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Gusto mo bang ipagpatuloy ang iyong session?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Magsimulang muli"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Oo, magpatuloy"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Naka-guest mode ka"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Kapag nagdagdag ka ng bagong user, aalis sa guest mode at made-delete ang lahat ng app at data mula sa kasalukuyang session ng bisita."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Naabot na ang limitasyon sa user"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Maaari kang magdagdag ng hanggang <xliff:g id="COUNT">%d</xliff:g> user.</item>
- <item quantity="other">Maaari kang magdagdag ng hanggang <xliff:g id="COUNT">%d</xliff:g> na user.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Isang user lang ang puwedeng gawin.}one{Puwede kang magdagdag ng hanggang # user.}other{Puwede kang magdagdag ng hanggang # na user.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Gusto mo bang alisin ang user?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Made-delete ang lahat ng app at data ng user na ito."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Alisin"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Paalalahanan ako"</string>
<string name="snooze_undo" msgid="2738844148845992103">"I-undo"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Na-snooze ng <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d oras</item>
- <item quantity="other">%d na oras</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d minuto</item>
- <item quantity="other">%d na minuto</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# oras}=2{# oras}one{# oras}other{# na oras}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minuto}one{# minuto}other{# na minuto}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Pantipid ng Baterya"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Button na <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Mga Alerto"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Baterya"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Mga Screenshot"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Mga Pangkalahatang Mensahe"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Setup"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Storage"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Mga Hint"</string>
<string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"i-toggle"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Mga kontrol ng device"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Pumili ng app para magdagdag ng mga kontrol"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> kontrol ang naidagdag.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> na kontrol ang naidagdag.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Nagdagdag ng # kontrol.}one{Nagdagdag ng # kontrol.}other{Nagdagdag ng # na kontrol.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Inalis"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Ginawang paborito"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Ginawang paborito, posisyon <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Idagdag ang tile"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Huwag idagdag"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pumili ng user"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> app ang aktibo</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> na app ang aktibo</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{Aktibo ang # app}one{Aktibo ang # app}other{Aktibo ang # na app}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Bagong impormasyon"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Mga aktibong app"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Aktibo at tumatakbo ang mga app na ito kahit na hindi mo ginagamit. Pinapahusay nito ang functionality ng mga app, pero posible rin itong makaapekto sa tagal ng baterya."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Nakatakda ang alarm"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Naka-off ang camera at mikropono"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}one{# notification}other{# na notification}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Nagbo-broadcast"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Ihinto ang pag-broadcast ng <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Kung magbo-broadcast ka ng <xliff:g id="SWITCHAPP">%1$s</xliff:g> o babaguhin mo ang output, hihinto ang iyong kasalukuyang broadcast"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"I-broadcast ang <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Baguhin ang output"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Hindi alam"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
index 59fed0f6b247..cd7dcf51b279 100644
--- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Naka-off"</item>
<item msgid="460891964396502657">"Naka-on"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Hindi available"</item>
+ <item msgid="8014986104355098744">"Naka-off"</item>
+ <item msgid="5966994759929723339">"Naka-on"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index b52d41d43f8c..fb928b2f5c4a 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Cihaz kilitlendi"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Yüz taranıyor"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Gönder"</string>
- <string name="phone_label" msgid="5715229948920451352">"telefonu aç"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"sesli yardımı aç"</string>
- <string name="camera_label" msgid="8253821920931143699">"kamerayı aç"</string>
<string name="cancel" msgid="1089011503403416730">"İptal"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Onayla"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Tekrar dene"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensörler kapalı ayarı etkin"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Tüm bildirimleri temizle"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">Grup içinde <xliff:g id="NUMBER_1">%s</xliff:g> bildirim daha var.</item>
- <item quantity="one">Grup içinde <xliff:g id="NUMBER_0">%s</xliff:g> bildirim daha var.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Grup içinde # bildirim daha var.}other{Grup içinde # bildirim daha var.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Ekran yatay yönde kilitlendi."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Ekran dikey yönde kilitlendi."</string>
<string name="dessert_case" msgid="9104973640704357717">"Tatlı Kutusu"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Otomatik döndür"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranı otomatik döndür"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Konum"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Ekran koruyucu"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Kamera erişimi"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofon erişimi"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Kullanılabilir"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Açılıyor…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Veri Tasarrufu açık"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d cihaz</item>
- <item quantity="one">%d cihaz</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# cihaz}other{# cihaz}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Fener"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera kullanımda"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobil veri"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Tekrar dokunun"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Açmak için yukarı kaydırın"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Açmak için Kilit açma simgesine basın"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Kilit, yüzünüzle açıldı. Kilit açma simgesine basın."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Cihazın kilidini yüzünüzle açtınız. Açmak için basın."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Yüzünüz tanındı. Açmak için basın."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Oturumunuza devam etmek istiyor musunuz?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Baştan başla"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Evet, devam et"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Misafir modu"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Misafir modundasınız"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Yeni bir kullanıcı eklendiğinde misafir modundan çıkılarak mevcut misafir oturumundaki tüm uygulamalar ve veriler silinir."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Kullanıcı sınırına ulaşıldı"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">En fazla <xliff:g id="COUNT">%d</xliff:g> kullanıcı ekleyebilirsiniz.</item>
- <item quantity="one">Yalnızca bir kullanıcı oluşturulabilir.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Yalnızca bir kullanıcı oluşturulabilir.}other{En çok # kullanıcı ekleyebilirsiniz.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Kullanıcı kaldırılsın mı?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Bu kullanıcının tüm uygulamaları ve verileri silinecek."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Kaldır"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Bana hatırlat"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Geri al"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> süreyle ertelendi"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d saat</item>
- <item quantity="one">%d saat</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d dakika</item>
- <item quantity="one">%d dakika</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# saat}=2{# saat}other{# saat}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# dakika}other{# dakika}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Pil Tasarrufu"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> düğmesi"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Uyarılar"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Pil"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Ekran görüntüleri"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Genel Mesajlar"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Hazır Uygulamalar"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Kurulum"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Depolama alanı"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"İpuçları"</string>
<string name="instant_apps" msgid="8337185853050247304">"Hazır Uygulamalar"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"değiştir"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Cihaz denetimleri"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Denetim eklemek için uygulama seçin"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> kontrol eklendi.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> kontrol eklendi.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# kontrol eklendi.}other{# kontrol eklendi.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Kaldırıldı"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Favoriler listesine eklendi"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Favorilere eklendi, konum: <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Kart ekle"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Kart ekleme"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kullanıcı seçin"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> uygulama etkin</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> uygulama etkin</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# uygulama etkin}other{# uygulama etkin}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Yeni bilgi"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Etkin uygulamalar"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Bu uygulamalar, kullanmadığınız zamanlarda bile etkin ve çalışır durumdadır. Bu durum daha iyi çalışmalarını sağlar ancak pil ömrünü de olumsuz etkileyebilir."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm kuruldu"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera ve mikrofon kapalı"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# bildirim}other{# bildirim}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Yayınlama"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasında anons durdurulsun mu?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> uygulamasında anons yapar veya çıkışı değiştirirseniz mevcut anonsunuz duraklatılır"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> uygulamasında anons yapın"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Çıkışı değiştirme"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Bilinmiyor"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"d MMM, EEE"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:dd"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
index d06d72715d44..28ba7dcb9010 100644
--- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Kapalı"</item>
<item msgid="460891964396502657">"Açık"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Kullanılamıyor"</item>
+ <item msgid="8014986104355098744">"Kapalı"</item>
+ <item msgid="5966994759929723339">"Açık"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 51c61ce3dcf6..3df26f662787 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Пристрій заблоковано"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Сканування обличчя"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Надіслати"</string>
- <string name="phone_label" msgid="5715229948920451352">"відкрити телефон"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"запустити голосові підказки"</string>
- <string name="camera_label" msgid="8253821920931143699">"відкрити камеру"</string>
<string name="cancel" msgid="1089011503403416730">"Скасувати"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Підтвердити"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Повторити спробу"</string>
@@ -205,12 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Активовано вимкнення датчиків"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Очистити всі сповіщення."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one">Ще <xliff:g id="NUMBER_1">%s</xliff:g> сповіщення в групі.</item>
- <item quantity="few">Ще <xliff:g id="NUMBER_1">%s</xliff:g> сповіщення в групі.</item>
- <item quantity="many">Ще <xliff:g id="NUMBER_1">%s</xliff:g> сповіщень у групі.</item>
- <item quantity="other">Ще <xliff:g id="NUMBER_1">%s</xliff:g> сповіщення в групі.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Ще # сповіщення в групі.}one{Ще # сповіщення в групі.}few{Ще # сповіщення в групі.}many{Ще # сповіщень у групі.}other{Ще # сповіщення в групі.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Екран заблоковано в альбомній орієнтації."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Екран заблоковано в книжковій орієнтації."</string>
<string name="dessert_case" msgid="9104973640704357717">"Вітрина десертів"</string>
@@ -228,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматичне обертання"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматично обертати екран"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Геодані"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Заставка"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Доступ до камери"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Доступ до мікрофона"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Дозволено"</string>
@@ -257,12 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Точка доступу"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Увімкнення…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Заощадження трафіку ввімкнено"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d пристрій</item>
- <item quantity="few">%d пристрої</item>
- <item quantity="many">%d пристроїв</item>
- <item quantity="other">%d пристрою</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# пристрій}one{# пристрій}few{# пристрої}many{# пристроїв}other{# пристрою}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Ліхтарик"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Використовується камера"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Мобільне передавання даних"</string>
@@ -319,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Натисніть знову"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Проведіть пальцем угору, щоб відкрити"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Щоб відкрити, натисніть значок розблокування."</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Розблоковано (фейсконтроль). Натисніть значок розблокування."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Розблоковано (фейсконтроль). Натисніть, щоб відкрити."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Обличчя розпізнано. Натисніть, щоб відкрити."</string>
@@ -355,13 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Продовжити сеанс?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почати знову"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Так, продовжити"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Режим гостя"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Ви ввійшли в режимі гостя"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Якщо додати нового користувача, ви вийдете з режиму гостя, а всі додатки й дані з поточного сеансу цього режиму буде видалено."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Ви досягли ліміту користувачів"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Можна додати до <xliff:g id="COUNT">%d</xliff:g> користувача.</item>
- <item quantity="few">Можна додати до <xliff:g id="COUNT">%d</xliff:g> користувачів.</item>
- <item quantity="many">Можна додати до <xliff:g id="COUNT">%d</xliff:g> користувачів.</item>
- <item quantity="other">Можна додати до <xliff:g id="COUNT">%d</xliff:g> користувача.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Можна створити лише одного користувача.}one{Можна додати щонайбільше # користувача.}few{Можна додати щонайбільше # користувачів.}many{Можна додати щонайбільше # користувачів.}other{Можна додати щонайбільше # користувача.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Видалити користувача?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Усі додатки й дані цього користувача буде видалено."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Видалити"</string>
@@ -547,18 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Нагадати"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Відмінити"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Відкладено на <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d година</item>
- <item quantity="few">%d години</item>
- <item quantity="many">%d годин</item>
- <item quantity="other">%d години</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d хвилина</item>
- <item quantity="few">%d хвилини</item>
- <item quantity="many">%d хвилин</item>
- <item quantity="other">%d хвилини</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# година}=2{# години}one{# година}few{# години}many{# годин}other{# години}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# хвилина}one{# хвилина}few{# хвилини}many{# хвилин}other{# хвилини}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Режим енергозбереження"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -707,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Сповіщення"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Акумулятор"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Знімки екрана"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Загальні повідомлення"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Додатки з миттєвим запуском"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Налаштування"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Пам’ять"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Поради"</string>
<string name="instant_apps" msgid="8337185853050247304">"Додатки з миттєвим запуском"</string>
@@ -781,12 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"перемкнути"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Керування пристроями"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Виберіть, для якого додатка налаштувати елементи керування"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one">Додано <xliff:g id="NUMBER_1">%s</xliff:g> елемент керування.</item>
- <item quantity="few">Додано <xliff:g id="NUMBER_1">%s</xliff:g> елементи керування.</item>
- <item quantity="many">Додано <xliff:g id="NUMBER_1">%s</xliff:g> елементів керування.</item>
- <item quantity="other">Додано <xliff:g id="NUMBER_1">%s</xliff:g> елемента керування.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Додано # елемент керування.}one{Додано # елемент керування.}few{Додано # елементи керування.}many{Додано # елементів керування.}other{Додано # елемента керування.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Вилучено"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Додано у вибране"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Додано у вибране, позиція <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -943,12 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Додати параметр"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не додавати параметр"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Виберіть користувача"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one"><xliff:g id="COUNT_1">%s</xliff:g> додаток активний</item>
- <item quantity="few"><xliff:g id="COUNT_1">%s</xliff:g> додатки активні</item>
- <item quantity="many"><xliff:g id="COUNT_1">%s</xliff:g> додатків активні</item>
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> додатка активні</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# додаток активний}one{# додаток активний}few{# додатки активні}many{# додатків активні}other{# додатка активні}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Нова інформація"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активні додатки"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Навіть якщо ви не використовуєте ці додатки, вони залишаються активними й продовжують працювати. Це покращує їх функціональні можливості, але може впливати на час роботи акумулятора."</string>
@@ -977,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Будильник установлено"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камеру й мікрофон вимкнено"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# сповіщення}one{# сповіщення}few{# сповіщення}many{# сповіщень}other{# сповіщення}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Трансляція"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Зупинити трансляцію з додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Якщо ви зміните додаток (<xliff:g id="SWITCHAPP">%1$s</xliff:g>) або аудіовихід, поточну трансляцію буде припинено"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Змінити додаток для трансляції на <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Змінити аудіовихід"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Невідомо"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, d MMM"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
index 040fb4d3d6e3..3f6ca461ac1d 100644
--- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Вимкнено"</item>
<item msgid="460891964396502657">"Увімкнено"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Недоступно"</item>
+ <item msgid="8014986104355098744">"Вимкнено"</item>
+ <item msgid="5966994759929723339">"Увімкнено"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index d2abb7a64454..40e0bb3e3148 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"آلہ مقفل کر دیا گیا"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"اسکیننگ چہرہ"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"بھیجیں"</string>
- <string name="phone_label" msgid="5715229948920451352">"فون کھولیں"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"صوتی معاون کھولیں"</string>
- <string name="camera_label" msgid="8253821920931143699">"کیمرا کھولیں"</string>
<string name="cancel" msgid="1089011503403416730">"منسوخ کريں"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"تصدیق کریں"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"دوبارہ کوشش کریں"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"سینسرز آف فعال ہے"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"سبھی اطلاعات صاف کریں۔"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"<xliff:g id="NUMBER">%s</xliff:g> +"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">اندر <xliff:g id="NUMBER_1">%s</xliff:g> مزید اطلاعات ہیں۔ </item>
- <item quantity="one">اندر <xliff:g id="NUMBER_0">%s</xliff:g> مزید اطلاع ہے۔</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{گروپ میں # مزید اطلاع ہے۔}other{گروپ میں # مزید اطلاعات ہے۔}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"اسکرین لینڈ اسکیپ سمت بندی میں مقفل ہے۔"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"اسکرین پورٹریٹ سمت بندی میں مقفل ہے۔"</string>
<string name="dessert_case" msgid="9104973640704357717">"ڈیزرٹ کیس"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"خود کار طور پر گھمائیں"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"اسکرین کو خود کار طور پر گھمائیں"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"مقام"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"اسکرین سیور"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"کیمرا تک رسائی"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"مائیکروفون تک رسائی"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"دستیاب ہے"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"ہاٹ اسپاٹ"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"آن ہو رہا ہے…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"ڈیٹا سیور آن ہے"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">‏%d آلات</item>
- <item quantity="one">‏%d آلہ</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# آلہ}other{# آلات}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"فلیش لائٹ"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"کیمرا زیر استعمال ہے"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"موبائل ڈیٹا"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"دوبارہ تھپتھپائیں"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"کھولنے کے لیے اوپر سوائپ کريں"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"کھولنے کیلئے انلاک آئیکن دبائیں"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"چہرے سے انلاک کیا گیا۔ کھولنے کیلئے انلاک آئیکن دبائیں۔"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"چہرے سے انلاک کیا گیا۔ کھولنے کے لیے دبائیں۔"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"چہرے کی شناخت ہو گئی۔ کھولنے کے لیے دبائیں۔"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"کیا آپ اپنا سیشن جاری رکھنا چاہتے ہیں؟"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"دوبارہ شروع کریں"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ہاں، جاری رکھیں"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"مہمان وضع"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"آپ مہمان وضع میں ہیں"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"نئے صارف کو شامل کرنے سے مہمان وضع سے باہر نکل جائے گا اور موجودہ مہمان سیشن سے تمام ایپس اور ڈیٹا حذف ہو جائیں گے۔"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"صارف کی حد مکمل ہو گئی"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">صرف <xliff:g id="COUNT">%d</xliff:g> صارفین بنائے جا سکتے ہیں۔</item>
- <item quantity="one">صرف ایک صارف بنایا جا سکتا ہے۔</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{صرف ایک صارف بنایا جا سکتا ہے۔}other{آپ # تک صارفین شامل کر سکتے ہیں۔}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"صارف کو ہٹائیں؟"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"اس صارف کی سبھی ایپس اور ڈیٹا حذف کر دیا جائے گا۔"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"ہٹائیں"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"مجھے یاد دلائیں"</string>
<string name="snooze_undo" msgid="2738844148845992103">"کالعدم کریں"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> کیلئے اسنوز کیا گیا"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">‏‎%d گھنٹے</item>
- <item quantity="one">‏‎%d گھنٹہ</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">‏‎%d منٹ</item>
- <item quantity="one">‏‎%d منٹ</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# گھنٹہ}=2{# گھنٹے}other{# گھنٹے}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# منٹ}other{# منٹ}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"بیٹری سیور"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"بٹن <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"الرٹس"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"بیٹری"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"اسکرین شاٹس"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"عمومی پیغامات"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"فوری ایپس"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"سیٹ اپ"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"اسٹوریج"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"اشارات"</string>
<string name="instant_apps" msgid="8337185853050247304">"فوری ایپس"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ٹوگل کریں"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"آلہ کے کنٹرولز"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"کنٹرولز شامل کرنے کے لیے ایپ منتخب کریں"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> کنٹرولز شامل کر دیے گئے۔</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> کنٹرول شامل کر دیا گیا۔</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# کنٹرول کو شامل کیا گیا۔}other{# کنٹرولز کو شامل کیا گیا۔}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"ہٹا دیا گیا"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"پسند کردہ"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"پسند کردہ، پوزیشن <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ٹائل شامل کریں"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ٹائل شامل نہ کریں"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"صارف منتخب کریں"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ایپس فعال ہیں</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> ایپ فعال ہے</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# ایپ فعال ہے}other{# ایپس فعال ہیں}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"نئی معلومات"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"فعال ایپس"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"یہ ایپس فعال اور چل رہی ہوتی ہیں، تب بھی جب آپ انہیں استعمال نہ کر رہے ہوں۔ اس سے ان کی فعالیت بہتر ہو جاتی ہے لیکن اس سے بیٹری لائف بھی متاثر ہو سکتی ہے۔"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"الارم سیٹ ہوگیا"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"کیمرا اور مائیک آف ہیں"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# اطلاع}other{# اطلاعات}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"نشریات"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> براڈکاسٹنگ روکیں؟"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"اگر آپ <xliff:g id="SWITCHAPP">%1$s</xliff:g> براڈکاسٹ کرتے ہیں یا آؤٹ پٹ کو تبدیل کرتے ہیں تو آپ کا موجودہ براڈکاسٹ رک جائے گا"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> پر براڈکاسٹ کریں"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"آؤٹ پٹ تبدیل کریں"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"نامعلوم"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
index 79d2cf6d0624..05aa4e91e5cc 100644
--- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"آف"</item>
<item msgid="460891964396502657">"آن"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"غیر دستیاب"</item>
+ <item msgid="8014986104355098744">"آف"</item>
+ <item msgid="5966994759929723339">"آن"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 293677ae7daa..a6490f429742 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Qurilma qulflandi"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Yuzni skanerlash"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Yuborish"</string>
- <string name="phone_label" msgid="5715229948920451352">"telefonni ochish"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"ovozli yordamni yoqish"</string>
- <string name="camera_label" msgid="8253821920931143699">"kamerani ochish"</string>
<string name="cancel" msgid="1089011503403416730">"Bekor qilish"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"OK"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Qayta urinish"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Sensorlar nofaol ishlayapti"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Barcha eslatmalarni tozalash."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">Guruhda yana <xliff:g id="NUMBER_1">%s</xliff:g> ta bildirishnoma.</item>
- <item quantity="one">Guruhda yana <xliff:g id="NUMBER_0">%s</xliff:g> ta bildirishnoma.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Ichida yana # ta bildirishnoma bor.}other{Ichida yana # ta bildirishnoma bor.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Ekran eniga holatida qulflandi."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Ekran bo‘yiga holatida qulflandi."</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Avto-burilish"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranning avtomatik burilishi"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Joylashuv"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Ekran lavhasi"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameraga ruxsat"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonga ruxsat"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Mavjud"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Yoqilmoqda…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Trafik tejash yoniq"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d ta qurilma</item>
- <item quantity="one">%d ta qurilma</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# ta qurilma}other{# ta qurilma}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Fonar"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera ishlatilmoqda"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobil internet"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Yana bosing"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Ochish uchun tepaga suring"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Ochish uchun ochish belgisini bosing"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Yuz orqali ochilgan. Ochish uchun ochish belgisini bosing."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Yuz orqali ochildi. Ochish uchun bosing."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Yuz aniqlandi. Ochish uchun bosing."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Seansni davom ettirmoqchimisiz?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Boshidan boshlansin"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ha, davom ettirilsin"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Mehmon rejimi"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Mehmon rejimidasiz"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Yangi foydalanuvchi kiritilsa, mehmon rejimi tark etiladi va joriy mehmon seansidagi barcha ilova va ularning maʼlumotlari tozalanadi."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Limitga yetib keldi"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> tagacha foydalanuvchi qo‘shish mumkin.</item>
- <item quantity="one">Faqat bitta foydalanuvchi yaratish mumkin.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Faqat bitta foydalanuvchi yaratish mumkin.}other{# tagacha foydalanuvchi kiritishingiz mumkin.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Foydalanuvchi olib tashlansinmi?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Ushbu foydalanuvchining barcha ilovalari va ma’lumotlari o‘chirib tashlanadi."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Olib tashlash"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Menga eslatilsin"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Qaytarish"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> muddatga kechiktirildi"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d soat</item>
- <item quantity="one">%d soat</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d daqiqa</item>
- <item quantity="one">%d daqiqa</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# soat}=2{# soat}other{# soat}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# daqiqa}other{# daqiqa}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Quvvat tejash"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> tugmasi"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Bosh ekran"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Bildirishnomalar"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Batareya"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Skrinshotlar"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Umumiy xabarlar"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Darhol ochiladigan ilovalar"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Sozlash"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Xotira"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Maslahatlar"</string>
<string name="instant_apps" msgid="8337185853050247304">"Darhol ochiladigan ilovalar"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"oʻzgartirish"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Qurilmalarni boshqarish"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Boshqaruv elementlarini kiritish uchun ilovani tanlang"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> ta nazorat kiritilgan.</item>
- <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> ta nazorat kiritilgan.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# ta boshqaruv elementi kiritildi.}other{# ta boshqaruv elementi kiritildi.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Olib tashlandi"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Saralanganlarga kiritilgan"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Saralanganlarga kiritilgan, <xliff:g id="NUMBER">%d</xliff:g>-joy"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tugma kiritish"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Tugma kiritilmasin"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Foydalanuvchini tanlang"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> ta ilova faol</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> ta ilova faol</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# ta ilova faol}other{# ta ilova faol}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Yangi axborot"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Faol ilovalar"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Bu ilovalardan foydalanmasangiz ham ular faol va ishlamoqda. Bu ularning ishlashini yaxshilaydi, lekin batareya quvvatiga ham taʼsir qilishi mumkin."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Signal oʻrnatildi"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera va mikrofon yoqilmagan"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ta bildirishnoma}other{# ta bildirishnoma}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Signal uzatish"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga translatsiya toʻxtatilsinmi?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Agar <xliff:g id="SWITCHAPP">%1$s</xliff:g> ilovasiga translatsiya qilsangiz yoki ovoz chiqishini oʻzgartirsangiz, joriy translatsiya toʻxtab qoladi"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ilovasiga translatsiya"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Ovoz chiqishini oʻzgartirish"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Noaniq"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"s:dd"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/tiles_states_strings.xml b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
index b6875974d2de..a84f7698d861 100644
--- a/packages/SystemUI/res/values-uz/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Oʻchiq"</item>
<item msgid="460891964396502657">"Yoniq"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Bandman"</item>
+ <item msgid="8014986104355098744">"Oʻchiq"</item>
+ <item msgid="5966994759929723339">"Yoniq"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index af4bf0fafc5c..3a579f15ead4 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Đã khóa thiết bị"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Quét tìm khuôn mặt"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Gửi"</string>
- <string name="phone_label" msgid="5715229948920451352">"mở điện thoại"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"mở trợ lý thoại"</string>
- <string name="camera_label" msgid="8253821920931143699">"mở máy ảnh"</string>
<string name="cancel" msgid="1089011503403416730">"Hủy"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Xác nhận"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Thử lại"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Tùy chọn tắt cảm biến đang hoạt động"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Xóa tất cả thông báo."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">Còn <xliff:g id="NUMBER_1">%s</xliff:g> thông báo nữa bên trong.</item>
- <item quantity="one">Còn <xliff:g id="NUMBER_0">%s</xliff:g> thông báo nữa bên trong.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{# thông báo khác bên trong.}other{# thông báo khác bên trong.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Màn hình hiện bị khóa theo hướng ngang."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Màn hình hiện bị khóa theo hướng dọc."</string>
<string name="dessert_case" msgid="9104973640704357717">"Tủ trưng bày bánh ngọt"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Tự động xoay"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Tự động xoay màn hình"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Vị trí"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Trình bảo vệ màn hình"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Truy cập máy ảnh"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Truy cập micrô"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Được phép"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Điểm phát sóng"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Đang bật…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Trình tiết kiệm dữ liệu đang bật"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d thiết bị</item>
- <item quantity="one">%d thiết bị</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# thiết bị}other{# thiết bị}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Đèn pin"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Máy ảnh đang được sử dụng"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Dữ liệu di động"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Nhấn lại"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Vuốt lên để mở"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Nhấn biểu tượng mở khoá để mở"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Đã mở khoá bằng khuôn mặt. Nhấn vào biểu tượng mở khoá để mở."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Đã mở khoá bằng khuôn mặt. Nhấn để mở."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Đã nhận diện khuôn mặt. Nhấn để mở."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Bạn có muốn tiếp tục phiên của mình không?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Bắt đầu lại"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Có, tiếp tục"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Chế độ khách"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Bạn đang ở chế độ khách"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Khi thêm người dùng mới, chế độ khách sẽ bị thoát và mọi ứng dụng cũng như dữ liệu trong phiên khách hiện tại sẽ bị xoá."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Đã đạt đến giới hạn người dùng"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">Bạn có thể thêm tối đa <xliff:g id="COUNT">%d</xliff:g> người dùng.</item>
- <item quantity="one">Chỉ có thể tạo một người dùng.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Bạn chỉ có thể tạo một người dùng.}other{Bạn có thể thêm tối đa # người dùng.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Xóa người dùng?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Tất cả các ứng dụng và dữ liệu của người dùng này sẽ bị xóa."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Xóa"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Nhắc tôi"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Hủy"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Báo lại sau <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d giờ</item>
- <item quantity="one">%d giờ</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d phút</item>
- <item quantity="one">%d phút</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# giờ}=2{# giờ}other{# giờ}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# phút}other{# phút}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Trình tiết kiệm pin"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Nút <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Cảnh báo"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Pin"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Ảnh chụp màn hình"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Thông báo chung"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Ứng dụng tức thì"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Thiết lập"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Bộ nhớ"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Gợi ý"</string>
<string name="instant_apps" msgid="8337185853050247304">"Ứng dụng tức thì"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"bật/tắt"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Điều khiển thiết bị"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Chọn ứng dụng để thêm các tùy chọn điều khiển"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">Đã thêm <xliff:g id="NUMBER_1">%s</xliff:g> tùy chọn điều khiển.</item>
- <item quantity="one">Đã thêm <xliff:g id="NUMBER_0">%s</xliff:g> tùy chọn điều khiển.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{Đã thêm # chế độ điều khiển.}other{Đã thêm # chế độ điều khiển.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Đã xóa"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Được yêu thích"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Được yêu thích, vị trí số <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Thêm ô"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Không thêm ô"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Chọn người dùng"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other">Ứng dụng <xliff:g id="COUNT_1">%s</xliff:g> đang hoạt động</item>
- <item quantity="one">Ứng dụng <xliff:g id="COUNT_0">%s</xliff:g> đang hoạt động</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{Ứng dụng # đang hoạt động}other{Ứng dụng # đang hoạt động}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Thông tin mới"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Ứng dụng đang hoạt động"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Các ứng dụng này vẫn hoạt động và đang chạy ngay cả khi bạn không sử dụng chúng. Việc này giúp cải thiện các chức năng nhưng đồng thời cũng có thể ảnh hưởng đến thời lượng pin."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Đã đặt chuông báo"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Máy ảnh và micrô đang tắt"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# thông báo}other{# thông báo}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Phát sóng"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Dừng phát <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Nếu bạn phát <xliff:g id="SWITCHAPP">%1$s</xliff:g> hoặc thay đổi đầu ra, phiên truyền phát hiện tại sẽ dừng"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Phát <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Thay đổi đầu ra"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Không xác định"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
index 827c72ae2867..482a32f902b4 100644
--- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Đang tắt"</item>
<item msgid="460891964396502657">"Đang bật"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Không có sẵn"</item>
+ <item msgid="8014986104355098744">"Tắt"</item>
+ <item msgid="5966994759929723339">"Đang bật"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 9724a2e7acdd..b46406e9d31e 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"设备已锁定"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"正在扫描面孔"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"发送"</string>
- <string name="phone_label" msgid="5715229948920451352">"打开电话"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"打开语音助理"</string>
- <string name="camera_label" msgid="8253821920931143699">"打开相机"</string>
<string name="cancel" msgid="1089011503403416730">"取消"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"确认"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"重试"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"传感器已关闭"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"清除所有通知。"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">此群组内还有 <xliff:g id="NUMBER_1">%s</xliff:g> 条通知。</item>
- <item quantity="one">此群组内还有 <xliff:g id="NUMBER_0">%s</xliff:g> 条通知。</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{此群组内还有 # 条通知。}other{此群组内还有 # 条通知。}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"屏幕锁定为横屏模式。"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"屏幕锁定为纵向模式。"</string>
<string name="dessert_case" msgid="9104973640704357717">"甜品盒"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自动旋转屏幕"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自动旋转屏幕"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"位置信息"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"屏保"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"摄像头使用权限"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"麦克风使用权限"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"已允许"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"热点"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"正在开启…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"流量节省程序已开启"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d 台设备</item>
- <item quantity="one">%d 台设备</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# 部设备}other{# 部设备}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"手电筒"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"相机正在使用中"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"移动数据"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"请再点按一次"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"向上滑动即可打开"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"按下解锁图标即可打开"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"已通过面孔识别解锁。按下解锁图标即可打开。"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"已通过面孔识别解锁。点按即可打开。"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"识别出面孔。点按即可打开。"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"要继续您的会话吗?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新开始"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"是,继续"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"访客模式"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"您当前处于访客模式"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"如果添加新用户,系统将退出访客模式并删除当前访客会话中的所有应用和数据。"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"已达到用户数上限"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">您最多可以添加 <xliff:g id="COUNT">%d</xliff:g> 位用户。</item>
- <item quantity="one">您只能创建一位用户。</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{您只能创建一位用户。}other{您最多可添加 # 位用户。}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"是否移除用户?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"此用户的所有应用和数据均将被删除。"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"移除"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"提醒我"</string>
<string name="snooze_undo" msgid="2738844148845992103">"撤消"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"已延后 <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d 小时</item>
- <item quantity="one">%d 小时</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d 分钟</item>
- <item quantity="one">%d 分钟</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# 小时}=2{# 小时}other{# 小时}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# 分钟}other{# 分钟}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"省电模式"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g>按钮"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"提醒"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"电池"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"屏幕截图"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"常规消息"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"免安装应用"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"设置"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"存储空间"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"提示"</string>
<string name="instant_apps" msgid="8337185853050247304">"免安装应用"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"开启/关闭"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"设备控制器"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"选择要添加控制器的应用"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">已添加 <xliff:g id="NUMBER_1">%s</xliff:g> 个控件。</item>
- <item quantity="one">已添加 <xliff:g id="NUMBER_0">%s</xliff:g> 个控件。</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{已添加 # 个控件。}other{已添加 # 个控件。}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"已移除"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"已收藏"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"已收藏,位置:<xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"添加图块"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"不添加图块"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"选择用户"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> 个应用正在运行</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> 个应用正在运行</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# 个应用处于活动状态}other{# 个应用处于活动状态}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"新信息"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"已开启的应用"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"这些应用正在保持活跃运行状态,即使您没有在使用它们。这可以改进它们的功能,但可能会影响到电池续航时间。"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"闹钟已设置"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"摄像头和麦克风已关闭"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 条通知}other{# 条通知}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"正在广播"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止广播“<xliff:g id="APP_NAME">%1$s</xliff:g>”的内容吗?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如果广播“<xliff:g id="SWITCHAPP">%1$s</xliff:g>”的内容或更改输出来源,当前的广播就会停止"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"广播“<xliff:g id="SWITCHAPP">%1$s</xliff:g>”的内容"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"更改输出来源"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"未知"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
index 3c628721aa5b..b476255bc669 100644
--- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"已关闭"</item>
<item msgid="460891964396502657">"已开启"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"不可用"</item>
+ <item msgid="8014986104355098744">"关闭"</item>
+ <item msgid="5966994759929723339">"开启"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index abcf78b8ad42..1958d0df7401 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"裝置已上鎖"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"掃瞄緊面孔"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"傳送"</string>
- <string name="phone_label" msgid="5715229948920451352">"開啟電話"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"開啟語音助手"</string>
- <string name="camera_label" msgid="8253821920931143699">"開啟相機"</string>
<string name="cancel" msgid="1089011503403416730">"取消"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"確認"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"請再試一次"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"已啟用「感應器關閉」"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"清除所有通知。"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">裡面還有 <xliff:g id="NUMBER_1">%s</xliff:g> 個通知。</item>
- <item quantity="one">裡面還有 <xliff:g id="NUMBER_0">%s</xliff:g> 個通知。</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{裡面還有 # 個通知。}other{裡面還有 # 個通知。}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"螢幕已鎖定為橫向模式。"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"螢幕已鎖定為垂直模式。"</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動旋轉"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自動旋轉螢幕"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"位置"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"螢幕保護程式"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"相機存取權"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"麥克風存取權"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"允許"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"熱點"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"正在開啟…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"數據節省模式已開啟"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d 部裝置</item>
- <item quantity="one">%d 部裝置</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# 部裝置}other{# 部裝置}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"電筒"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"相機使用中"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"流動數據"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"再次輕按"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"向上滑動即可開啟"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"按解鎖圖示即可開啟"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"已使用面孔解鎖。按解鎖圖示即可開啟。"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"已使用面孔解鎖。按下即可開啟。"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"已識別面孔。按下即可開啟。"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"您要繼續您的工作階段嗎?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"是的,請繼續"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"訪客模式"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"您正在使用訪客模式"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"新增使用者後,系統就會結束訪客模式,並刪除目前訪客工作階段中的所有應用程式和資料。"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"已達到使用者上限"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">您可以加入多達 <xliff:g id="COUNT">%d</xliff:g> 個使用者。</item>
- <item quantity="one">只可以建立一個使用者。</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{只可建立一位使用者。}other{您可以加入多達 # 位使用者。}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"移除使用者?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"系統將會刪除這個使用者的所有應用程式和資料。"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"移除"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"提醒我"</string>
<string name="snooze_undo" msgid="2738844148845992103">"復原"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"已延後 <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d 個小時</item>
- <item quantity="one">%d 個小時</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d 分鐘</item>
- <item quantity="one">%d 分鐘</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# 小時}=2{# 小時}other{# 小時}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# 分鐘}other{# 分鐘}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"省電模式"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> 鍵"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"通知"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"電池"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"螢幕擷取畫面"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"一般訊息"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"免安裝應用程式"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"設定"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"儲存空間"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"提示"</string>
<string name="instant_apps" msgid="8337185853050247304">"免安裝應用程式"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"切換"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"裝置控制"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"選擇要新增控制項的應用程式"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">已新增 <xliff:g id="NUMBER_1">%s</xliff:g> 個控制項。</item>
- <item quantity="one">已新增 <xliff:g id="NUMBER_0">%s</xliff:g> 個控制項。</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{已新增 # 個控制項。}other{已新增 # 個控制項。}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"已移除"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"已加入收藏"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"已加入至收藏位置 <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"新增圖塊"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"不要新增圖塊"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"選取使用者"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other">已啟用 <xliff:g id="COUNT_1">%s</xliff:g> 個應用程式</item>
- <item quantity="one">已啟用 <xliff:g id="COUNT_0">%s</xliff:g> 個應用程式</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{已啟用 # 個應用程式}other{已啟用 # 個應用程式}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"新資料"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"使用中的應用程式"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"這些應用程式已啟用並執行 (即使您沒有使用)。這會提升應用程式的功能,但也可影響電池壽命。"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"已設定鬧鐘"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"相機和麥克風已關閉"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 則通知}other{# 則通知}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"廣播"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止廣播「<xliff:g id="APP_NAME">%1$s</xliff:g>」的內容嗎?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如要廣播「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容或變更輸出來源,系統就會停止廣播目前的內容"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"廣播「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"變更輸出來源"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"不明"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"MMM d EEE"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
index ee4106639a67..ab8e961a1f47 100644
--- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"關閉"</item>
<item msgid="460891964396502657">"開啟"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"無法使用"</item>
+ <item msgid="8014986104355098744">"已關閉"</item>
+ <item msgid="5966994759929723339">"已開啟"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 32d48ced6d82..22d0544f3314 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"裝置已鎖定"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"掃描臉孔"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"傳送"</string>
- <string name="phone_label" msgid="5715229948920451352">"開啟電話"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"開啟語音小幫手"</string>
- <string name="camera_label" msgid="8253821920931143699">"開啟攝影機"</string>
<string name="cancel" msgid="1089011503403416730">"取消"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"確認"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"再試一次"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"感應器已關閉"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"清除所有通知。"</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="other">群組中還有 <xliff:g id="NUMBER_1">%s</xliff:g> 則通知。</item>
- <item quantity="one">群組中還有 <xliff:g id="NUMBER_0">%s</xliff:g> 則通知。</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{其中還有 # 則通知。}other{其中還有 # 則通知。}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"螢幕已鎖定為橫向模式。"</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"螢幕已鎖定為垂直模式。"</string>
<string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動旋轉"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自動旋轉螢幕"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"定位"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"螢幕保護程式"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"相機存取權"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"麥克風存取權"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"可以使用"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"無線基地台"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"開啟中…"</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"數據節省模式已開啟"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="other">%d 個裝置</item>
- <item quantity="one">%d 個裝置</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# 部裝置}other{# 部裝置}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"手電筒"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"正在使用相機"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"行動數據"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"再輕觸一次"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"向上滑動即可開啟"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"按下「解鎖」圖示即可開啟"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"裝置已透過人臉解鎖,按下「解鎖」圖示即可開啟。"</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"裝置已透過你的臉解鎖,按下即可開啟。"</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"臉孔辨識完成,按下即可開啟。"</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"你要繼續這個工作階段嗎?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"是,繼續"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"訪客模式"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"你目前處於訪客模式"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"新增使用者後,系統就會結束訪客模式,並刪除目前訪客工作階段中的所有應用程式和資料。"</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"已達使用者數量上限"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="other">最多可新增 <xliff:g id="COUNT">%d</xliff:g> 位使用者。</item>
- <item quantity="one">只能建立 1 位使用者。</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{只能建立 1 位使用者。}other{最#多可新增 # 位使用者。}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"要移除使用者嗎?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"系統將刪除這個使用者的所有應用程式和資料。"</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"移除"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"提醒我"</string>
<string name="snooze_undo" msgid="2738844148845992103">"復原"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"已延後 <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="other">%d 小時</item>
- <item quantity="one">%d 小時</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="other">%d 分鐘</item>
- <item quantity="one">%d 分鐘</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# 小時}=2{# 小時}other{# 小時}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# 分鐘}other{# 分鐘}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"省電模式"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> 按鈕"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Home 鍵"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"快訊"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"電池"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"螢幕截圖"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"一般訊息"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"免安裝應用程式"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"設定"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"儲存空間"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"提示"</string>
<string name="instant_apps" msgid="8337185853050247304">"免安裝應用程式"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"切換"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"裝置控制"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"選擇應用程式以新增控制項"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="other">已新增 <xliff:g id="NUMBER_1">%s</xliff:g> 個控制項。</item>
- <item quantity="one">已新增 <xliff:g id="NUMBER_0">%s</xliff:g> 個控制項。</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{已新增 # 個控制項。}other{已新增 # 個控制項。}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"已移除"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"已加入收藏"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"已加入收藏,位置 <xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"新增設定方塊"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"不要新增設定方塊"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"選取使用者"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="other"><xliff:g id="COUNT_1">%s</xliff:g> 個應用程式正在運作</item>
- <item quantity="one"><xliff:g id="COUNT_0">%s</xliff:g> 個應用程式正在運作</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# 個應用程式使用中}other{# 個應用程式使用中}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"新資訊"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"使用中的應用程式"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"即使您並未使用,這些應用程式仍會持續啟用並執行。這可提升其功能,但也可能影響電池續航力。"</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"鬧鐘設定成功"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"已關閉相機和麥克風"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 則通知}other{# 則通知}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"廣播"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止播送「<xliff:g id="APP_NAME">%1$s</xliff:g>」的內容嗎?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如果播送「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容或變更輸出來源,系統就會停止播送目前的內容"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"播送「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"變更輸出來源"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"不明"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"MMM d EEE"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
index 1f707408b95b..3d6a546e6103 100644
--- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"已關閉"</item>
<item msgid="460891964396502657">"已開啟"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"無法使用"</item>
+ <item msgid="8014986104355098744">"已關閉"</item>
+ <item msgid="5966994759929723339">"已開啟"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 6545d729cc01..4000ce00de59 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -124,9 +124,6 @@
<string name="accessibility_lock_icon" msgid="661492842417875775">"Idivayisi ikhiyiwe"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Ukuskena ubuso"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Thumela"</string>
- <string name="phone_label" msgid="5715229948920451352">"vula ifoni"</string>
- <string name="voice_assist_label" msgid="3725967093735929020">"vula isilekeleli sezwi"</string>
- <string name="camera_label" msgid="8253821920931143699">"vula ikhamera"</string>
<string name="cancel" msgid="1089011503403416730">"Khansela"</string>
<string name="biometric_dialog_confirm" msgid="2005978443007344895">"Qinisekisa"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"Zama futhi"</string>
@@ -205,10 +202,7 @@
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Izinzwa zivalwe kokusebenzayo"</string>
<string name="accessibility_clear_all" msgid="970525598287244592">"Susa zonke izaziso."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <plurals name="notification_group_overflow_description" formatted="false" msgid="91483442850649192">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> izaziso eziningi ngaphakathi.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> izaziso eziningi ngaphakathi.</item>
- </plurals>
+ <string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{isaziso esingu-# esengeziwe ngaphakathi.}one{izaziso ezingu-# ezengeziwe ngaphakathi.}other{izaziso ezingu-# ezengeziwe ngaphakathi.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Isikrini sikhiyelwe ngomumo we-landscape."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Isikrini sikhiyelwe ngomumo we-portrait."</string>
<string name="dessert_case" msgid="9104973640704357717">"Isikhwama soswidi"</string>
@@ -226,6 +220,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Ukuphenduka okuzenzakalelayo"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Phendula iskrini ngokuzenzakalela"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Indawo"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Isilondolozi sesikrini"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Ukufinyelela kwekhamera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Ukufinyelela kwe-mic"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Iyatholakala"</string>
@@ -255,10 +250,7 @@
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"I-Hotspot"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Iyavula..."</string>
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Iseva yedatha ivuliwe"</string>
- <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
- <item quantity="one">%d amadivayisi</item>
- <item quantity="other">%d amadivayisi</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{idivayisi engu-#}one{amadivayisi angu-#}other{amadivayisi angu-#}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"I-Flashlight"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Ikhamera esetshenziswayo"</string>
<string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Idatha yeselula"</string>
@@ -315,6 +307,8 @@
<string name="tap_again" msgid="1315420114387908655">"Thepha futhi"</string>
<string name="keyguard_unlock" msgid="8031975796351361601">"Swayiphela phezulu ukuze uvule"</string>
<string name="keyguard_unlock_press" msgid="9140109453735019209">"Cindezela isithonjana sokuvula ukuze uvule"</string>
+ <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) -->
+ <skip />
<string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Ivulwe ngobuso. Cindezela isithonjana sokuvula ukuze uvule."</string>
<string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Vula ngobuso. Cindezela ukuze uvule."</string>
<string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Ubuso buyaziwa. Cindezela ukuze uvule."</string>
@@ -351,11 +345,11 @@
<string name="guest_wipe_session_message" msgid="3393823610257065457">"Ingabe ufuna ukuqhubeka ngesikhathi sakho?"</string>
<string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Qala phansi"</string>
<string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yebo, qhubeka"</string>
+ <string name="guest_notification_app_name" msgid="2110425506754205509">"Imodi yesivakashi"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Usemodini yesivakashi"</string>
+ <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ukwengeza umsebenzisi omusha kuzokhipha imodi yesivakashi futhi kusule wonke ama-app nedatha kusuka esikhathini sesihambeli samanje."</string>
<string name="user_limit_reached_title" msgid="2429229448830346057">"Kufinyelelwe kumkhawulo womsebenzisi"</string>
- <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398">
- <item quantity="one">Ungangeza kufikela kubasebenzisi abangu-<xliff:g id="COUNT">%d</xliff:g>.</item>
- <item quantity="other">Ungangeza kufikela kubasebenzisi abangu-<xliff:g id="COUNT">%d</xliff:g>.</item>
- </plurals>
+ <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Umsebenzisi oyedwa kuphela ongasungulwa.}one{Ungangeza kufikela kubasebenzisi abangu-#.}other{Ungangeza kufikela kubasebenzisi abangu-#.}}"</string>
<string name="user_remove_user_title" msgid="9124124694835811874">"Susa umsebenzisi?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Zonke izinhlelo zokusebenza nedatha yalo msebenzisi kuzosuswa."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Susa"</string>
@@ -541,14 +535,8 @@
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Ngikhumbuze"</string>
<string name="snooze_undo" msgid="2738844148845992103">"Susa"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Kusnuzwe u-<xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
- <plurals name="snoozeHourOptions" formatted="false" msgid="2066838694120718170">
- <item quantity="one">%d amahora</item>
- <item quantity="other">%d amahora</item>
- </plurals>
- <plurals name="snoozeMinuteOptions" formatted="false" msgid="8998483159208055980">
- <item quantity="one">%d amaminithi</item>
- <item quantity="other">%d amaminithi</item>
- </plurals>
+ <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{ihora elingu-#}=2{amahora angu-#}one{amahora angu-#}other{amahora angu-#}}"</string>
+ <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{umzuzu ongu-#}one{imizuzu engu-#}other{imizuzu engu-#}}"</string>
<string name="battery_detail_switch_title" msgid="6940976502957380405">"Isilondolozi sebhethri"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Inkinobho <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Ekhaya"</string>
@@ -697,7 +685,8 @@
<string name="notification_channel_alerts" msgid="3385787053375150046">"Izexwayiso"</string>
<string name="notification_channel_battery" msgid="9219995638046695106">"Ibhethri"</string>
<string name="notification_channel_screenshot" msgid="7665814998932211997">"Izithombe-skrini"</string>
- <string name="notification_channel_general" msgid="4384774889645929705">"Imilayezo ejwayelekile"</string>
+ <string name="notification_channel_instant" msgid="7556135423486752680">"Ama-App Asheshayo"</string>
+ <string name="notification_channel_setup" msgid="7660580986090760350">"Ukusetha"</string>
<string name="notification_channel_storage" msgid="2720725707628094977">"Isitoreji"</string>
<string name="notification_channel_hints" msgid="7703783206000346876">"Ukubonisa"</string>
<string name="instant_apps" msgid="8337185853050247304">"Izinhlelo zokusebenza ezisheshayo"</string>
@@ -771,10 +760,7 @@
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"guqula"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Izilawuli zezinsiza"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Khetha uhlelo lokusebenza ukwengeza izilawuli"</string>
- <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
- <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> ukulawulwa okwengeziwe.</item>
- <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> ukulawulwa okwengeziwe.</item>
- </plurals>
+ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{ulawulo olu-# olwengeziwe.}one{ukulawulwa okungu-# okwengeziwe.}other{ukulawulwa okungu-# okwengeziwe.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Isusiwe"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Kwenziwe intandokazi"</string>
<string name="accessibility_control_favorite_position" msgid="54220258048929221">"Kwenziwe intandokazi, isimo esiyi-<xliff:g id="NUMBER">%d</xliff:g>"</string>
@@ -931,10 +917,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Engeza ithayela"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ungafaki ithayela"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Khetha umsebenzisi"</string>
- <plurals name="fgs_manager_footer_label" formatted="false" msgid="790443735462280164">
- <item quantity="one">Ama-app angu-<xliff:g id="COUNT_1">%s</xliff:g> ayasebenza</item>
- <item quantity="other">Ama-app angu-<xliff:g id="COUNT_1">%s</xliff:g> ayasebenza</item>
- </plurals>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{I-app e-# iyasebenza}one{Ama-app angu-# ayasebenza}other{Ama-app angu-# ayasebenza}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Ulwazi olusha"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Ama-app asebenzayo"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Lama-app ayaqhubeka esebenza, ngisho nalapho ungawasebenzisi. Lokhu kuthuthukisa ukusebenza kwawo, kodwa kungase kuthinte impilo yawo yebhethri."</string>
@@ -963,4 +946,13 @@
<string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"I-alamu isethiwe"</string>
<string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Ikhamera nemakrofoni kuvaliwe"</string>
<string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{Isaziso esingu-#}one{Izaziso ezingu-#}other{Izaziso ezingu-#}}"</string>
+ <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Ukusakaza"</string>
+ <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Misa ukusakaza i-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
+ <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Uma usakaza i-<xliff:g id="SWITCHAPP">%1$s</xliff:g> noma ushintsha okuphumayo, ukusakaza kwakho kwamanje kuzoma"</string>
+ <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Sakaza i-<xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Shintsha okuphumayo"</string>
+ <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Akwaziwa"</string>
+ <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string>
+ <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
+ <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
index cc8bbb071449..81c46364a9fd 100644
--- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
@@ -171,4 +171,9 @@
<item msgid="146088982397753810">"Valiwe"</item>
<item msgid="460891964396502657">"Vuliwe"</item>
</string-array>
+ <string-array name="tile_states_dream">
+ <item msgid="6184819793571079513">"Ayitholakali"</item>
+ <item msgid="8014986104355098744">"Valiwe"</item>
+ <item msgid="5966994759929723339">"Vuliwe"</item>
+ </string-array>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 3228c3c9d37f..9a71995383ac 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -108,12 +108,6 @@
<attr name="android:layout" />
</declare-styleable>
- <declare-styleable name="HybridNotificationTheme">
- <attr name="hybridNotificationStyle" format="reference" />
- <attr name="hybridNotificationTitleStyle" format="reference" />
- <attr name="hybridNotificationTextStyle" format="reference" />
- </declare-styleable>
-
<declare-styleable name="PluginInflateContainer">
<attr name="viewType" format="string" />
</declare-styleable>
@@ -193,5 +187,9 @@
<declare-styleable name="DreamOverlayDotImageView">
<attr name="dotColor" format="color" />
</declare-styleable>
+
+ <declare-styleable name="DelayableMarqueeTextView">
+ <attr name="marqueeDelay" format="integer" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 02ba271ee9a4..1eece4cee179 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -129,7 +129,6 @@
<color name="smart_reply_button_stroke">@*android:color/accent_device_default</color>
<!-- Biometric dialog colors -->
- <color name="biometric_dialog_dim_color">#80000000</color> <!-- 50% black -->
<color name="biometric_dialog_gray">#ff757575</color>
<color name="biometric_dialog_accent">@color/material_dynamic_primary40</color>
<color name="biometric_dialog_error">#ffd93025</color> <!-- red 600 -->
@@ -233,4 +232,13 @@
<color name="connected_network_secondary_color">#41493D</color>
<color name="dream_overlay_camera_mic_off_dot_color">#FCBE03</color>
+
+ <!-- Air Quality -->
+ <color name="dream_overlay_aqi_good">#689F38</color>
+ <color name="dream_overlay_aqi_moderate">#FBC02D</color>
+ <color name="dream_overlay_aqi_unhealthy_sensitive">#F57C00</color>
+ <color name="dream_overlay_aqi_unhealthy">#C53929</color>
+ <color name="dream_overlay_aqi_very_unhealthy">#AD1457</color>
+ <color name="dream_overlay_aqi_hazardous">#880E4F</color>
+ <color name="dream_overlay_aqi_unknown">#BDC1C6</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 597e88093de8..82a3b58a5155 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -37,12 +37,6 @@
<item>400</item>
</integer-array>
- <!-- Show mic or phone affordance on Keyguard -->
- <bool name="config_keyguardShowLeftAffordance">false</bool>
-
- <!-- Show camera affordance on Keyguard -->
- <bool name="config_keyguardShowCameraAffordance">false</bool>
-
<!-- decay duration (from size_max -> size), in ms -->
<integer name="navigation_bar_deadzone_hold">333</integer>
<integer name="navigation_bar_deadzone_decay">333</integer>
@@ -87,7 +81,7 @@
<!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
<string name="quick_settings_tiles_stock" translatable="false">
- internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction
+ internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream
</string>
<!-- The tiles to display in QuickSettings -->
@@ -292,7 +286,7 @@
<bool name="config_enableFullscreenUserSwitcher">false</bool>
<!-- SystemUIFactory component -->
- <string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIFactory</string>
+ <string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIInitializerImpl</string>
<!-- QS tile shape store width. negative implies fill configuration instead of stroke-->
<dimen name="config_qsTileStrokeWidthActive">-1dp</dimen>
@@ -438,6 +432,9 @@
they were added. -->
<integer name="config_smart_replies_in_notifications_onclick_init_delay">200</integer>
+ <!-- Smartspace trampoline activity that is used when the user taps smartspace. -->
+ <string name="config_smartspaceTrampolineActivityComponent" translatable="false">com.google.android.apps.gsa.staticplugins.opa.smartspace.ExportedSmartspaceTrampolineActivity</string>
+
<!-- Screenshot editing default activity. Must handle ACTION_EDIT image/png intents.
Blank sends the user to the Chooser first.
This name is in the ComponentName flattened format (package/class) -->
@@ -629,11 +626,11 @@
<!-- This value is used when calculating whether the device is in ambient light mode. It is
light mode when the light sensor sample value exceeds above this value. -->
- <integer name="config_ambientLightModeThreshold">5</integer>
+ <integer name="config_ambientLightModeThreshold">10</integer>
<!-- This value is used when calculating whether the device is in ambient dark mode. It is
dark mode when the light sensor sample value drops below this value. -->
- <integer name="config_ambientDarkModeThreshold">2</integer>
+ <integer name="config_ambientDarkModeThreshold">5</integer>
<!-- This value is used when calculating whether the device is in ambient light mode. Each
sample contains light sensor events from this span of time duration. -->
@@ -690,14 +687,11 @@
<!-- Flag to enable privacy dot views, it shall be true for normal case -->
<bool name="config_enablePrivacyDot">true</bool>
- <!-- Flag to enable dream overlay service and its registration -->
- <bool name="config_dreamOverlayServiceEnabled">false</bool>
-
<!-- Class for the communal source connector to be used -->
<string name="config_communalSourceConnector" translatable="false"></string>
<!-- How often in milliseconds to jitter the dream overlay in order to avoid burn-in. -->
- <integer name="config_dreamOverlayBurnInProtectionUpdateIntervalMillis">500</integer>
+ <integer name="config_dreamOverlayBurnInProtectionUpdateIntervalMillis">1000</integer>
<!-- How long in milliseconds before full burn-in protection is achieved. -->
<integer name="config_dreamOverlayMillisUntilFullJitter">240000</integer>
@@ -727,4 +721,25 @@
<item>com.android.keyguard</item>
<item>com.android.systemui</item>
</string-array>
+
+ <!-- The thresholds which determine the color used by the AQI dream overlay.
+ NOTE: This must always be kept sorted from low to high -->
+ <integer-array name="config_dreamAqiThresholds">
+ <item>-1</item>
+ <item>50</item>
+ <item>100</item>
+ <item>150</item>
+ <item>200</item>
+ <item>300</item>
+ </integer-array>
+
+ <!-- The color values which correspond to the thresholds above -->
+ <integer-array name="config_dreamAqiColorValues">
+ <item>@color/dream_overlay_aqi_good</item>
+ <item>@color/dream_overlay_aqi_moderate</item>
+ <item>@color/dream_overlay_aqi_unhealthy_sensitive</item>
+ <item>@color/dream_overlay_aqi_unhealthy</item>
+ <item>@color/dream_overlay_aqi_very_unhealthy</item>
+ <item>@color/dream_overlay_aqi_hazardous</item>
+ </integer-array>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 66488415ff9f..0cafa355d847 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -44,11 +44,52 @@
<!-- The threshold to drag to trigger the edge action -->
<dimen name="navigation_edge_action_drag_threshold">16dp</dimen>
<!-- The threshold to progress back animation for edge swipe -->
- <dimen name="navigation_edge_action_progress_threshold">400dp</dimen>
+ <dimen name="navigation_edge_action_progress_threshold">412dp</dimen>
<!-- The minimum display position of the arrow on the screen -->
<dimen name="navigation_edge_arrow_min_y">64dp</dimen>
<!-- The amount by which the arrow is shifted to avoid the finger-->
- <dimen name="navigation_edge_finger_offset">48dp</dimen>
+ <dimen name="navigation_edge_finger_offset">64dp</dimen>
+
+ <!-- The thickness of the arrow -->
+ <dimen name="navigation_edge_arrow_thickness">4dp</dimen>
+ <!-- The minimum delta needed to change direction / stop triggering back -->
+ <dimen name="navigation_edge_minimum_x_delta_for_switch">32dp</dimen>
+
+ <!-- entry state -->
+ <dimen name="navigation_edge_entry_margin">4dp</dimen>
+ <dimen name="navigation_edge_entry_background_width">8dp</dimen>
+ <dimen name="navigation_edge_entry_background_height">60dp</dimen>
+ <dimen name="navigation_edge_entry_edge_corners">6dp</dimen>
+ <dimen name="navigation_edge_entry_far_corners">6dp</dimen>
+ <dimen name="navigation_edge_entry_arrow_length">10dp</dimen>
+ <dimen name="navigation_edge_entry_arrow_height">7dp</dimen>
+
+ <!-- pre-threshold -->
+ <dimen name="navigation_edge_pre_threshold_margin">4dp</dimen>
+ <dimen name="navigation_edge_pre_threshold_background_width">64dp</dimen>
+ <dimen name="navigation_edge_pre_threshold_background_height">60dp</dimen>
+ <dimen name="navigation_edge_pre_threshold_edge_corners">22dp</dimen>
+ <dimen name="navigation_edge_pre_threshold_far_corners">26dp</dimen>
+
+ <!-- post-threshold / active -->
+ <dimen name="navigation_edge_active_margin">14dp</dimen>
+ <dimen name="navigation_edge_active_background_width">60dp</dimen>
+ <dimen name="navigation_edge_active_background_height">60dp</dimen>
+ <dimen name="navigation_edge_active_edge_corners">30dp</dimen>
+ <dimen name="navigation_edge_active_far_corners">30dp</dimen>
+ <dimen name="navigation_edge_active_arrow_length">8dp</dimen>
+ <dimen name="navigation_edge_active_arrow_height">9dp</dimen>
+
+ <dimen name="navigation_edge_stretch_margin">18dp</dimen>
+ <dimen name="navigation_edge_stretch_background_width">74dp</dimen>
+ <dimen name="navigation_edge_stretch_background_height">60dp</dimen>
+ <dimen name="navigation_edge_stretch_edge_corners">30dp</dimen>
+ <dimen name="navigation_edge_stretch_far_corners">30dp</dimen>
+ <dimen name="navigation_edge_stretched_arrow_length">7dp</dimen>
+ <dimen name="navigation_edge_stretched_arrow_height">10dp</dimen>
+
+ <dimen name="navigation_edge_cancelled_arrow_length">12dp</dimen>
+ <dimen name="navigation_edge_cancelled_arrow_height">0dp</dimen>
<!-- Height of notification icons in the status bar -->
<dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
@@ -366,6 +407,7 @@
<!-- Height of status bar in split shade mode - visible only on large screens -->
<dimen name="large_screen_shade_header_height">@*android:dimen/quick_qs_offset_height</dimen>
<dimen name="large_screen_shade_header_min_height">@dimen/qs_header_row_min_height</dimen>
+ <dimen name="large_screen_shade_header_left_padding">@dimen/qs_horizontal_margin</dimen>
<!-- The top margin of the panel that holds the list of notifications.
On phones it's always 0dp but it's overridden in Car UI
@@ -452,7 +494,7 @@
<dimen name="navigation_key_padding">0dp</dimen>
<!-- Floating rotation button -->
- <dimen name="floating_rotation_button_diameter">40dp</dimen>
+ <dimen name="floating_rotation_button_diameter">52dp</dimen>
<dimen name="floating_rotation_button_min_margin">20dp</dimen>
<dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen>
<dimen name="floating_rotation_button_taskbar_bottom_margin">10dp</dimen>
@@ -511,7 +553,7 @@
<dimen name="qs_dual_tile_padding_horizontal">6dp</dimen>
<dimen name="qs_panel_elevation">4dp</dimen>
<dimen name="qs_panel_padding_bottom">@dimen/footer_actions_height</dimen>
- <dimen name="qs_panel_padding_top">48dp</dimen>
+ <dimen name="qs_panel_padding_top">80dp</dimen>
<dimen name="qs_data_usage_text_size">14sp</dimen>
<dimen name="qs_data_usage_usage_text_size">36sp</dimen>
@@ -522,6 +564,8 @@
<dimen name="qs_footer_icon_size">20dp</dimen>
<dimen name="qs_header_row_min_height">48dp</dimen>
+ <dimen name="qs_header_non_clickable_element_height">24dp</dimen>
+
<dimen name="qs_footer_padding">20dp</dimen>
<dimen name="qs_security_footer_height">88dp</dimen>
<dimen name="qs_security_footer_single_line_height">48dp</dimen>
@@ -634,9 +678,6 @@
<!-- The minimum background radius when swiping to a side for the camera / phone affordances. -->
<dimen name="keyguard_affordance_min_background_radius">30dp</dimen>
- <!-- The size of the touch targets on the keyguard for the affordances. -->
- <dimen name="keyguard_affordance_touch_target_size">120dp</dimen>
-
<!-- The grow amount for the camera and phone circles when hinting -->
<dimen name="hint_grow_amount_sideways">60dp</dimen>
@@ -1018,6 +1059,7 @@
<!-- Since the generic icon isn't circular, we need to scale it down so it still fits within
the circular chip. -->
<dimen name="media_ttt_generic_icon_size_receiver">70dp</dimen>
+ <dimen name="media_ttt_receiver_vert_translation">20dp</dimen>
<!-- Window magnification -->
<dimen name="magnification_border_drag_size">35dp</dimen>
@@ -1204,6 +1246,15 @@
<!-- Maximum overshoot for the pulse expansion -->
<dimen name="pulse_expansion_max_top_overshoot">32dp</dimen>
+ <!-- The drag amount required for the split shade to fully expand. -->
+ <dimen name="split_shade_full_transition_distance">200dp</dimen>
+ <!--
+ The drag amount required for the scrim to fully fade in when expanding the split shade.
+ Currently setting it a little longer than the full shade transition distance, to avoid
+ having a state where the screen is fully black without any content showing.
+ -->
+ <dimen name="split_shade_scrim_transition_distance">300dp</dimen>
+
<!-- Alpha in duration in ms for the auth ripple to become fully vislble. If set to 0,
it is immediately visible. -->
<integer name="auth_ripple_alpha_in_duration">100</integer>
@@ -1400,6 +1451,7 @@
@*android:dimen/status_bar_system_icon_size</dimen>
<dimen name="dream_overlay_camera_mic_off_indicator_size">8dp</dimen>
<dimen name="dream_overlay_notification_indicator_size">6dp</dimen>
+ <dimen name="dream_overlay_grey_chip_width">56dp</dimen>
<!-- Dream overlay complications related dimensions -->
<dimen name="dream_overlay_complication_clock_time_text_size">100sp</dimen>
@@ -1452,6 +1504,10 @@
<dimen name="dream_overlay_y_offset">80dp</dimen>
+ <dimen name="dream_aqi_badge_corner_radius">28dp</dimen>
+ <dimen name="dream_aqi_badge_padding_vertical">6dp</dimen>
+ <dimen name="dream_aqi_badge_padding_horizontal">16dp</dimen>
+
<dimen name="status_view_margin_horizontal">0dp</dimen>
<!-- Media output broadcast dialog QR code picture size -->
@@ -1464,4 +1520,18 @@
<dimen name="media_output_broadcast_info_title_height">24dp</dimen>
<dimen name="media_output_broadcast_info_summary_height">20dp</dimen>
<dimen name="media_output_broadcast_info_edit">18dp</dimen>
+
+ <!-- Broadcast dialog -->
+ <dimen name="broadcast_dialog_title_img_margin_top">18dp</dimen>
+ <dimen name="broadcast_dialog_title_text_size">24sp</dimen>
+ <dimen name="broadcast_dialog_title_text_margin">16dp</dimen>
+ <dimen name="broadcast_dialog_title_text_margin_top">18dp</dimen>
+ <dimen name="broadcast_dialog_subtitle_text_size">14sp</dimen>
+ <dimen name="broadcast_dialog_icon_size">24dp</dimen>
+ <dimen name="broadcast_dialog_icon_margin_top">25dp</dimen>
+ <dimen name="broadcast_dialog_btn_radius">100dp</dimen>
+ <dimen name="broadcast_dialog_btn_margin_bottom">4dp</dimen>
+ <dimen name="broadcast_dialog_btn_text_size">16sp</dimen>
+ <dimen name="broadcast_dialog_btn_minHeight">44dp</dimen>
+ <dimen name="broadcast_dialog_margin">16dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e144b43294c6..9c2542cbd05f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -313,13 +313,6 @@
<string name="accessibility_scanning_face">Scanning face</string>
<!-- Click action label for accessibility for the smart reply buttons (not shown on-screen).". [CHAR LIMIT=NONE] -->
<string name="accessibility_send_smart_reply">Send</string>
- <!-- Content description of the manage notification button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
- <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
- <string name="phone_label">open phone</string>
- <!-- Click action label for accessibility for the voice assist button. This is not shown on-screen and is an accessibility label for the icon which launches the voice assist from the lock screen.[CHAR LIMIT=NONE] -->
- <string name="voice_assist_label">open voice assist</string>
- <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
- <string name="camera_label">open camera</string>
<!-- Button name for "Cancel". [CHAR LIMIT=NONE] -->
<string name="cancel">Cancel</string>
@@ -505,10 +498,10 @@
<string name="notification_group_overflow_indicator">+ <xliff:g id="number" example="3">%s</xliff:g></string>
<!-- Content description describing how many more notifications are in a group [CHAR LIMIT=NONE] -->
- <plurals name="notification_group_overflow_description">
- <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> more notification inside.</item>
- <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> more notifications inside.</item>
- </plurals>
+ <string name="notification_group_overflow_description">{count, plural,
+ =1 {# more notification inside.}
+ other {# more notifications inside.}
+ }</string>
<!-- Format to use to summarize a message from a contact in a single line of text. For example: "Julia: How's it going?". [CHAR LIMIT=NONE] -->
@@ -578,6 +571,8 @@
<!-- QuickSettings: Location [CHAR LIMIT=NONE] -->
<string name="quick_settings_location_label">Location</string>
<!-- QuickSettings: Location (Off) [CHAR LIMIT=NONE] -->
+ <!-- QuickSettings: Screen saver [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_screensaver_label">Screen saver</string>
<!-- QuickSettings: Camera [CHAR LIMIT=NONE] -->
<string name="quick_settings_camera_label">Camera access</string>
<!-- QuickSettings: Microphone [CHAR LIMIT=NONE] -->
@@ -652,10 +647,10 @@
the user why they can't toggle the hotspot tile. [CHAR LIMIT=20] -->
<string name="quick_settings_hotspot_secondary_label_data_saver_enabled">Data Saver is on</string>
<!-- QuickSettings: Hotspot: Secondary label for how many devices are connected to the hotspot [CHAR LIMIT=NONE] -->
- <plurals name="quick_settings_hotspot_secondary_label_num_devices">
- <item quantity="one">%d device</item>
- <item quantity="other">%d devices</item>
- </plurals>
+ <string name="quick_settings_hotspot_secondary_label_num_devices">{count, plural,
+ =1 {# device}
+ other {# devices}
+ }</string>
<!-- QuickSettings: Notifications [CHAR LIMIT=NONE] -->
<!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] -->
<string name="quick_settings_flashlight_label">Flashlight</string>
@@ -807,6 +802,8 @@
<!-- Message shown when lock screen is unlocked (ie: by trust agent) and the user taps the empty space on the lock screen and UDFPS is supported. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] -->
<string name="keyguard_unlock_press">Press the unlock icon to open</string>
+ <!-- Message shown when non-bypass face authentication succeeds. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] -->
+ <string name="keyguard_face_successful_unlock_swipe">Unlocked by face. Swipe up to open.</string>
<!-- Message shown when non-bypass face authentication succeeds and UDFPS is supported. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] -->
<string name="keyguard_face_successful_unlock_press">Unlocked by face. Press the unlock icon to open.</string>
<!-- Message shown when non-bypass face authentication succeeds and UDFPS is supported. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] -->
@@ -911,14 +908,25 @@
<!-- Notification when resuming an existing guest session: Action that continues with the current session [CHAR LIMIT=35] -->
<string name="guest_wipe_session_dontwipe">Yes, continue</string>
+ <!-- App name of the notification when guest mode is entered [CHAR LIMIT=35] -->
+ <string name="guest_notification_app_name">Guest mode</string>
+ <!-- Title of the notification when guest mode is entered [CHAR LIMIT=35] -->
+ <string name="guest_notification_session_active">You are in guest mode</string>
+
+ <!-- Additional message for add user confirmation dialog that is appended when current user is
+ guest and guest is ephemeral. This is to warn users that current guest session
+ would get removed after a new user is added and switched to [CHAR LIMIT=none] -->
+ <string name="user_add_user_message_guest_remove">\n\nAdding a new user will exit guest mode
+ and delete all apps and data from the current guest session.</string>
+
<!-- Title for the dialog that lets users know that the maximum allowed number of users on the device has been reached. [CHAR LIMIT=35]-->
<string name="user_limit_reached_title">User limit reached</string>
<!-- Message that tells people what's the maximum number of uses allowed on the device. [CHAR_LIMIT=NONE]-->
- <plurals name="user_limit_reached_message">
- <item quantity="one">Only one user can be created.</item>
- <item quantity="other">You can add up to <xliff:g id="count" example="3">%d</xliff:g> users.</item>
- </plurals>
+ <string name="user_limit_reached_message">{count, plural,
+ =1 {Only one user can be created.}
+ other {You can add up to # users.}
+ }</string>
<!-- Title of the confirmation dialog for deleting a user [CHAR LIMIT=NONE] -->
<string name="user_remove_user_title">Remove user?</string>
@@ -1473,20 +1481,20 @@
<!-- Notification: Snooze panel: message indicating how long the notification was snoozed for. [CHAR LIMIT=100]-->
<string name="snoozed_for_time">Snoozed for <xliff:g id="time_amount" example="15 minutes">%1$s</xliff:g></string>
- <!-- Notification:Snooze panel: Snooze options for hours -->
- <plurals name="snoozeHourOptions">
- <item quantity="one">%d hour</item>
- <item quantity="two">%d hours</item>
- <item quantity="few">%d hours</item>
- <item quantity="other">%d hours</item>
- </plurals>
-
- <!-- Notification: Snooze panel: Snooze options for minutes -->
- <plurals name="snoozeMinuteOptions">
- <item quantity="one">%d minute</item>
- <item quantity="few">%d minutes</item>
- <item quantity="other">%d minutes</item>
- </plurals>
+ <!-- Notification:Snooze panel: Snooze options for hours [CHAR LIMIT=NONE]-->
+ <string name="snoozeHourOptions">{count, plural,
+ =1 {# hour}
+ =2 {# hours}
+ few {# hours}
+ other {# hours}
+ }</string>
+
+ <!-- Notification: Snooze panel: Snooze options for minutes [CHAR LIMIT=NONE]-->
+ <string name="snoozeMinuteOptions">{count, plural,
+ =1 {# minute}
+ few {# minutes}
+ other {# minutes}
+ }</string>
<!-- Summary of battery saver not available [CHAR LIMIT=NONE] -->
@@ -1896,8 +1904,10 @@
<string name="notification_channel_battery">Battery</string>
<!-- Title for the notification channel dedicated to screenshot progress. [CHAR LIMIT=NONE] -->
<string name="notification_channel_screenshot">Screenshots</string>
- <!-- Title for the notification channel for miscellaneous notices. [CHAR LIMIT=NONE] -->
- <string name="notification_channel_general">General Messages</string>
+ <!-- Title for the notification channel for instant app notices. [CHAR LIMIT=NONE] -->
+ <string name="notification_channel_instant">Instant Apps</string>
+ <!-- Title for the notification channel for setup notices. [CHAR LIMIT=NONE] -->
+ <string name="notification_channel_setup">Setup</string>
<!-- Title for the notification channel for problems with storage (i.e. low disk). [CHAR LIMIT=NONE] -->
<string name="notification_channel_storage">Storage</string>
<!-- Title for the notification channel for hints and suggestions. [CHAR LIMIT=NONE] -->
@@ -2121,10 +2131,10 @@
<!-- Controls management providers screen title [CHAR LIMIT=60]-->
<string name="controls_providers_title">Choose app to add controls</string>
<!-- Number of favorites for controls management screen [CHAR LIMIT=NONE]-->
- <plurals name="controls_number_of_favorites">
- <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> control added.</item>
- <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> controls added.</item>
- </plurals>
+ <string name="controls_number_of_favorites">{count, plural,
+ =1 {# control added.}
+ other {# controls added.}
+ }</string>
<!-- Removed control in management screen [CHAR LIMIT=20] -->
<string name="controls_removed">Removed</string>
@@ -2287,8 +2297,8 @@
<string name="media_output_dialog_disconnected">(disconnected)</string>
<!-- Summary for connecting error message [CHAR LIMIT=NONE] -->
<string name="media_output_dialog_connect_failed">Can\'t switch. Tap to try again.</string>
- <!-- Title for pairing item [CHAR LIMIT=60] -->
- <string name="media_output_dialog_pairing_new">Pair new device</string>
+ <!-- Title for connecting item [CHAR LIMIT=60] -->
+ <string name="media_output_dialog_pairing_new">Connect a device</string>
<!-- Title for launch app [CHAR LIMIT=60] -->
<string name="media_output_dialog_launch_app_text">To cast this session, please open the app.</string>
<!-- App name when can't get app name [CHAR LIMIT=60] -->
@@ -2480,10 +2490,10 @@
<string name="qs_user_switch_dialog_title">Select user</string>
<!-- Label for the entry point to open the dialog which shows currently running applications [CHAR LIMIT=NONE]-->
- <plurals name="fgs_manager_footer_label">
- <item quantity="one"><xliff:g id="count" example="1">%s</xliff:g> app is active</item>
- <item quantity="other"><xliff:g id="count" example="2">%s</xliff:g> apps are active</item>
- </plurals>
+ <string name="fgs_manager_footer_label">{count, plural,
+ =1 {# app is active}
+ other {# apps are active}
+ }</string>
<!-- Content description for a dot indicator in the running application indicating that there
is new information [CHAR LIMIT=NONE] -->
<string name="fgs_dot_content_description">New information</string>
@@ -2545,6 +2555,10 @@
<string name="dream_overlay_status_bar_priority_mode">Priority mode</string>
<!-- Content description for the alarm set icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
<string name="dream_overlay_status_bar_alarm_set">Alarm set</string>
+ <!-- Content description for the camera off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+ <string name="dream_overlay_status_bar_camera_off">Camera is off</string>
+ <!-- Content description for the mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+ <string name="dream_overlay_status_bar_mic_off">Mic is off</string>
<!-- Content description for the camera and mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
<string name="dream_overlay_status_bar_camera_mic_off">Camera and mic are off</string>
<!-- Content description for the notifications indicator icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
@@ -2552,4 +2566,30 @@
=1 {# notification}
other {# notifications}
}</string>
+ <!-- Accessibility label for weather complication on dreams with weather condition and temperature [CHAR_LIMIT=200] -->
+ <string name="dream_overlay_weather_complication_desc">
+ <xliff:g id="weather_condition" example="Partly cloudy">%1$s</xliff:g>, <xliff:g id="temperature" example="7°C">%2$s</xliff:g>
+ </string>
+
+ <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, media app is broadcasting -->
+ <string name="broadcasting_description_is_broadcasting">Broadcasting</string>
+ <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, title -->
+ <string name="bt_le_audio_broadcast_dialog_title">Stop broadcasting <xliff:g id="app_name" example="App Name 1">%1$s</xliff:g>?</string>
+ <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, sub-title -->
+ <string name="bt_le_audio_broadcast_dialog_sub_title">If you broadcast <xliff:g id="switchApp" example="App Name 2">%1$s</xliff:g> or change the output, your current broadcast will stop</string>
+ <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, switch to others app. -->
+ <string name="bt_le_audio_broadcast_dialog_switch_app">Broadcast <xliff:g id="switchApp" example="App Name 2">%1$s</xliff:g></string>
+ <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, different output. -->
+ <string name="bt_le_audio_broadcast_dialog_different_output">Change output</string>
+ <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, media app is unknown -->
+ <string name="bt_le_audio_broadcast_dialog_unknown_name">Unknown</string>
+
+ <!-- Date format for the Dream Date Complication [CHAR LIMIT=NONE] -->
+ <string name="dream_date_complication_date_format">EEE, MMM d</string>
+
+ <!-- Time format for the Dream Time Complication for 12-hour time format [CHAR LIMIT=NONE] -->
+ <string name="dream_time_complication_12_hr_time_format">h:mm</string>
+
+ <!-- Time format for the Dream Time Complication for 24-hour time format [CHAR LIMIT=NONE] -->
+ <string name="dream_time_complication_24_hr_time_format">kk:mm</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 0c25f5473416..112d903c609c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -17,30 +17,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <!-- HybridNotification themes and styles -->
-
- <style name="HybridNotification">
- <item name="hybridNotificationStyle">@style/hybrid_notification</item>
- <item name="hybridNotificationTitleStyle">@style/hybrid_notification_title</item>
- <item name="hybridNotificationTextStyle">@style/hybrid_notification_text</item>
- </style>
-
- <style name="hybrid_notification">
- <item name="android:paddingStart">@*android:dimen/notification_content_margin_start</item>
- <item name="android:paddingEnd">12dp</item>
- </style>
-
- <style name="hybrid_notification_title">
- <item name="android:paddingEnd">4dp</item>
- <item name="android:textAppearance">@*android:style/TextAppearance.DeviceDefault.Notification.Title</item>
- </style>
-
- <style name="hybrid_notification_text"
- parent="@*android:style/Widget.DeviceDefault.Notification.Text">
- <item name="android:paddingEnd">4dp</item>
- </style>
-
-
<style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon">
<item name="android:textSize">@dimen/status_bar_clock_size</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
@@ -289,6 +265,10 @@
<style name="Animation.ShutdownUi" parent="@android:style/Animation.Toast">
</style>
+ <style name="Theme.SystemUI.MediaProjectionAppSelector"
+ parent="@*android:style/Theme.DeviceDefault.Chooser">
+ </style>
+
<!-- Standard animations for hiding and showing the status bar. -->
<style name="Theme.SystemUI" parent="@*android:style/Theme.DeviceDefault.SystemUI">
@@ -1196,4 +1176,48 @@
<item name="android:shadowColor">@color/keyguard_shadow_color</item>
<item name="android:shadowRadius">?attr/shadowRadius</item>
</style>
+
+ <style name="BroadcastDialogTitleStyle">
+ <item name="android:textAppearance">@style/TextAppearanceBroadcastDialogTitle</item>
+ <item name="android:layout_marginStart">@dimen/broadcast_dialog_title_text_margin</item>
+ <item name="android:layout_marginEnd">@dimen/broadcast_dialog_title_text_margin</item>
+ <item name="android:layout_marginTop">@dimen/broadcast_dialog_title_text_margin_top</item>
+ <item name="android:layout_marginBottom">18dp</item>
+ </style>
+
+ <style name="TextAppearanceBroadcastDialogTitle" parent="@android:style/TextAppearance.DeviceDefault.Headline">
+ <item name="android:textSize">@dimen/broadcast_dialog_title_text_size</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textDirection">locale</item>
+ <item name="android:ellipsize">end</item>
+ </style>
+
+ <style name="BroadcastDialogBodyStyle">
+ <item name="android:textAppearance">@style/TextAppearanceBroadcastDialogSubTitle</item>
+ <item name="android:layout_margin">@dimen/broadcast_dialog_title_text_margin</item>
+ </style>
+
+ <style name="TextAppearanceBroadcastDialogSubTitle" parent="@android:style/TextAppearance.DeviceDefault.Headline">
+ <item name="android:textSize">@dimen/broadcast_dialog_subtitle_text_size</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textDirection">locale</item>
+ <item name="android:ellipsize">end</item>
+ </style>
+
+ <style name="BroadcastDialogButtonStyle">
+ <item name="android:textAppearance">@style/TextAppearanceBroadcastDialogButton</item>
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_gravity">center</item>
+ <item name="android:gravity">center</item>
+ <item name="android:stateListAnimator">@null</item>
+ <item name="android:elevation">0dp</item>
+ <item name="android:minHeight">@dimen/broadcast_dialog_btn_minHeight</item>
+ <item name="android:background">@drawable/broadcast_dialog_btn_bg</item>
+ </style>
+
+ <style name="TextAppearanceBroadcastDialogButton" parent="@android:style/TextAppearance.DeviceDefault.Headline">
+ <item name="android:textColor">?androidprv:attr/textColorOnAccent</item>
+ <item name="android:textSize">@dimen/broadcast_dialog_btn_text_size</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res/values/tiles_states_strings.xml b/packages/SystemUI/res/values/tiles_states_strings.xml
index e2734164161f..c8095510e2c2 100644
--- a/packages/SystemUI/res/values/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values/tiles_states_strings.xml
@@ -308,4 +308,14 @@
<item>Off</item>
<item>On</item>
</string-array>
+
+ <!-- State names for dream (screensaver) tile: unavailable, off, on.
+ This subtitle is shown when the tile is in that particular state but does not set its own
+ subtitle, so some of these may never appear on screen. They should still be translated as
+ if they could appear. [CHAR LIMIT=32] -->
+ <string-array name="tile_states_dream">
+ <item>Unavailable</item>
+ <item>Off</item>
+ <item>On</item>
+ </string-array>
</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/xml/combined_qs_header_scene.xml b/packages/SystemUI/res/xml/combined_qs_header_scene.xml
index 0e833265c15f..0fac76d11fbc 100644
--- a/packages/SystemUI/res/xml/combined_qs_header_scene.xml
+++ b/packages/SystemUI/res/xml/combined_qs_header_scene.xml
@@ -25,20 +25,109 @@
<KeyFrameSet>
<!-- These positions are to prevent visual movement of @id/date -->
<KeyPosition
- app:keyPositionType="pathRelative"
+ app:keyPositionType="deltaRelative"
app:percentX="0"
+ app:percentY="0"
app:framePosition="49"
+ app:percentWidth="1"
+ app:percentHeight="1"
+ app:curveFit="linear"
app:motionTarget="@id/date" />
<KeyPosition
- app:keyPositionType="pathRelative"
+ app:keyPositionType="deltaRelative"
app:percentX="1"
+ app:percentY="0.51"
app:framePosition="51"
+ app:percentWidth="1"
+ app:percentHeight="1"
+ app:curveFit="linear"
app:motionTarget="@id/date" />
<KeyAttribute
app:motionTarget="@id/date"
+ app:framePosition="30"
+ android:alpha="0"
+ />
+ <KeyAttribute
+ app:motionTarget="@id/date"
+ app:framePosition="70"
+ android:alpha="0"
+ />
+ <KeyPosition
+ app:keyPositionType="pathRelative"
+ app:percentX="0"
+ app:percentY="0"
+ app:framePosition="0"
+ app:curveFit="linear"
+ app:motionTarget="@id/statusIcons" />
+ <KeyPosition
+ app:keyPositionType="pathRelative"
+ app:percentX="0"
+ app:percentY="0"
+ app:framePosition="50"
+ app:curveFit="linear"
+ app:motionTarget="@id/statusIcons" />
+ <KeyPosition
+ app:keyPositionType="deltaRelative"
+ app:percentX="1"
+ app:percentY="0.51"
+ app:framePosition="51"
+ app:curveFit="linear"
+ app:motionTarget="@id/statusIcons" />
+ <KeyAttribute
+ app:motionTarget="@id/statusIcons"
+ app:framePosition="30"
+ android:alpha="0"
+ />
+ <KeyAttribute
+ app:motionTarget="@id/statusIcons"
+ app:framePosition="70"
+ android:alpha="0"
+ />
+ <KeyPosition
+ app:keyPositionType="deltaRelative"
+ app:percentX="0"
+ app:percentY="0"
app:framePosition="50"
+ app:percentWidth="1"
+ app:percentHeight="1"
+ app:curveFit="linear"
+ app:motionTarget="@id/batteryRemainingIcon" />
+ <KeyPosition
+ app:keyPositionType="deltaRelative"
+ app:percentX="1"
+ app:percentY="0.51"
+ app:framePosition="51"
+ app:percentWidth="1"
+ app:percentHeight="1"
+ app:curveFit="linear"
+ app:motionTarget="@id/batteryRemainingIcon" />
+ <KeyAttribute
+ app:motionTarget="@id/batteryRemainingIcon"
+ app:framePosition="30"
android:alpha="0"
/>
+ <KeyAttribute
+ app:motionTarget="@id/batteryRemainingIcon"
+ app:framePosition="70"
+ android:alpha="0"
+ />
+ <KeyPosition
+ app:motionTarget="@id/carrier_group"
+ app:percentX="1"
+ app:percentY="0.51"
+ app:framePosition="51"
+ app:percentWidth="1"
+ app:percentHeight="1"
+ app:curveFit="linear"
+ app:keyPositionType="deltaRelative" />
+ <KeyAttribute
+ app:motionTarget="@id/carrier_group"
+ app:framePosition="0"
+ android:alpha="0" />
+ <KeyAttribute
+ app:motionTarget="@id/carrier_group"
+ app:framePosition="70"
+ android:alpha="0" />
</KeyFrameSet>
</Transition>
diff --git a/packages/SystemUI/res/xml/large_screen_shade_header.xml b/packages/SystemUI/res/xml/large_screen_shade_header.xml
index 89090513ea37..cdbf8ab0be41 100644
--- a/packages/SystemUI/res/xml/large_screen_shade_header.xml
+++ b/packages/SystemUI/res/xml/large_screen_shade_header.xml
@@ -29,7 +29,12 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/date"
+ app:layout_constraintHorizontal_bias="0"
/>
+ <Transform
+ android:scaleX="1"
+ android:scaleY="1"
+ />
</Constraint>
<Constraint
@@ -47,9 +52,38 @@
<Constraint
android:id="@+id/carrier_group">
+ <Layout
+ app:layout_constraintWidth_min="48dp"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constrainedWidth="true"
+ android:layout_gravity="end|center_vertical"
+ android:layout_marginStart="8dp"
+ app:layout_constraintStart_toEndOf="@id/date"
+ app:layout_constraintEnd_toStartOf="@id/statusIcons"
+ app:layout_constraintTop_toTopOf="@id/clock"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ />
+ <PropertySet
+ android:alpha="1"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/statusIcons">
+ <Layout
+ app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
+ app:layout_constraintStart_toEndOf="@id/carrier_group"
+ app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
+ app:layout_constraintTop_toTopOf="@id/clock"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ />
<PropertySet
android:alpha="1"
- app:customFloatValue="1"
/>
</Constraint>
@@ -64,6 +98,9 @@
app:layout_constraintTop_toTopOf="@id/clock"
app:layout_constraintBottom_toBottomOf="parent"
/>
+ <PropertySet
+ android:alpha="1"
+ />
</Constraint>
<Constraint
@@ -75,6 +112,7 @@
app:layout_constraintTop_toTopOf="@id/date"
app:layout_constraintBottom_toBottomOf="@id/date"
app:layout_constraintStart_toEndOf="@id/batteryRemainingIcon"
+ app:layout_constraintHorizontal_bias="1"
/>
</Constraint>
diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml
index eab7defe1e52..9115d42fc42d 100644
--- a/packages/SystemUI/res/xml/media_session_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_session_collapsed.xml
@@ -19,7 +19,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<Constraint
- android:id="@+id/media_action_barrier"
+ android:id="@+id/media_action_barrier_start"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/media_seamless"
@@ -91,12 +91,16 @@
app:layout_constraintRight_toLeftOf="@id/media_progress_bar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/media_seamless"
- app:layout_constraintLeft_toRightOf="@id/media_action_barrier" />
+ app:layout_constraintLeft_toRightOf="@id/media_action_barrier_start" />
<!-- Showing time while scrubbing isn't available in collapsed mode. -->
<Constraint
android:id="@+id/media_scrubbing_elapsed_time"
- android:visibility="gone" />
+ android:visibility="gone"
+ app:layout_constraintRight_toLeftOf="@id/media_progress_bar"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/media_seamless"
+ app:layout_constraintLeft_toRightOf="@id/media_action_barrier_start" />
<Constraint
android:id="@+id/media_progress_bar"
@@ -124,7 +128,12 @@
<!-- Showing time while scrubbing isn't available in collapsed mode. -->
<Constraint
android:id="@+id/media_scrubbing_total_time"
- android:visibility="gone" />
+ android:visibility="gone"
+ app:layout_constraintVertical_bias="1"
+ app:layout_constraintLeft_toRightOf="@id/media_progress_bar"
+ app:layout_constraintRight_toLeftOf="@id/action0"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/media_seamless" />
<Constraint
android:id="@+id/action0"
diff --git a/packages/SystemUI/res/xml/qqs_header.xml b/packages/SystemUI/res/xml/qqs_header.xml
index c5b4c5d776b9..ee0c4fb6bab8 100644
--- a/packages/SystemUI/res/xml/qqs_header.xml
+++ b/packages/SystemUI/res/xml/qqs_header.xml
@@ -26,22 +26,27 @@
<Layout
android:layout_width="wrap_content"
android:layout_height="0dp"
- app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintStart_toStartOf="@id/begin_guide"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/date"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="packed"
/>
+ <Transform
+ android:scaleX="1"
+ android:scaleY="1"
+ />
</Constraint>
<Constraint
android:id="@+id/date">
<Layout
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="0dp"
+ app:layout_constrainedWidth="true"
app:layout_constraintStart_toEndOf="@id/clock"
- app:layout_constraintEnd_toStartOf="@id/carrier_group"
+ app:layout_constraintEnd_toStartOf="@id/barrier"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0"
@@ -50,17 +55,41 @@
<Constraint
android:id="@+id/statusIcons">
+ <Layout
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_header_non_clickable_element_height"
+ app:layout_constraintHeight_min="@dimen/qs_header_non_clickable_element_height"
+ app:layout_constraintStart_toEndOf="@id/date"
+ app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
+ app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ />
</Constraint>
<Constraint
- android:id="@+id/batteryRemainingIcon" >
+ android:id="@+id/batteryRemainingIcon">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/qs_header_non_clickable_element_height"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintHeight_min="@dimen/qs_header_non_clickable_element_height"
+ app:layout_constraintStart_toEndOf="@id/statusIcons"
+ app:layout_constraintEnd_toEndOf="@id/end_guide"
+ app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ />
</Constraint>
<Constraint
android:id="@+id/carrier_group">
- <CustomAttribute
- app:attributeName="alpha"
- app:customFloatValue="0"
+ <Layout
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ />
+ <PropertySet
+ android:alpha="0"
/>
</Constraint>
@@ -69,9 +98,11 @@
<Layout
android:layout_width="wrap_content"
android:layout_height="0dp"
- app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/date"
+ app:layout_constraintEnd_toEndOf="@id/end_guide"
app:layout_constraintTop_toTopOf="@id/date"
app:layout_constraintBottom_toBottomOf="@id/date"
+ app:layout_constraintHorizontal_bias="1"
/>
</Constraint>
diff --git a/packages/SystemUI/res/xml/qs_header_new.xml b/packages/SystemUI/res/xml/qs_header_new.xml
new file mode 100644
index 000000000000..f39e6bd65b86
--- /dev/null
+++ b/packages/SystemUI/res/xml/qs_header_new.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<ConstraintSet
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/qs_header_constraint"
+>
+
+ <Constraint
+ android:id="@+id/privacy_container">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
+ app:layout_constraintEnd_toEndOf="@id/end_guide"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/carrier_group"
+ app:layout_constraintHorizontal_bias="1"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/clock">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/privacy_container"
+ app:layout_constraintBottom_toTopOf="@id/date"
+ app:layout_constraintEnd_toStartOf="@id/carrier_group"
+ app:layout_constraintHorizontal_bias="0"
+ />
+ <Transform
+ android:scaleX="2.4"
+ android:scaleY="2.4"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/date">
+ <Layout
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_header_non_clickable_element_height"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/space"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/clock"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/carrier_group">
+ <Layout
+ app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height"
+ android:minHeight="@dimen/large_screen_shade_header_min_height"
+ app:layout_constraintWidth_min="48dp"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintStart_toEndOf="@id/clock"
+ app:layout_constraintTop_toBottomOf="@id/privacy_container"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintBottom_toTopOf="@id/batteryRemainingIcon"
+ />
+ <PropertySet
+ android:alpha="1"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/statusIcons">
+ <Layout
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_header_non_clickable_element_height"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintStart_toEndOf="@id/space"
+ app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
+ app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/batteryRemainingIcon">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/qs_header_non_clickable_element_height"
+ app:layout_constraintHeight_min="@dimen/qs_header_non_clickable_element_height"
+ app:layout_constraintStart_toEndOf="@id/statusIcons"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ />
+ </Constraint>
+
+
+ <Constraint
+ android:id="@id/space">
+ <Layout
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintStart_toEndOf="@id/date"
+ app:layout_constraintEnd_toStartOf="@id/statusIcons"
+ />
+ </Constraint>
+</ConstraintSet> \ No newline at end of file
diff --git a/packages/SystemUI/screenshot/Android.bp b/packages/SystemUI/screenshot/Android.bp
new file mode 100644
index 000000000000..f449398fc9f8
--- /dev/null
+++ b/packages/SystemUI/screenshot/Android.bp
@@ -0,0 +1,45 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+android_library {
+ name: "SystemUIScreenshotLib",
+ manifest: "AndroidManifest.xml",
+
+ srcs: [
+ "src/**/*.kt",
+ ],
+
+ resource_dirs: [
+ "res",
+ ],
+
+ static_libs: [
+ "SystemUI-core",
+ "androidx.test.espresso.core",
+ "androidx.appcompat_appcompat",
+ "platform-screenshot-diff-core",
+ "guava",
+ ],
+
+ kotlincflags: ["-Xjvm-default=all"],
+}
diff --git a/packages/SystemUI/screenshot/AndroidManifest.xml b/packages/SystemUI/screenshot/AndroidManifest.xml
new file mode 100644
index 000000000000..a405836bd77f
--- /dev/null
+++ b/packages/SystemUI/screenshot/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.systemui.testing.screenshot">
+ <application>
+ <activity
+ android:name="com.android.systemui.testing.screenshot.ScreenshotActivity"
+ android:exported="true"
+ android:theme="@style/Theme.SystemUI.Screenshot" />
+ </application>
+</manifest>
diff --git a/packages/SystemUI/screenshot/res/values/themes.xml b/packages/SystemUI/screenshot/res/values/themes.xml
new file mode 100644
index 000000000000..a7f8a264e892
--- /dev/null
+++ b/packages/SystemUI/screenshot/res/values/themes.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <style name="Theme.SystemUI.Screenshot" parent="Theme.SystemUI">
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowNoTitle">true</item>
+
+ <!-- We make the status and navigation bars transparent so that the screenshotted content is
+ not clipped by the status bar height when drawn into the Bitmap (which is what happens
+ given that we draw the view into the Bitmap using hardware acceleration). -->
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ <item name="android:navigationBarColor">@android:color/transparent</item>
+
+ <!-- Make sure that device specific cutouts don't impact the outcome of screenshot tests -->
+ <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/Bitmap.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/Bitmap.kt
new file mode 100644
index 000000000000..a4a70a49fce3
--- /dev/null
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/Bitmap.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.testing.screenshot
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.os.Build
+import android.view.View
+import platform.test.screenshot.matchers.MSSIMMatcher
+import platform.test.screenshot.matchers.PixelPerfectMatcher
+
+/** Draw this [View] into a [Bitmap]. */
+// TODO(b/195673633): Remove this once Compose screenshot tests use hardware rendering for their
+// tests.
+fun View.drawIntoBitmap(): Bitmap {
+ val bitmap =
+ Bitmap.createBitmap(
+ measuredWidth,
+ measuredHeight,
+ Bitmap.Config.ARGB_8888,
+ )
+ val canvas = Canvas(bitmap)
+ draw(canvas)
+ return bitmap
+}
+
+/**
+ * The [BitmapMatcher][platform.test.screenshot.matchers.BitmapMatcher] that should be used for
+ * screenshot *unit* tests.
+ */
+val UnitTestBitmapMatcher =
+ if (Build.CPU_ABI == "x86_64") {
+ // Different CPU architectures can sometimes end up rendering differently, so we can't do
+ // pixel-perfect matching on different architectures using the same golden. Given that our
+ // presubmits are run on cf_x86_64_phone, our goldens should be perfectly matched on the
+ // x86_64 architecture and use the Structural Similarity Index on others.
+ // TODO(b/237511747): Run our screenshot presubmit tests on arm64 instead so that we can
+ // do pixel perfect matching both at presubmit time and at development time with actual
+ // devices.
+ PixelPerfectMatcher()
+ } else {
+ MSSIMMatcher()
+ }
+
+/**
+ * The [BitmapMatcher][platform.test.screenshot.matchers.BitmapMatcher] that should be used for
+ * screenshot *unit* tests.
+ *
+ * We use the Structural Similarity Index for integration tests because they usually contain
+ * additional information and noise that shouldn't break the test.
+ */
+val IntegrationTestBitmapMatcher = MSSIMMatcher()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotActivity.kt
index d7010b174744..2a55a80eb7f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotActivity.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,17 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell;
+package com.android.systemui.testing.screenshot
-import com.android.wm.shell.common.annotations.ExternalThread;
+import androidx.activity.ComponentActivity
-/**
- * An entry point into the shell for initializing shell internal state.
- */
-@ExternalThread
-public interface ShellInit {
- /**
- * Initializes the shell state.
- */
- void init();
-}
+/** The Activity that is launched and whose content is set for screenshot tests. */
+class ScreenshotActivity : ComponentActivity()
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt
new file mode 100644
index 000000000000..cbab0a75061e
--- /dev/null
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.testing.screenshot
+
+import androidx.test.platform.app.InstrumentationRegistry
+import platform.test.screenshot.GoldenImagePathManager
+import platform.test.screenshot.PathConfig
+
+/** A [GoldenImagePathManager] that should be used for all SystemUI screenshot tests. */
+class SystemUIGoldenImagePathManager(
+ pathConfig: PathConfig,
+) :
+ GoldenImagePathManager(
+ appContext = InstrumentationRegistry.getInstrumentation().context,
+ deviceLocalPath =
+ InstrumentationRegistry.getInstrumentation()
+ .targetContext
+ .filesDir
+ .absolutePath
+ .toString() + "/sysui_screenshots",
+ pathConfig = pathConfig,
+ ) {
+ override fun toString(): String {
+ // This string is appended to all actual/expected screenshots on the device, so make sure
+ // it is a static value.
+ return "SystemUIGoldenImagePathManager"
+ }
+}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
new file mode 100644
index 000000000000..c609e6f8b4bf
--- /dev/null
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
@@ -0,0 +1,180 @@
+package com.android.systemui.testing.screenshot
+
+import android.app.Activity
+import android.content.Context
+import android.content.ContextWrapper
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Rect
+import android.os.Build
+import android.os.Handler
+import android.os.Looper
+import android.util.Log
+import android.view.PixelCopy
+import android.view.SurfaceView
+import android.view.View
+import android.view.ViewTreeObserver
+import android.view.Window
+import androidx.annotation.RequiresApi
+import androidx.concurrent.futures.ResolvableFuture
+import androidx.test.annotation.ExperimentalTestApi
+import androidx.test.core.internal.os.HandlerExecutor
+import androidx.test.platform.graphics.HardwareRendererCompat
+import com.google.common.util.concurrent.ListenableFuture
+
+/*
+ * This file was forked from androidx/test/core/view/ViewCapture.kt to add [Window] parameter to
+ * [View.captureToBitmap].
+ * TODO(b/195673633): Remove this fork and use the AndroidX version instead.
+ */
+
+/**
+ * Asynchronously captures an image of the underlying view into a [Bitmap].
+ *
+ * For devices below [Build.VERSION_CODES#O] (or if the view's window cannot be determined), the
+ * image is obtained using [View#draw]. Otherwise, [PixelCopy] is used.
+ *
+ * This method will also enable [HardwareRendererCompat#setDrawingEnabled(boolean)] if required.
+ *
+ * This API is primarily intended for use in lower layer libraries or frameworks. For test authors,
+ * its recommended to use espresso or compose's captureToImage.
+ *
+ * This API is currently experimental and subject to change or removal.
+ */
+@ExperimentalTestApi
+@RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
+fun View.captureToBitmap(window: Window? = null): ListenableFuture<Bitmap> {
+ val bitmapFuture: ResolvableFuture<Bitmap> = ResolvableFuture.create()
+ val mainExecutor = HandlerExecutor(Handler(Looper.getMainLooper()))
+
+ // disable drawing again if necessary once work is complete
+ if (!HardwareRendererCompat.isDrawingEnabled()) {
+ HardwareRendererCompat.setDrawingEnabled(true)
+ bitmapFuture.addListener({ HardwareRendererCompat.setDrawingEnabled(false) }, mainExecutor)
+ }
+
+ mainExecutor.execute {
+ val forceRedrawFuture = forceRedraw()
+ forceRedrawFuture.addListener({ generateBitmap(bitmapFuture, window) }, mainExecutor)
+ }
+
+ return bitmapFuture
+}
+
+/**
+ * Trigger a redraw of the given view.
+ *
+ * Should only be called on UI thread.
+ *
+ * @return a [ListenableFuture] that will be complete once ui drawing is complete
+ */
+// NoClassDefFoundError occurs on API 15
+@RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
+// @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@ExperimentalTestApi
+fun View.forceRedraw(): ListenableFuture<Void> {
+ val future: ResolvableFuture<Void> = ResolvableFuture.create()
+
+ if (Build.VERSION.SDK_INT >= 29 && isHardwareAccelerated) {
+ viewTreeObserver.registerFrameCommitCallback() { future.set(null) }
+ } else {
+ viewTreeObserver.addOnDrawListener(
+ object : ViewTreeObserver.OnDrawListener {
+ var handled = false
+ override fun onDraw() {
+ if (!handled) {
+ handled = true
+ future.set(null)
+ // cannot remove on draw listener inside of onDraw
+ Handler(Looper.getMainLooper()).post {
+ viewTreeObserver.removeOnDrawListener(this)
+ }
+ }
+ }
+ }
+ )
+ }
+ invalidate()
+ return future
+}
+
+private fun View.generateBitmap(
+ bitmapFuture: ResolvableFuture<Bitmap>,
+ window: Window? = null,
+) {
+ if (bitmapFuture.isCancelled) {
+ return
+ }
+ val destBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+ when {
+ Build.VERSION.SDK_INT < 26 -> generateBitmapFromDraw(destBitmap, bitmapFuture)
+ this is SurfaceView -> generateBitmapFromSurfaceViewPixelCopy(destBitmap, bitmapFuture)
+ else -> {
+ val window = window ?: getActivity()?.window
+ if (window != null) {
+ generateBitmapFromPixelCopy(window, destBitmap, bitmapFuture)
+ } else {
+ Log.i(
+ "View.captureToImage",
+ "Could not find window for view. Falling back to View#draw instead of PixelCopy"
+ )
+ generateBitmapFromDraw(destBitmap, bitmapFuture)
+ }
+ }
+ }
+}
+
+@SuppressWarnings("NewApi")
+private fun SurfaceView.generateBitmapFromSurfaceViewPixelCopy(
+ destBitmap: Bitmap,
+ bitmapFuture: ResolvableFuture<Bitmap>
+) {
+ val onCopyFinished =
+ PixelCopy.OnPixelCopyFinishedListener { result ->
+ if (result == PixelCopy.SUCCESS) {
+ bitmapFuture.set(destBitmap)
+ } else {
+ bitmapFuture.setException(
+ RuntimeException(String.format("PixelCopy failed: %d", result))
+ )
+ }
+ }
+ PixelCopy.request(this, null, destBitmap, onCopyFinished, handler)
+}
+
+internal fun View.generateBitmapFromDraw(
+ destBitmap: Bitmap,
+ bitmapFuture: ResolvableFuture<Bitmap>
+) {
+ destBitmap.density = resources.displayMetrics.densityDpi
+ computeScroll()
+ val canvas = Canvas(destBitmap)
+ canvas.translate((-scrollX).toFloat(), (-scrollY).toFloat())
+ draw(canvas)
+ bitmapFuture.set(destBitmap)
+}
+
+private fun View.getActivity(): Activity? {
+ fun Context.getActivity(): Activity? {
+ return when (this) {
+ is Activity -> this
+ is ContextWrapper -> this.baseContext.getActivity()
+ else -> null
+ }
+ }
+ return context.getActivity()
+}
+
+private fun View.generateBitmapFromPixelCopy(
+ window: Window,
+ destBitmap: Bitmap,
+ bitmapFuture: ResolvableFuture<Bitmap>
+) {
+ val locationInWindow = intArrayOf(0, 0)
+ getLocationInWindow(locationInWindow)
+ val x = locationInWindow[0]
+ val y = locationInWindow[1]
+ val boundsInWindow = Rect(x, y, x + width, y + height)
+
+ return window.generateBitmapFromPixelCopy(boundsInWindow, destBitmap, bitmapFuture)
+}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
new file mode 100644
index 000000000000..60130e1086ef
--- /dev/null
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.testing.screenshot
+
+import android.app.Activity
+import android.app.Dialog
+import android.graphics.Bitmap
+import android.graphics.HardwareRenderer
+import android.os.Looper
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.view.Window
+import androidx.activity.ComponentActivity
+import androidx.test.espresso.Espresso
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import com.google.common.util.concurrent.FutureCallback
+import com.google.common.util.concurrent.Futures
+import kotlin.coroutines.suspendCoroutine
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertEquals
+import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import platform.test.screenshot.DeviceEmulationRule
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.MaterialYouColorsRule
+import platform.test.screenshot.ScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+/** A rule for View screenshot diff unit tests. */
+class ViewScreenshotTestRule(emulationSpec: DeviceEmulationSpec) : TestRule {
+ private val colorsRule = MaterialYouColorsRule()
+ private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
+ private val screenshotRule =
+ ScreenshotTestRule(
+ SystemUIGoldenImagePathManager(getEmulatedDevicePathConfig(emulationSpec))
+ )
+ private val activityRule = ActivityScenarioRule(ScreenshotActivity::class.java)
+ private val delegateRule =
+ RuleChain.outerRule(colorsRule)
+ .around(deviceEmulationRule)
+ .around(screenshotRule)
+ .around(activityRule)
+ private val matcher = UnitTestBitmapMatcher
+
+ override fun apply(base: Statement, description: Description): Statement {
+ return delegateRule.apply(base, description)
+ }
+
+ /**
+ * Compare the content of the view provided by [viewProvider] with the golden image identified
+ * by [goldenIdentifier] in the context of [emulationSpec].
+ */
+ fun screenshotTest(
+ goldenIdentifier: String,
+ mode: Mode = Mode.WrapContent,
+ viewProvider: (ComponentActivity) -> View,
+ ) {
+ activityRule.scenario.onActivity { activity ->
+ // Make sure that the activity draws full screen and fits the whole display instead of
+ // the system bars.
+ val window = activity.window
+ window.setDecorFitsSystemWindows(false)
+
+ // Set the content.
+ activity.setContentView(viewProvider(activity), mode.layoutParams)
+
+ // Elevation/shadows is not deterministic when doing hardware rendering, so we disable
+ // it for any view in the hierarchy.
+ window.decorView.removeElevationRecursively()
+ }
+
+ // We call onActivity again because it will make sure that our Activity is done measuring,
+ // laying out and drawing its content (that we set in the previous onActivity lambda).
+ var contentView: View? = null
+ activityRule.scenario.onActivity { activity ->
+ // Check that the content is what we expected.
+ val content = activity.requireViewById<ViewGroup>(android.R.id.content)
+ assertEquals(1, content.childCount)
+ contentView = content.getChildAt(0)
+ }
+
+ val bitmap = contentView?.toBitmap() ?: error("contentView is null")
+ screenshotRule.assertBitmapAgainstGolden(
+ bitmap,
+ goldenIdentifier,
+ matcher,
+ )
+ }
+
+ /**
+ * Compare the content of the dialog provided by [dialogProvider] with the golden image
+ * identified by [goldenIdentifier] in the context of [emulationSpec].
+ */
+ fun dialogScreenshotTest(
+ goldenIdentifier: String,
+ dialogProvider: (Activity) -> Dialog,
+ ) {
+ var dialog: Dialog? = null
+ activityRule.scenario.onActivity { activity ->
+ dialog =
+ dialogProvider(activity).apply {
+ // Make sure that the dialog draws full screen and fits the whole display
+ // instead of the system bars.
+ window.setDecorFitsSystemWindows(false)
+
+ // Disable enter/exit animations.
+ create()
+ window.setWindowAnimations(0)
+
+ // Elevation/shadows is not deterministic when doing hardware rendering, so we
+ // disable it for any view in the hierarchy.
+ window.decorView.removeElevationRecursively()
+
+ // Show the dialog.
+ show()
+ }
+ }
+
+ try {
+ val bitmap = dialog?.toBitmap() ?: error("dialog is null")
+ screenshotRule.assertBitmapAgainstGolden(
+ bitmap,
+ goldenIdentifier,
+ matcher,
+ )
+ } finally {
+ dialog?.dismiss()
+ }
+ }
+
+ private fun View.removeElevationRecursively() {
+ this.elevation = 0f
+
+ if (this is ViewGroup) {
+ repeat(childCount) { i -> getChildAt(i).removeElevationRecursively() }
+ }
+ }
+
+ private fun Dialog.toBitmap(): Bitmap {
+ val window = window
+ return window.decorView.toBitmap(window)
+ }
+
+ private fun View.toBitmap(window: Window? = null): Bitmap {
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ error("toBitmap() can't be called from the main thread")
+ }
+
+ if (!HardwareRenderer.isDrawingEnabled()) {
+ error("Hardware rendering is not enabled")
+ }
+
+ // Make sure we are idle.
+ Espresso.onIdle()
+
+ val mainExecutor = context.mainExecutor
+ return runBlocking {
+ suspendCoroutine { continuation ->
+ Futures.addCallback(
+ captureToBitmap(window),
+ object : FutureCallback<Bitmap> {
+ override fun onSuccess(result: Bitmap?) {
+ continuation.resumeWith(Result.success(result!!))
+ }
+
+ override fun onFailure(t: Throwable) {
+ continuation.resumeWith(Result.failure(t))
+ }
+ },
+ // We know that we are not on the main thread, so we can block the current
+ // thread and wait for the result in the main thread.
+ mainExecutor,
+ )
+ }
+ }
+ }
+
+ enum class Mode(val layoutParams: LayoutParams) {
+ WrapContent(LayoutParams(WRAP_CONTENT, WRAP_CONTENT)),
+ MatchSize(LayoutParams(MATCH_PARENT, MATCH_PARENT)),
+ MatchWidth(LayoutParams(MATCH_PARENT, WRAP_CONTENT)),
+ MatchHeight(LayoutParams(WRAP_CONTENT, MATCH_PARENT)),
+ }
+}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/WindowCapture.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/WindowCapture.kt
new file mode 100644
index 000000000000..d34f46bf48a6
--- /dev/null
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/WindowCapture.kt
@@ -0,0 +1,37 @@
+package com.android.systemui.testing.screenshot
+
+import android.graphics.Bitmap
+import android.graphics.Rect
+import android.os.Handler
+import android.os.Looper
+import android.view.PixelCopy
+import android.view.Window
+import androidx.concurrent.futures.ResolvableFuture
+
+/*
+ * This file was forked from androidx/test/core/view/WindowCapture.kt.
+ * TODO(b/195673633): Remove this fork and use the AndroidX version instead.
+ */
+fun Window.generateBitmapFromPixelCopy(
+ boundsInWindow: Rect? = null,
+ destBitmap: Bitmap,
+ bitmapFuture: ResolvableFuture<Bitmap>
+) {
+ val onCopyFinished =
+ PixelCopy.OnPixelCopyFinishedListener { result ->
+ if (result == PixelCopy.SUCCESS) {
+ bitmapFuture.set(destBitmap)
+ } else {
+ bitmapFuture.setException(
+ RuntimeException(String.format("PixelCopy failed: %d", result))
+ )
+ }
+ }
+ PixelCopy.request(
+ this,
+ boundsInWindow,
+ destBitmap,
+ onCopyFinished,
+ Handler(Looper.getMainLooper())
+ )
+}
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 3cf5bc1bf13a..114ea657a758 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -47,8 +47,10 @@ android_library {
],
static_libs: [
"PluginCoreLib",
+ "SystemUIUnfoldLib",
"androidx.dynamicanimation_dynamicanimation",
"androidx.concurrent_concurrent-futures",
+ "gson-prebuilt-jar",
"dagger2",
"jsr330",
],
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
index 19a53091b35c..a8a526a33229 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -29,6 +29,7 @@ interface Flag<T> {
interface ParcelableFlag<T> : Flag<T>, Parcelable {
val default: T
+ val overridden: Boolean
override fun describeContents() = 0
}
@@ -36,6 +37,12 @@ interface ResourceFlag<T> : Flag<T> {
val resourceId: Int
}
+interface DeviceConfigFlag<T> : Flag<T> {
+ val name: String
+ val namespace: String
+ val default: T
+}
+
interface SysPropFlag<T> : Flag<T> {
val name: String
val default: T
@@ -46,7 +53,8 @@ interface SysPropFlag<T> : Flag<T> {
data class BooleanFlag @JvmOverloads constructor(
override val id: Int,
override val default: Boolean = false,
- override val teamfood: Boolean = false
+ override val teamfood: Boolean = false,
+ override val overridden: Boolean = false
) : ParcelableFlag<Boolean> {
companion object {
@@ -59,12 +67,16 @@ data class BooleanFlag @JvmOverloads constructor(
private constructor(parcel: Parcel) : this(
id = parcel.readInt(),
- default = parcel.readBoolean()
+ default = parcel.readBoolean(),
+ teamfood = parcel.readBoolean(),
+ overridden = parcel.readBoolean()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(id)
parcel.writeBoolean(default)
+ parcel.writeBoolean(teamfood)
+ parcel.writeBoolean(overridden)
}
}
@@ -74,6 +86,14 @@ data class ResourceBooleanFlag @JvmOverloads constructor(
override val teamfood: Boolean = false
) : ResourceFlag<Boolean>
+data class DeviceConfigBooleanFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val name: String,
+ override val namespace: String,
+ override val default: Boolean = false,
+ override val teamfood: Boolean = false
+) : DeviceConfigFlag<Boolean>
+
data class SysPropBooleanFlag @JvmOverloads constructor(
override val id: Int,
override val name: String,
@@ -86,7 +106,8 @@ data class SysPropBooleanFlag @JvmOverloads constructor(
data class StringFlag @JvmOverloads constructor(
override val id: Int,
override val default: String = "",
- override val teamfood: Boolean = false
+ override val teamfood: Boolean = false,
+ override val overridden: Boolean = false
) : ParcelableFlag<String> {
companion object {
@JvmField
@@ -116,7 +137,8 @@ data class ResourceStringFlag @JvmOverloads constructor(
data class IntFlag @JvmOverloads constructor(
override val id: Int,
override val default: Int = 0,
- override val teamfood: Boolean = false
+ override val teamfood: Boolean = false,
+ override val overridden: Boolean = false
) : ParcelableFlag<Int> {
companion object {
@@ -147,7 +169,8 @@ data class ResourceIntFlag @JvmOverloads constructor(
data class LongFlag @JvmOverloads constructor(
override val id: Int,
override val default: Long = 0,
- override val teamfood: Boolean = false
+ override val teamfood: Boolean = false,
+ override val overridden: Boolean = false
) : ParcelableFlag<Long> {
companion object {
@@ -172,7 +195,8 @@ data class LongFlag @JvmOverloads constructor(
data class FloatFlag @JvmOverloads constructor(
override val id: Int,
override val default: Float = 0f,
- override val teamfood: Boolean = false
+ override val teamfood: Boolean = false,
+ override val overridden: Boolean = false
) : ParcelableFlag<Float> {
companion object {
@@ -203,7 +227,8 @@ data class ResourceFloatFlag @JvmOverloads constructor(
data class DoubleFlag @JvmOverloads constructor(
override val id: Int,
override val default: Double = 0.0,
- override val teamfood: Boolean = false
+ override val teamfood: Boolean = false,
+ override val overridden: Boolean = false
) : ParcelableFlag<Double> {
companion object {
@@ -223,4 +248,4 @@ data class DoubleFlag @JvmOverloads constructor(
parcel.writeInt(id)
parcel.writeDouble(default)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
index 26e40e1ecad3..d172690e2488 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
@@ -64,20 +64,29 @@ class FlagManager constructor(
intent.setPackage(RECEIVING_PACKAGE)
return CallbackToFutureAdapter.getFuture {
- completer: CallbackToFutureAdapter.Completer<Collection<Flag<*>>> ->
- context.sendOrderedBroadcast(intent, null,
- object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- val extras: Bundle? = getResultExtras(false)
- val listOfFlags: java.util.ArrayList<ParcelableFlag<*>>? =
- extras?.getParcelableArrayList(EXTRA_FLAGS)
- if (listOfFlags != null) {
- completer.set(listOfFlags)
- } else {
- completer.setException(NoFlagResultsException())
- }
+ completer: CallbackToFutureAdapter.Completer<Collection<Flag<*>>> ->
+ context.sendOrderedBroadcast(
+ intent,
+ null,
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val extras: Bundle? = getResultExtras(false)
+ val listOfFlags: java.util.ArrayList<ParcelableFlag<*>>? =
+ extras?.getParcelableArrayList(
+ EXTRA_FLAGS, ParcelableFlag::class.java
+ )
+ if (listOfFlags != null) {
+ completer.set(listOfFlags)
+ } else {
+ completer.setException(NoFlagResultsException())
}
- }, null, Activity.RESULT_OK, "extra data", null)
+ }
+ },
+ null,
+ Activity.RESULT_OK,
+ "extra data",
+ null
+ )
"QueryingFlags"
}
}
@@ -152,7 +161,11 @@ class FlagManager constructor(
}
val parts = uri.pathSegments
val idStr = parts[parts.size - 1]
- val id = try { idStr.toInt() } catch (e: NumberFormatException) { return }
+ val id = try {
+ idStr.toInt()
+ } catch (e: NumberFormatException) {
+ return
+ }
clearCacheAction?.accept(id)
dispatchListenersAndMaybeRestart(id, onSettingsChangedAction)
}
@@ -188,4 +201,5 @@ class FlagManager constructor(
}
class NoFlagResultsException : Exception(
- "SystemUI failed to communicate its flags back successfully") \ No newline at end of file
+ "SystemUI failed to communicate its flags back successfully"
+)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockProviderPlugin.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockProviderPlugin.kt
new file mode 100644
index 000000000000..916a557d7af8
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockProviderPlugin.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.shared.clocks
+
+import com.android.systemui.plugins.Plugin
+import com.android.systemui.plugins.annotations.ProvidesInterface
+import android.annotation.FloatRange
+import android.graphics.drawable.Drawable
+import android.view.View
+
+/** Identifies a clock design */
+typealias ClockId = String
+
+/** A Plugin which exposes the ClockProvider interface */
+@ProvidesInterface(action = ClockProviderPlugin.ACTION, version = ClockProviderPlugin.VERSION)
+interface ClockProviderPlugin : Plugin, ClockProvider {
+ companion object {
+ const val ACTION = "com.android.systemui.action.PLUGIN_CLOCK_PROVIDER"
+ const val VERSION = 1
+ }
+}
+
+/** Interface for building clocks and providing information about those clocks */
+interface ClockProvider {
+ /** Returns metadata for all clocks this provider knows about */
+ fun getClocks(): List<ClockMetadata>
+
+ /** Initializes and returns the target clock design */
+ fun createClock(id: ClockId): Clock
+
+ /** A static thumbnail for rendering in some examples */
+ fun getClockThumbnail(id: ClockId): Drawable?
+}
+
+/** Interface for controlling an active clock */
+interface Clock {
+ /** A small version of the clock, appropriate for smaller viewports */
+ val smallClock: View
+
+ /** A large version of the clock, appropriate when a bigger viewport is available */
+ val largeClock: View
+
+ /** Callback to update the clock view to the current time */
+ fun onTimeTick()
+
+ /** Sets the level of the AOD transition */
+ fun setAodFraction(@FloatRange(from = 0.0, to = 1.0) fraction: Float)
+}
+
+/** Some data about a clock design */
+data class ClockMetadata(
+ val clockId: ClockId,
+ val name: String
+) \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
new file mode 100644
index 000000000000..32459665e6cf
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.shared.clocks
+
+import android.content.Context
+import android.database.ContentObserver
+import android.graphics.drawable.Drawable
+import android.net.Uri
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.util.Log
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.PluginListener
+import com.android.systemui.shared.plugins.PluginManager
+import com.google.gson.Gson
+import javax.inject.Inject
+
+private val TAG = ClockRegistry::class.simpleName
+private val DEBUG = true
+const val DEFAULT_CLOCK_ID = "DEFAULT"
+
+typealias ClockChangeListener = () -> Unit
+
+/** ClockRegistry aggregates providers and plugins */
+open class ClockRegistry @Inject constructor(
+ val context: Context,
+ val pluginManager: PluginManager,
+ @Main val handler: Handler
+) {
+ private val gson = Gson()
+ private val availableClocks = mutableMapOf<ClockId, ClockInfo>()
+ private val clockChangeListeners = mutableListOf<ClockChangeListener>()
+ private val settingObserver = object : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean, uris: Collection<Uri>, flags: Int, userId: Int) =
+ clockChangeListeners.forEach { it() }
+ }
+
+ private val pluginListener = object : PluginListener<ClockProviderPlugin> {
+ override fun onPluginConnected(plugin: ClockProviderPlugin, context: Context) {
+ val currentId = currentClockId
+ for (clock in plugin.getClocks()) {
+ val id = clock.clockId
+ val current = availableClocks[id]
+ if (current != null) {
+ Log.e(TAG, "Clock Id conflict: $id is registered by both " +
+ "${plugin::class.simpleName} and ${current.provider::class.simpleName}")
+ return
+ }
+
+ availableClocks[id] = ClockInfo(clock, plugin)
+
+ if (currentId == id) {
+ if (DEBUG) {
+ Log.i(TAG, "Current clock ($currentId) was connected")
+ }
+ clockChangeListeners.forEach { it() }
+ }
+ }
+ }
+
+ override fun onPluginDisconnected(plugin: ClockProviderPlugin) {
+ val currentId = currentClockId
+ for (clock in plugin.getClocks()) {
+ availableClocks.remove(clock.clockId)
+
+ if (currentId == clock.clockId) {
+ Log.w(TAG, "Current clock ($currentId) was disconnected")
+ clockChangeListeners.forEach { it() }
+ }
+ }
+ }
+ }
+
+ open var currentClockId: ClockId
+ get() {
+ val json = Settings.Secure.getString(context.contentResolver,
+ Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE)
+ return gson.fromJson(json, ClockSetting::class.java).clockId
+ }
+ set(value) {
+ val json = gson.toJson(ClockSetting(value, System.currentTimeMillis()))
+ Settings.Secure.putString(context.contentResolver,
+ Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, json)
+ }
+
+ init {
+ pluginManager.addPluginListener(pluginListener, ClockProviderPlugin::class.java)
+ context.contentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
+ false,
+ settingObserver,
+ UserHandle.USER_ALL)
+ }
+
+ fun getClocks(): List<ClockMetadata> = availableClocks.map { (_, clock) -> clock.metadata }
+
+ fun getClockThumbnail(clockId: ClockId): Drawable? =
+ availableClocks[clockId]?.provider?.getClockThumbnail(clockId)
+
+ fun createExampleClock(clockId: ClockId): Clock? = createClock(clockId)
+
+ fun registerClockChangeListener(listener: ClockChangeListener) =
+ clockChangeListeners.add(listener)
+
+ fun unregisterClockChangeListener(listener: ClockChangeListener) =
+ clockChangeListeners.remove(listener)
+
+ fun createCurrentClock(): Clock {
+ val clockId = currentClockId
+ if (!clockId.isNullOrEmpty()) {
+ val clock = createClock(clockId)
+ if (clock != null) {
+ return clock
+ } else {
+ Log.e(TAG, "Clock $clockId not found; using default")
+ }
+ }
+
+ return createClock(DEFAULT_CLOCK_ID)!!
+ }
+
+ private fun createClock(clockId: ClockId): Clock? =
+ availableClocks[clockId]?.provider?.createClock(clockId)
+
+ private data class ClockInfo(
+ val metadata: ClockMetadata,
+ val provider: ClockProvider
+ )
+
+ private data class ClockSetting(
+ val clockId: ClockId,
+ val _applied_timestamp: Long
+ )
+} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 88fe03465405..203b236fcdd6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -80,7 +80,8 @@ public class PipSurfaceTransactionHelper {
public PictureInPictureSurfaceTransaction scaleAndCrop(
SurfaceControl.Transaction tx, SurfaceControl leash,
- Rect sourceRectHint, Rect sourceBounds, Rect destinationBounds, Rect insets) {
+ Rect sourceRectHint, Rect sourceBounds, Rect destinationBounds, Rect insets,
+ float progress) {
mTmpSourceRectF.set(sourceBounds);
mTmpDestinationRect.set(sourceBounds);
mTmpDestinationRect.inset(insets);
@@ -93,9 +94,13 @@ public class PipSurfaceTransactionHelper {
: (float) destinationBounds.height() / sourceBounds.height();
} else {
// scale by sourceRectHint if it's not edge-to-edge
- scale = sourceRectHint.width() <= sourceRectHint.height()
+ final float endScale = sourceRectHint.width() <= sourceRectHint.height()
? (float) destinationBounds.width() / sourceRectHint.width()
: (float) destinationBounds.height() / sourceRectHint.height();
+ final float startScale = sourceRectHint.width() <= sourceRectHint.height()
+ ? (float) destinationBounds.width() / sourceBounds.width()
+ : (float) destinationBounds.height() / sourceBounds.height();
+ scale = (1 - progress) * startScale + progress * endScale;
}
final float left = destinationBounds.left - (insets.left + sourceBounds.left) * scale;
final float top = destinationBounds.top - (insets.top + sourceBounds.top) * scale;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index e743c4e5a83d..a030bf629507 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -42,12 +42,6 @@ interface ISystemUiProxy {
void onOverviewShown(boolean fromHome) = 6;
/**
- * Get the secondary split screen app's rectangle when not minimized.
- * @deprecated
- */
- Rect getNonMinimizedSplitScreenSecondaryBounds() = 7;
-
- /**
* Control the {@param alpha} of the option nav bar button (back-button in 2 button mode
* and home handle & background in gestural mode). The {@param animate} is currently only
* supported for 2 button mode.
@@ -94,21 +88,6 @@ interface ISystemUiProxy {
*/
void stopScreenPinning() = 17;
- /**
- * Handle the provided image as if it was a screenshot.
- *
- * Deprecated, use handleImageBundleAsScreenshot with image bundle and UserTask
- * @deprecated
- */
- void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen,
- in Insets visibleInsets, int taskId) = 21;
-
- /**
- * Sets the split-screen divider minimized state
- * @deprecated
- */
- void setSplitScreenMinimized(boolean minimized) = 22;
-
/*
* Notifies that the swipe-to-home (recents animation) is finished.
*/
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 675dc9b533fb..e3f568732b9d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -18,6 +18,9 @@ package com.android.systemui.shared.recents.model;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
+import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
+
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
import android.app.TaskInfo;
@@ -31,6 +34,8 @@ import android.view.ViewDebug;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.util.ArrayUtils;
+
import java.io.PrintWriter;
import java.util.Objects;
@@ -242,8 +247,10 @@ public class Task {
ActivityManager.TaskDescription td = taskInfo.taskDescription;
return new Task(taskKey,
td != null ? td.getPrimaryColor() : 0,
- td != null ? td.getBackgroundColor() : 0,
- taskInfo.supportsSplitScreenMultiWindow, isLocked, td, taskInfo.topActivity);
+ td != null ? td.getBackgroundColor() : 0, taskInfo.supportsMultiWindow
+ && ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
+ && ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode()),
+ isLocked, td, taskInfo.topActivity);
}
public Task(TaskKey key) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 01f417f147d7..b29dc835a6f4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.FEATURE_PC;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
+import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -97,6 +98,7 @@ public class RotationButtonController {
@SuppressLint("InlinedApi")
private @WindowInsetsController.Behavior
int mBehavior = WindowInsetsController.BEHAVIOR_DEFAULT;
+ private int mNavBarMode;
private boolean mSkipOverrideUserLockPrefsOnce;
private final int mLightIconColor;
private final int mDarkIconColor;
@@ -397,6 +399,10 @@ public class RotationButtonController {
if (rotateSuggestionsDisabled) onRotationSuggestionsDisabled();
}
+ public void onNavigationModeChanged(int mode) {
+ mNavBarMode = mode;
+ }
+
public void onBehaviorChanged(int displayId, @WindowInsetsController.Behavior int behavior) {
if (DEFAULT_DISPLAY != displayId) {
return;
@@ -433,7 +439,8 @@ public class RotationButtonController {
*/
@SuppressLint("InlinedApi")
private boolean canShowRotationButton() {
- return mIsNavigationBarShowing || mBehavior == WindowInsetsController.BEHAVIOR_DEFAULT;
+ return mIsNavigationBarShowing || mBehavior == WindowInsetsController.BEHAVIOR_DEFAULT
+ || isGesturalMode(mNavBarMode);
}
@DrawableRes
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerKt.kt
index b87cf47dd93f..c1429335292f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerKt.kt
@@ -14,22 +14,19 @@
* limitations under the License.
*/
-package com.android.wm.shell.compatui;
+package com.android.systemui.shared.system
-import com.android.wm.shell.common.annotations.ExternalThread;
+import android.app.ActivityManager
+
+/** Kotlin extensions for [ActivityManager] */
+object ActivityManagerKt {
-/**
- * Interface to engage compat UI.
- */
-@ExternalThread
-public interface CompatUI {
/**
- * Called when the keyguard showing state changes. Removes all compat UIs if the
- * keyguard is now showing.
- *
- * <p>Note that if the keyguard is occluded it will also be considered showing.
- *
- * @param showing indicates if the keyguard is now showing.
+ * Returns `true` whether the app with the given package name has an activity at the top of the
+ * most recent task; `false` otherwise
*/
- void onKeyguardShowingChanged(boolean showing);
+ fun ActivityManager.isInForeground(packageName: String): Boolean {
+ val tasks: List<ActivityManager.RunningTaskInfo> = getRunningTasks(1)
+ return tasks.isNotEmpty() && packageName == tasks[0].topActivity.packageName
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index be3dfdcf05d5..916526d0efac 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -130,19 +130,14 @@ public class ActivityManagerWrapper {
}
/**
- * @return a list of the recents tasks.
- */
- public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
- return mAtm.getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId);
- }
-
- /**
- * @return the task snapshot for the given {@param taskId}.
+ * @return a {@link ThumbnailData} with {@link TaskSnapshot} for the given {@param taskId}.
+ * The snapshot will be triggered if no cached {@link TaskSnapshot} exists.
*/
public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean isLowResolution) {
TaskSnapshot snapshot = null;
try {
- snapshot = getService().getTaskSnapshot(taskId, isLowResolution);
+ snapshot = getService().getTaskSnapshot(taskId, isLowResolution,
+ true /* takeSnapshotIfNeeded */);
} catch (RemoteException e) {
Log.w(TAG, "Failed to retrieve task snapshot", e);
}
@@ -245,25 +240,6 @@ public class ActivityManagerWrapper {
}
/**
- * Starts a task from Recents.
- *
- * @param resultCallback The result success callback
- * @param resultCallbackHandler The handler to receive the result callback
- */
- public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options,
- Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
- final boolean result = startActivityFromRecents(taskKey, options);
- if (resultCallback != null) {
- resultCallbackHandler.post(new Runnable() {
- @Override
- public void run() {
- resultCallback.accept(result);
- }
- });
- }
- }
-
- /**
* Starts a task from Recents synchronously.
*/
public boolean startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options) {
@@ -284,20 +260,6 @@ public class ActivityManagerWrapper {
}
/**
- * @deprecated use {@link TaskStackChangeListeners#registerTaskStackListener}
- */
- public void registerTaskStackListener(TaskStackChangeListener listener) {
- TaskStackChangeListeners.getInstance().registerTaskStackListener(listener);
- }
-
- /**
- * @deprecated use {@link TaskStackChangeListeners#unregisterTaskStackListener}
- */
- public void unregisterTaskStackListener(TaskStackChangeListener listener) {
- TaskStackChangeListeners.getInstance().unregisterTaskStackListener(listener);
- }
-
- /**
* Requests that the system close any open system windows (including other SystemUI).
*/
public void closeSystemWindows(final String reason) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index add2d022e893..be99b270c09a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -28,14 +28,6 @@ import android.os.Handler;
public abstract class ActivityOptionsCompat {
/**
- * @Deprecated
- * @return ActivityOptions for starting a task in split screen as the primary window.
- */
- public static ActivityOptions makeSplitScreenOptions(boolean dockTopLeft) {
- return ActivityOptions.makeBasic();
- }
-
- /**
* @return ActivityOptions for starting a task in freeform.
*/
public static ActivityOptions makeFreeformOptions() {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 630fb360cc14..97e024238778 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -204,28 +204,6 @@ public class QuickStepContract {
}
/**
- * Touch slopes and thresholds for quick step operations. Drag slop is the point where the
- * home button press/long press over are ignored and will start to drag when exceeded and the
- * touch slop is when the respected operation will occur when exceeded. Touch slop must be
- * larger than the drag slop.
- */
- public static int getQuickStepDragSlopPx() {
- return convertDpToPixel(10);
- }
-
- public static int getQuickStepTouchSlopPx() {
- return convertDpToPixel(24);
- }
-
- public static int getQuickScrubTouchSlopPx() {
- return convertDpToPixel(24);
- }
-
- private static int convertDpToPixel(float dp) {
- return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
- }
-
- /**
* Returns whether the specified sysui state is such that the assistant gesture should be
* disabled.
*/
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 13f1db4a0831..0094820f0dad 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -65,14 +65,6 @@ public class RecentsAnimationControllerCompat {
}
}
- public void hideCurrentInputMethod() {
- try {
- mAnimationController.hideCurrentInputMethod();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to set hide input method", e);
- }
- }
-
/**
* Sets the final surface transaction on a Task. This is used by Launcher to notify the system
* that animating Activity to PiP has completed and the associated task surface should be
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index 06f5372e5cce..9265f07ad284 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -114,6 +114,8 @@ public class RemoteAnimationAdapterCompat {
private static IRemoteTransition.Stub wrapRemoteTransition(
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteTransition.Stub() {
+ final ArrayMap<IBinder, Runnable> mFinishRunnables = new ArrayMap<>();
+
@Override
public void startAnimation(IBinder token, TransitionInfo info,
SurfaceControl.Transaction t,
@@ -137,6 +139,8 @@ public class RemoteAnimationAdapterCompat {
float displayH = 0;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
+ // skip changes that we didn't wrap
+ if (!leashMap.containsKey(change.getLeash())) continue;
if (change.getTaskInfo() != null
&& change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) {
isReturnToHome = change.getMode() == TRANSIT_OPEN
@@ -173,6 +177,8 @@ public class RemoteAnimationAdapterCompat {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
final SurfaceControl leash = leashMap.get(change.getLeash());
+ // skip changes that we didn't wrap
+ if (leash == null) continue;
final int mode = info.getChanges().get(i).getMode();
// Only deal with independent layers
if (!TransitionInfo.isIndependent(change, info)) continue;
@@ -214,9 +220,9 @@ public class RemoteAnimationAdapterCompat {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
info.getChanges().get(i).getLeash().release();
}
- for (int i = leashMap.size() - 1; i >= 0; --i) {
- leashMap.valueAt(i).release();
- }
+ // Don't release here since launcher might still be using them. Instead
+ // let launcher release them (eg. via RemoteAnimationTargets)
+ leashMap.clear();
try {
finishCallback.onTransitionFinished(null /* wct */, finishTransaction);
} catch (RemoteException e) {
@@ -225,19 +231,32 @@ public class RemoteAnimationAdapterCompat {
}
}
};
+ synchronized (mFinishRunnables) {
+ mFinishRunnables.put(token, animationFinishedCallback);
+ }
// TODO(bc-unlcok): Pass correct transit type.
- remoteAnimationAdapter.onAnimationStart(
- TRANSIT_OLD_NONE,
- appsCompat, wallpapersCompat, nonAppsCompat,
- animationFinishedCallback);
+ remoteAnimationAdapter.onAnimationStart(TRANSIT_OLD_NONE,
+ appsCompat, wallpapersCompat, nonAppsCompat, () -> {
+ synchronized (mFinishRunnables) {
+ if (mFinishRunnables.remove(token) == null) return;
+ }
+ animationFinishedCallback.run();
+ });
}
@Override
public void mergeAnimation(IBinder token, TransitionInfo info,
SurfaceControl.Transaction t, IBinder mergeTarget,
IRemoteTransitionFinishedCallback finishCallback) {
- // TODO: hook up merge to recents onTaskAppeared if applicable. Until then, ignore
- // any incoming merges.
+ // TODO: hook up merge to recents onTaskAppeared if applicable. Until then, adapt
+ // to legacy cancel.
+ final Runnable finishRunnable;
+ synchronized (mFinishRunnables) {
+ finishRunnable = mFinishRunnables.remove(mergeTarget);
+ }
+ if (finishRunnable == null) return;
+ remoteAnimationAdapter.onAnimationCancelled();
+ finishRunnable.run();
}
};
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 9cf482fdf790..249133a9a63b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -17,7 +17,6 @@
package com.android.systemui.shared.system;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
-import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -32,7 +31,6 @@ import android.app.WindowConfiguration;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.ArrayMap;
-import android.util.IntArray;
import android.util.SparseArray;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -142,21 +140,12 @@ public class RemoteAnimationTargetCompat {
// changes should be ordered top-to-bottom in z
final int mode = change.getMode();
- // Don't move anything that isn't independent within its parents
- if (!TransitionInfo.isIndependent(change, info)) {
- if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) {
- t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y);
- }
- return;
- }
-
- final boolean hasParent = change.getParent() != null;
+ t.reparent(leash, info.getRootLeash());
+ final Rect absBounds =
+ (mode == TRANSIT_OPEN) ? change.getEndAbsBounds() : change.getStartAbsBounds();
+ t.setPosition(leash, absBounds.left - info.getRootOffset().x,
+ absBounds.top - info.getRootOffset().y);
- if (!hasParent) {
- t.reparent(leash, info.getRootLeash());
- t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x,
- change.getStartAbsBounds().top - info.getRootOffset().y);
- }
// Put all the OPEN/SHOW on top
if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
if (isOpening) {
@@ -196,8 +185,7 @@ public class RemoteAnimationTargetCompat {
.setContainerLayer()
// Initial the surface visible to respect the visibility of the original surface.
.setHidden(false)
- .setParent(change.getParent() == null ? info.getRootLeash()
- : info.getChange(change.getParent()).getLeash())
+ .setParent(info.getRootLeash())
.build();
// Copied Transitions setup code (which expects bottom-to-top order, so we swap here)
setupLeash(leashSurface, change, info.getChanges().size() - order, info, t);
@@ -269,58 +257,32 @@ public class RemoteAnimationTargetCompat {
public static RemoteAnimationTargetCompat[] wrap(TransitionInfo info, boolean wallpapers,
SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
final ArrayList<RemoteAnimationTargetCompat> out = new ArrayList<>();
- final SparseArray<RemoteAnimationTargetCompat> childTaskTargets = new SparseArray<>();
- final IntArray excludedParentTaskIds = new IntArray();
+ final SparseArray<TransitionInfo.Change> childTaskTargets = new SparseArray<>();
for (int i = 0; i < info.getChanges().size(); i++) {
final TransitionInfo.Change change = info.getChanges().get(i);
final boolean changeIsWallpaper =
(change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0;
if (wallpapers != changeIsWallpaper) continue;
- final RemoteAnimationTargetCompat targetCompat =
- new RemoteAnimationTargetCompat(change, info.getChanges().size() - i, info, t);
- if (leashMap != null) {
- leashMap.put(change.getLeash(), targetCompat.leash);
- }
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (taskInfo != null) {
- // Skip wrapping excluded parent task animate target since it will animate its child
- // tasks instead.
- if (excludedParentTaskIds.binarySearch(taskInfo.taskId) != -1) {
- continue;
- }
-
- // Check if there's a matching child task target in cache.
- RemoteAnimationTargetCompat childTaskTarget = childTaskTargets.get(taskInfo.taskId);
- if (childTaskTarget != null) {
- // Launcher monitors leaf task ids to perform animation, override the target
- // with its child task information so Launcher can animate this parent surface
- // directly with leaf task information.
- targetCompat.taskInfo = childTaskTarget.taskInfo;
- targetCompat.taskId = childTaskTarget.taskId;
- childTaskTargets.remove(taskInfo.taskId);
- }
-
- // Check if it has a parent task, cache its information for later use.
- if (taskInfo.parentTaskId != -1
- && excludedParentTaskIds.binarySearch(taskInfo.parentTaskId) == -1) {
- if (!childTaskTargets.contains(taskInfo.parentTaskId)) {
- // Cache the target amd skip wrapping it info the final animation targets.
- // Otherwise, the child task might get transformed multiple-times with the
- // flow like RecentsView#redrawLiveTile.
- childTaskTargets.put(taskInfo.parentTaskId, targetCompat);
+ if (!wallpapers) {
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ // Children always come before parent since changes are in top-to-bottom z-order.
+ if (taskInfo != null) {
+ if (childTaskTargets.contains(taskInfo.taskId)) {
+ // has children, so not a leaf. Skip.
continue;
}
-
- // There is another child task target cached with the same parent task id.
- // Which means the parent having multiple child tasks in transition. Stop
- // propagate child task info.
- childTaskTarget = childTaskTargets.removeReturnOld(taskInfo.parentTaskId);
- out.add(childTaskTarget);
- excludedParentTaskIds.add(taskInfo.parentTaskId);
+ if (taskInfo.hasParentTask()) {
+ childTaskTargets.put(taskInfo.parentTaskId, change);
+ }
}
}
+ final RemoteAnimationTargetCompat targetCompat =
+ new RemoteAnimationTargetCompat(change, info.getChanges().size() - i, info, t);
+ if (leashMap != null) {
+ leashMap.put(change.getLeash(), targetCompat.leash);
+ }
out.add(targetCompat);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index de35514be2cb..609846e8c729 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -21,12 +21,14 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionFilter.CONTAINER_ORDER_TOP;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -132,16 +134,17 @@ public class RemoteTransitionCompat implements Parcelable {
// TODO(b/177438007): Move this set-up logic into launcher's animation impl.
mToken = transition;
// This transition is for opening recents, so recents is on-top. We want to draw
- // the current going-away task on top of recents, though, so move it to front
+ // the current going-away tasks on top of recents, though, so move them to front.
+ // Note that we divide up the "layer space" into 3 regions each the size of
+ // the change count. This way we can easily move changes into above/below/between
+ // while maintaining their relative ordering.
final ArrayList<WindowContainerToken> pausingTasks = new ArrayList<>();
WindowContainerToken pipTask = null;
WindowContainerToken recentsTask = null;
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (change.getMode() == TRANSIT_CLOSE || change.getMode() == TRANSIT_TO_BACK) {
- t.setLayer(leashMap.get(change.getLeash()),
- info.getChanges().size() * 3 - i);
+ for (int i = apps.length - 1; i >= 0; --i) {
+ final ActivityManager.RunningTaskInfo taskInfo = apps[i].taskInfo;
+ if (apps[i].mode == MODE_CLOSING) {
+ t.setLayer(apps[i].leash, info.getChanges().size() * 3 - i);
if (taskInfo == null) {
continue;
}
@@ -154,8 +157,7 @@ public class RemoteTransitionCompat implements Parcelable {
} else if (taskInfo != null
&& taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) {
// This task is for recents, keep it on top.
- t.setLayer(leashMap.get(change.getLeash()),
- info.getChanges().size() * 3 - i);
+ t.setLayer(apps[i].leash, info.getChanges().size() * 3 - i);
recentsTask = taskInfo.token;
} else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
recentsTask = taskInfo.token;
@@ -167,7 +169,8 @@ public class RemoteTransitionCompat implements Parcelable {
}
t.apply();
mRecentsSession.setup(controller, info, finishedCallback, pausingTasks, pipTask,
- recentsTask, leashMap, mToken);
+ recentsTask, leashMap, mToken,
+ (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0);
recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0),
new Rect());
}
@@ -183,6 +186,8 @@ public class RemoteTransitionCompat implements Parcelable {
} catch (RemoteException e) {
Log.e(TAG, "Error merging transition.", e);
}
+ // commit taskAppeared after merge transition finished.
+ mRecentsSession.commitTasksAppearedIfNeeded(recents);
}
};
mTransition = new RemoteTransition(remote, appThread);
@@ -219,15 +224,18 @@ public class RemoteTransitionCompat implements Parcelable {
private WindowContainerToken mRecentsTask = null;
private TransitionInfo mInfo = null;
private ArrayList<SurfaceControl> mOpeningLeashes = null;
+ private boolean mOpeningHome = false;
private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
private PictureInPictureSurfaceTransaction mPipTransaction = null;
private IBinder mTransition = null;
+ private boolean mKeyguardLocked = false;
+ private RemoteAnimationTargetCompat[] mAppearedTargets;
void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info,
IRemoteTransitionFinishedCallback finishCB,
ArrayList<WindowContainerToken> pausingTasks, WindowContainerToken pipTask,
WindowContainerToken recentsTask, ArrayMap<SurfaceControl, SurfaceControl> leashMap,
- IBinder transition) {
+ IBinder transition, boolean keyguardLocked) {
if (mInfo != null) {
throw new IllegalStateException("Trying to run a new recents animation while"
+ " recents is already active.");
@@ -240,12 +248,14 @@ public class RemoteTransitionCompat implements Parcelable {
mRecentsTask = recentsTask;
mLeashMap = leashMap;
mTransition = transition;
+ mKeyguardLocked = keyguardLocked;
}
@SuppressLint("NewApi")
boolean merge(TransitionInfo info, SurfaceControl.Transaction t,
RecentsAnimationListener recents) {
SparseArray<TransitionInfo.Change> openingTasks = null;
+ mAppearedTargets = null;
boolean cancelRecents = false;
boolean homeGoingAway = false;
boolean hasChangingApp = false;
@@ -312,6 +322,7 @@ public class RemoteTransitionCompat implements Parcelable {
}
final int layer = mInfo.getChanges().size() * 3;
mOpeningLeashes = new ArrayList<>();
+ mOpeningHome = cancelRecents;
final RemoteAnimationTargetCompat[] targets =
new RemoteAnimationTargetCompat[openingTasks.size()];
for (int i = 0; i < openingTasks.size(); ++i) {
@@ -326,10 +337,17 @@ public class RemoteTransitionCompat implements Parcelable {
targets[i] = target;
}
t.apply();
- recents.onTasksAppeared(targets);
+ mAppearedTargets = targets;
return true;
}
+ private void commitTasksAppearedIfNeeded(RecentsAnimationListener recents) {
+ if (mAppearedTargets != null) {
+ recents.onTasksAppeared(mAppearedTargets);
+ mAppearedTargets = null;
+ }
+ }
+
@Override public ThumbnailData screenshotTask(int taskId) {
try {
final TaskSnapshot snapshot =
@@ -351,10 +369,6 @@ public class RemoteTransitionCompat implements Parcelable {
if (mWrapped != null) mWrapped.setAnimationTargetsBehindSystemBars(behindSystemBars);
}
- @Override public void hideCurrentInputMethod() {
- mWrapped.hideCurrentInputMethod();
- }
-
@Override public void setFinishTaskTransaction(int taskId,
PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) {
mPipTransaction = finishTransaction;
@@ -374,6 +388,10 @@ public class RemoteTransitionCompat implements Parcelable {
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (mKeyguardLocked && mRecentsTask != null) {
+ if (toHome) wct.reorder(mRecentsTask, true /* toTop */);
+ else wct.restoreTransientOrder(mRecentsTask);
+ }
if (!toHome && mPausingTasks != null && mOpeningLeashes == null) {
// The gesture went back to opening the app rather than continuing with
// recents, so end the transition by moving the app back to the top (and also
@@ -383,16 +401,41 @@ public class RemoteTransitionCompat implements Parcelable {
wct.reorder(mPausingTasks.get(i), true /* onTop */);
t.show(mInfo.getChange(mPausingTasks.get(i)).getLeash());
}
- if (mRecentsTask != null) {
+ if (!mKeyguardLocked && mRecentsTask != null) {
+ wct.restoreTransientOrder(mRecentsTask);
+ }
+ } else if (toHome && mOpeningHome && mPausingTasks != null) {
+ // Special situaition where 3p launcher was changed during recents (this happens
+ // during tapltests...). Here we get both "return to home" AND "home opening".
+ // This is basically going home, but we have to restore recents order and also
+ // treat the home "pausing" task properly.
+ for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = mInfo.getChange(mPausingTasks.get(i));
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
+ // Treat as opening (see above)
+ wct.reorder(mPausingTasks.get(i), true /* onTop */);
+ t.show(mInfo.getChange(mPausingTasks.get(i)).getLeash());
+ } else {
+ // Treat as hiding (see below)
+ t.hide(mInfo.getChange(mPausingTasks.get(i)).getLeash());
+ }
+ }
+ if (!mKeyguardLocked && mRecentsTask != null) {
wct.restoreTransientOrder(mRecentsTask);
}
} else {
- if (!sendUserLeaveHint) {
- for (int i = 0; i < mPausingTasks.size(); ++i) {
+ for (int i = 0; i < mPausingTasks.size(); ++i) {
+ if (!sendUserLeaveHint) {
// This means recents is not *actually* finishing, so of course we gotta
// do special stuff in WMCore to accommodate.
wct.setDoNotPip(mPausingTasks.get(i));
}
+ // Since we will reparent out of the leashes, pre-emptively hide the child
+ // surface to match the leash. Otherwise, there will be a flicker before the
+ // visibility gets committed in Core when using split-screen (in splitscreen,
+ // the leaf-tasks are not "independent" so aren't hidden by normal setup).
+ t.hide(mInfo.getChange(mPausingTasks.get(i)).getLeash());
}
if (mPipTask != null && mPipTransaction != null && sendUserLeaveHint) {
t.show(mInfo.getChange(mPipTask).getLeash());
@@ -402,18 +445,14 @@ public class RemoteTransitionCompat implements Parcelable {
mPipTransaction = null;
}
}
- // Release surface references now. This is apparently to free GPU
- // memory while doing quick operations (eg. during CTS).
- for (int i = 0; i < mLeashMap.size(); ++i) {
- if (mLeashMap.keyAt(i) == mLeashMap.valueAt(i)) continue;
- t.remove(mLeashMap.valueAt(i));
- }
try {
mFinishCB.onTransitionFinished(wct.isEmpty() ? null : wct, t);
} catch (RemoteException e) {
Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e);
t.apply();
}
+ // Only release the non-local created surface references. The animator is responsible
+ // for releasing the leashes created by local.
for (int i = 0; i < mInfo.getChanges().size(); ++i) {
mInfo.getChanges().get(i).getLeash().release();
}
@@ -423,6 +462,7 @@ public class RemoteTransitionCompat implements Parcelable {
mPausingTasks = null;
mInfo = null;
mOpeningLeashes = null;
+ mOpeningHome = false;
mLeashMap = null;
mTransition = null;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java
deleted file mode 100644
index 359d36939b31..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.shared.system;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.SurfaceView;
-
-/** Utility class that is shared between SysUI and Launcher for Universal Smartspace features. */
-public final class UniversalSmartspaceUtils {
- public static final String ACTION_REQUEST_SMARTSPACE_VIEW =
- "com.android.systemui.REQUEST_SMARTSPACE_VIEW";
- public static final String INTENT_BUNDLE_KEY = "bundle_key";
-
- private static final String SYSUI_PACKAGE = "com.android.systemui";
-
- /** Creates an intent to request that sysui draws the Smartspace to the SurfaceView. */
- public static Intent createRequestSmartspaceIntent(SurfaceView surfaceView) {
- Intent intent = new Intent(ACTION_REQUEST_SMARTSPACE_VIEW);
-
- Bundle bundle = SurfaceViewRequestUtils.createSurfaceBundle(surfaceView);
- return intent
- .putExtra(INTENT_BUNDLE_KEY, bundle)
- .setPackage(SYSUI_PACKAGE)
- .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- }
-
- private UniversalSmartspaceUtils() {}
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 5bd81a42a814..5577513f4c3c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -28,6 +28,7 @@ import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.view.InsetsController;
+import android.view.InsetsFrameProvider;
import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -105,30 +106,10 @@ public class WindowManagerWrapper {
*/
public void setProvidesInsetsTypes(WindowManager.LayoutParams params,
int[] providesInsetsTypes) {
- params.providesInsetsTypes = providesInsetsTypes;
- }
-
- /**
- * Sets if app requested fixed orientation should be ignored for given displayId.
- */
- public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) {
- try {
- WindowManagerGlobal.getWindowManagerService().setIgnoreOrientationRequest(
- displayId, ignoreOrientationRequest);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to setIgnoreOrientationRequest()", e);
- }
- }
-
- /**
- * @return the stable insets for the primary display.
- */
- public void getStableInsets(Rect outStableInsets) {
- try {
- WindowManagerGlobal.getWindowManagerService().getStableInsets(DEFAULT_DISPLAY,
- outStableInsets);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get stable insets", e);
+ final int length = providesInsetsTypes.length;
+ params.providedInsets = new InsetsFrameProvider[length];
+ for (int i = 0; i < length; i++) {
+ params.providedInsets[i] = new InsetsFrameProvider(providesInsetsTypes[i]);
}
}
@@ -148,16 +129,6 @@ public class WindowManagerWrapper {
}
}
- public void overridePendingAppTransitionRemote(
- RemoteAnimationAdapterCompat remoteAnimationAdapter, int displayId) {
- try {
- WindowManagerGlobal.getWindowManagerService().overridePendingAppTransitionRemote(
- remoteAnimationAdapter.getWrapped(), displayId);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to override pending app transition (remote): ", e);
- }
- }
-
/**
* Enable or disable haptic feedback on the navigation bar buttons.
*/
@@ -170,19 +141,6 @@ public class WindowManagerWrapper {
}
}
- public void setRecentsVisibility(boolean visible) {
- try {
- WindowManagerGlobal.getWindowManagerService().setRecentsVisibility(visible);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to set recents visibility");
- }
- }
-
- @Deprecated
- public void setPipVisibility(final boolean visible) {
- // To be removed
- }
-
/**
* @param displayId the id of display to check if there is a software navigation bar.
*
@@ -197,22 +155,6 @@ public class WindowManagerWrapper {
}
/**
- * @return The side of the screen where navigation bar is positioned.
- * @see #NAV_BAR_POS_RIGHT
- * @see #NAV_BAR_POS_LEFT
- * @see #NAV_BAR_POS_BOTTOM
- * @see #NAV_BAR_POS_INVALID
- */
- public int getNavBarPosition(int displayId) {
- try {
- return WindowManagerGlobal.getWindowManagerService().getNavBarPosition(displayId);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to get nav bar position");
- }
- return NAV_BAR_POS_INVALID;
- }
-
- /**
* Mirrors a specified display. The SurfaceControl returned is the root of the mirrored
* hierarchy.
*
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
deleted file mode 100644
index d1b06394b818..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.unfold.config
-
-import android.content.Context
-import android.os.SystemProperties
-
-internal class ResourceUnfoldTransitionConfig(private val context: Context) :
- UnfoldTransitionConfig {
-
- override val isEnabled: Boolean
- get() = readIsEnabledResource() && isPropertyEnabled
-
- override val isHingeAngleEnabled: Boolean
- get() = readIsHingeAngleEnabled()
-
- private val isPropertyEnabled: Boolean
- get() =
- SystemProperties.getInt(
- UNFOLD_TRANSITION_MODE_PROPERTY_NAME, UNFOLD_TRANSITION_PROPERTY_ENABLED) ==
- UNFOLD_TRANSITION_PROPERTY_ENABLED
-
- private fun readIsEnabledResource(): Boolean =
- context.resources.getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled)
-
- private fun readIsHingeAngleEnabled(): Boolean =
- context.resources.getBoolean(com.android.internal.R.bool.config_unfoldTransitionHingeAngle)
-}
-
-/**
- * Temporary persistent property to control unfold transition mode.
- *
- * See [com.android.unfold.config.AnimationMode].
- */
-private const val UNFOLD_TRANSITION_MODE_PROPERTY_NAME = "persist.unfold.transition_enabled"
-private const val UNFOLD_TRANSITION_PROPERTY_ENABLED = 1
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt
new file mode 100644
index 000000000000..7f2933e44b32
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.system
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration
+import com.android.systemui.unfold.util.CurrentActivityTypeProvider
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class ActivityManagerActivityTypeProvider @Inject constructor(
+ private val activityManager: ActivityManager
+) : CurrentActivityTypeProvider {
+
+ override val isHomeActivity: Boolean?
+ get() {
+ val activityType = activityManager.getRunningTasks(/* maxNum= */ 1)
+ ?.getOrNull(0)?.topActivityType ?: return null
+
+ return activityType == WindowConfiguration.ACTIVITY_TYPE_HOME
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt
new file mode 100644
index 000000000000..3b8d318a3a79
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.system
+
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
+import com.android.systemui.unfold.updates.FoldProvider
+import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class DeviceStateManagerFoldProvider @Inject constructor(
+ private val deviceStateManager: DeviceStateManager,
+ private val context: Context
+) : FoldProvider {
+
+ private val callbacks: MutableMap<FoldCallback,
+ DeviceStateManager.DeviceStateCallback> = hashMapOf()
+
+ override fun registerCallback(callback: FoldCallback, executor: Executor) {
+ val listener = FoldStateListener(context, callback)
+ deviceStateManager.registerCallback(executor, listener)
+ callbacks[callback] = listener
+ }
+
+ override fun unregisterCallback(callback: FoldCallback) {
+ val listener = callbacks.remove(callback)
+ listener?.let {
+ deviceStateManager.unregisterCallback(it)
+ }
+ }
+
+ private inner class FoldStateListener(
+ context: Context,
+ listener: FoldCallback
+ ) : DeviceStateManager.FoldStateListener(context, { listener.onFoldUpdated(it) })
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
new file mode 100644
index 000000000000..24ae42ae4db2
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.system
+
+import android.os.Handler
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dagger.qualifiers.UiBackground
+import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.dagger.UnfoldBackground
+import com.android.systemui.unfold.dagger.UnfoldMain
+import com.android.systemui.unfold.updates.FoldProvider
+import com.android.systemui.unfold.util.CurrentActivityTypeProvider
+import dagger.Binds
+import dagger.Module
+import java.util.concurrent.Executor
+
+/**
+ * Dagger module with system-only dependencies for the unfold animation.
+ * The code that is used to calculate unfold transition progress
+ * depends on some hidden APIs that are not available in normal
+ * apps. In order to re-use this code and use alternative implementations
+ * of these classes in other apps and hidden APIs here.
+ */
+@Module
+abstract class SystemUnfoldSharedModule {
+
+ @Binds
+ abstract fun activityTypeProvider(executor: ActivityManagerActivityTypeProvider):
+ CurrentActivityTypeProvider
+
+ @Binds
+ abstract fun config(config: ResourceUnfoldTransitionConfig): UnfoldTransitionConfig
+
+ @Binds
+ abstract fun foldState(provider: DeviceStateManagerFoldProvider): FoldProvider
+
+ @Binds
+ @UnfoldMain
+ abstract fun mainExecutor(@Main executor: Executor): Executor
+
+ @Binds
+ @UnfoldMain
+ abstract fun mainHandler(@Main handler: Handler): Handler
+
+ @Binds
+ @UnfoldBackground
+ abstract fun backgroundExecutor(@UiBackground executor: Executor): Executor
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
deleted file mode 100644
index b351585de364..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.android.systemui.unfold.updates.hinge
-
-import androidx.core.util.Consumer
-
-internal object EmptyHingeAngleProvider : HingeAngleProvider {
- override fun start() {}
-
- override fun stop() {}
-
- override fun removeCallback(listener: Consumer<Float>) {}
-
- override fun addCallback(listener: Consumer<Float>) {}
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
deleted file mode 100644
index 48a5b12c759a..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.android.systemui.unfold.updates.hinge
-
-import androidx.core.util.Consumer
-import com.android.systemui.statusbar.policy.CallbackController
-
-/**
- * Emits device hinge angle values (angle between two integral parts of the device).
- *
- * The hinge angle could be from 0 to 360 degrees inclusive. For foldable devices usually 0
- * corresponds to fully closed (folded) state and 180 degrees corresponds to fully open (flat)
- * state.
- */
-interface HingeAngleProvider : CallbackController<Consumer<Float>> {
- fun start()
- fun stop()
-}
-
-const val FULLY_OPEN_DEGREES = 180f
-const val FULLY_CLOSED_DEGREES = 0f
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
index 53c528ff24a8..ec938b219933 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
@@ -1,3 +1,17 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.util
import android.content.Context
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 12fa401d7fea..d32219a9817f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -336,6 +336,11 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
mKeyguardSecurityContainerController.onStartingToHide();
}
+ /** Called when bouncer visibility changes. */
+ public void onBouncerVisibilityChanged(@View.Visibility int visibility) {
+ mKeyguardSecurityContainerController.onBouncerVisibilityChanged(visibility);
+ }
+
public boolean hasDismissActions() {
return mDismissAction != null || mCancelAction != null;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index db2b4ac2c669..58e0fb968204 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -58,7 +58,6 @@ data class KeyguardFaceListenModel(
val keyguardAwake: Boolean,
val keyguardGoingAway: Boolean,
val listeningForFaceAssistant: Boolean,
- val lockIconPressed: Boolean,
val occludingAppRequestingFaceAuth: Boolean,
val primaryUser: Boolean,
val scanningAllowedByStrongAuth: Boolean,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 81cc3a933600..3ec8ce918a4d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -18,6 +18,7 @@ package com.android.keyguard;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
+import android.text.TextUtils;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -103,6 +104,15 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag
mView.setMessage(resId);
}
+ /**
+ * Set Text if KeyguardMessageArea is empty.
+ */
+ public void setMessageIfEmpty(int resId) {
+ if (TextUtils.isEmpty(mView.getText())) {
+ setMessage(resId);
+ }
+ }
+
public void setNextMessageColor(ColorStateList colorState) {
mView.setNextMessageColor(colorState);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index bfacc590dc52..29e912fdab32 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -207,6 +207,7 @@ public class KeyguardPasswordViewController
if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
showInput();
}
+ mMessageAreaController.setMessageIfEmpty(R.string.keyguard_enter_your_password);
}
private void showInput() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 39c394981193..b97d9671eb25 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -304,6 +304,12 @@ public class KeyguardPatternViewController
}
@Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ mMessageAreaController.setMessageIfEmpty(R.string.keyguard_enter_your_pattern);
+ }
+
+ @Override
public boolean needsInput() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index f7423ed12e68..59a018ad51df 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -127,6 +127,7 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
public void onResume(int reason) {
super.onResume(reason);
mPasswordEntry.requestFocus();
+ mMessageAreaController.setMessageIfEmpty(R.string.keyguard_enter_your_pin);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index cce516d981a5..8fb622a8c55e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -50,6 +50,7 @@ import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
import android.util.TypedValue;
+import android.view.GestureDetector;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -93,6 +94,7 @@ import com.android.systemui.util.settings.GlobalSettings;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
public class KeyguardSecurityContainer extends FrameLayout {
static final int USER_TYPE_PRIMARY = 1;
@@ -127,12 +129,12 @@ public class KeyguardSecurityContainer extends FrameLayout {
private static final long IME_DISAPPEAR_DURATION_MS = 125;
- // The duration of the animation to switch bouncer sides.
- private static final long BOUNCER_HANDEDNESS_ANIMATION_DURATION_MS = 500;
+ // The duration of the animation to switch security sides.
+ private static final long SECURITY_SHIFT_ANIMATION_DURATION_MS = 500;
- // How much of the switch sides animation should be dedicated to fading the bouncer out. The
+ // How much of the switch sides animation should be dedicated to fading the security out. The
// remainder will fade it back in again.
- private static final float BOUNCER_HANDEDNESS_ANIMATION_FADE_OUT_PROPORTION = 0.2f;
+ private static final float SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION = 0.2f;
@VisibleForTesting
KeyguardSecurityViewFlipper mSecurityViewFlipper;
@@ -146,6 +148,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
private final SpringAnimation mSpringAnimation;
private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
private final List<Gefingerpoken> mMotionEventListeners = new ArrayList<>();
+ private final GestureDetector mDoubleTapDetector;
private float mLastTouchY = -1;
private int mActivePointerId = -1;
@@ -305,6 +308,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
super(context, attrs, defStyle);
mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
mViewConfiguration = ViewConfiguration.get(context);
+ mDoubleTapDetector = new GestureDetector(context, new DoubleTapListener());
}
void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
@@ -372,10 +376,23 @@ public class KeyguardSecurityContainer extends FrameLayout {
mViewMode.updatePositionByTouchX(x);
}
- /** Returns whether the inner SecurityViewFlipper is left-aligned when in one-handed mode. */
- public boolean isOneHandedModeLeftAligned() {
- return mCurrentMode == MODE_ONE_HANDED
- && ((OneHandedViewMode) mViewMode).isLeftAligned();
+ public boolean isSidedSecurityMode() {
+ return mViewMode instanceof SidedSecurityMode;
+ }
+
+ /** Returns whether the inner SecurityViewFlipper is left-aligned when in sided mode. */
+ public boolean isSecurityLeftAligned() {
+ return mViewMode instanceof SidedSecurityMode
+ && ((SidedSecurityMode) mViewMode).isLeftAligned();
+ }
+
+ /**
+ * Returns whether the touch happened on the other side of security (like bouncer) when in
+ * sided mode.
+ */
+ public boolean isTouchOnTheOtherSideOfSecurity(MotionEvent ev) {
+ return mViewMode instanceof SidedSecurityMode
+ && ((SidedSecurityMode) mViewMode).isTouchOnTheOtherSideOfSecurity(ev);
}
public void onPause() {
@@ -439,6 +456,10 @@ public class KeyguardSecurityContainer extends FrameLayout {
.anyMatch(listener -> listener.onTouchEvent(event))
|| super.onTouchEvent(event);
+ // double tap detector should be called after listeners handle touches as listeners are
+ // helping with ignoring falsing. Otherwise falsing will be activated for some double taps
+ mDoubleTapDetector.onTouchEvent(event);
+
switch (action) {
case MotionEvent.ACTION_MOVE:
mVelocityTracker.addMovement(event);
@@ -475,15 +496,26 @@ public class KeyguardSecurityContainer extends FrameLayout {
if (mSwipeListener != null) {
mSwipeListener.onSwipeUp();
}
- } else {
- if (!mIsDragging) {
- mViewMode.handleTap(event);
- }
}
}
return true;
}
+ private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ return handleDoubleTap(e);
+ }
+ }
+
+ @VisibleForTesting boolean handleDoubleTap(MotionEvent e) {
+ if (!mIsDragging) {
+ mViewMode.handleDoubleTap(e);
+ return true;
+ }
+ return false;
+ }
+
void addMotionEventListener(Gefingerpoken listener) {
mMotionEventListeners.add(listener);
}
@@ -736,8 +768,8 @@ public class KeyguardSecurityContainer extends FrameLayout {
/** Alter the ViewFlipper position, based upon a touch outside of it */
default void updatePositionByTouchX(float x) {};
- /** A tap on the container, outside of the ViewFlipper */
- default void handleTap(MotionEvent event) {};
+ /** A double tap on the container, outside of the ViewFlipper */
+ default void handleDoubleTap(MotionEvent event) {};
/** Called when the view needs to reset or hides */
default void reset() {};
@@ -761,6 +793,195 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
/**
+ * Base class for modes which support having on left/right side of the screen, used for large
+ * screen devices
+ */
+ abstract static class SidedSecurityMode implements ViewMode {
+ @Nullable private ValueAnimator mRunningSecurityShiftAnimator;
+ private KeyguardSecurityViewFlipper mViewFlipper;
+ private ViewGroup mView;
+ private GlobalSettings mGlobalSettings;
+ private int mDefaultSideSetting;
+
+ public void init(ViewGroup v, KeyguardSecurityViewFlipper viewFlipper,
+ GlobalSettings globalSettings, boolean leftAlignedByDefault) {
+ mView = v;
+ mViewFlipper = viewFlipper;
+ mGlobalSettings = globalSettings;
+ mDefaultSideSetting =
+ leftAlignedByDefault ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
+ : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT;
+ }
+
+ /**
+ * Determine if a double tap on this view is on the other side. If so, will animate
+ * positions and record the preference to always show on this side.
+ */
+ @Override
+ public void handleDoubleTap(MotionEvent event) {
+ boolean currentlyLeftAligned = isLeftAligned();
+ // Did the tap hit the "other" side of the bouncer?
+ if (isTouchOnTheOtherSideOfSecurity(event, currentlyLeftAligned)) {
+ boolean willBeLeftAligned = !currentlyLeftAligned;
+ updateSideSetting(willBeLeftAligned);
+
+ int keyguardState = willBeLeftAligned
+ ? SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SWITCH_LEFT
+ : SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SWITCH_RIGHT;
+ SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED, keyguardState);
+
+ updateSecurityViewLocation(willBeLeftAligned, /* animate= */ true);
+ }
+ }
+
+ private boolean isTouchOnTheOtherSideOfSecurity(MotionEvent ev, boolean leftAligned) {
+ float x = ev.getX();
+ return (leftAligned && (x > mView.getWidth() / 2f))
+ || (!leftAligned && (x < mView.getWidth() / 2f));
+ }
+
+ public boolean isTouchOnTheOtherSideOfSecurity(MotionEvent ev) {
+ return isTouchOnTheOtherSideOfSecurity(ev, isLeftAligned());
+ }
+
+ protected abstract void updateSecurityViewLocation(boolean leftAlign, boolean animate);
+
+ protected void translateSecurityViewLocation(boolean leftAlign, boolean animate) {
+ translateSecurityViewLocation(leftAlign, animate, i -> {});
+ }
+
+ /**
+ * Moves the inner security view to the correct location with animation. This is triggered
+ * when the user double taps on the side of the screen that is not currently occupied by
+ * the security view.
+ */
+ protected void translateSecurityViewLocation(boolean leftAlign, boolean animate,
+ Consumer<Float> securityAlphaListener) {
+ if (mRunningSecurityShiftAnimator != null) {
+ mRunningSecurityShiftAnimator.cancel();
+ mRunningSecurityShiftAnimator = null;
+ }
+
+ int targetTranslation = leftAlign
+ ? 0 : mView.getMeasuredWidth() - mViewFlipper.getWidth();
+
+ if (animate) {
+ // This animation is a bit fun to implement. The bouncer needs to move, and fade
+ // in/out at the same time. The issue is, the bouncer should only move a short
+ // amount (120dp or so), but obviously needs to go from one side of the screen to
+ // the other. This needs a pretty custom animation.
+ //
+ // This works as follows. It uses a ValueAnimation to simply drive the animation
+ // progress. This animator is responsible for both the translation of the bouncer,
+ // and the current fade. It will fade the bouncer out while also moving it along the
+ // 120dp path. Once the bouncer is fully faded out though, it will "snap" the
+ // bouncer closer to its destination, then fade it back in again. The effect is that
+ // the bouncer will move from 0 -> X while fading out, then
+ // (destination - X) -> destination while fading back in again.
+ // TODO(b/208250221): Make this animation properly abortable.
+ Interpolator positionInterpolator = AnimationUtils.loadInterpolator(
+ mView.getContext(), android.R.interpolator.fast_out_extra_slow_in);
+ Interpolator fadeOutInterpolator = Interpolators.FAST_OUT_LINEAR_IN;
+ Interpolator fadeInInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
+
+ mRunningSecurityShiftAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
+ mRunningSecurityShiftAnimator.setDuration(SECURITY_SHIFT_ANIMATION_DURATION_MS);
+ mRunningSecurityShiftAnimator.setInterpolator(Interpolators.LINEAR);
+
+ int initialTranslation = (int) mViewFlipper.getTranslationX();
+ int totalTranslation = (int) mView.getResources().getDimension(
+ R.dimen.security_shift_animation_translation);
+
+ final boolean shouldRestoreLayerType = mViewFlipper.hasOverlappingRendering()
+ && mViewFlipper.getLayerType() != View.LAYER_TYPE_HARDWARE;
+ if (shouldRestoreLayerType) {
+ mViewFlipper.setLayerType(View.LAYER_TYPE_HARDWARE, /* paint= */null);
+ }
+
+ float initialAlpha = mViewFlipper.getAlpha();
+
+ mRunningSecurityShiftAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRunningSecurityShiftAnimator = null;
+ }
+ });
+ mRunningSecurityShiftAnimator.addUpdateListener(animation -> {
+ float switchPoint = SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION;
+ boolean isFadingOut = animation.getAnimatedFraction() < switchPoint;
+
+ int currentTranslation = (int) (positionInterpolator.getInterpolation(
+ animation.getAnimatedFraction()) * totalTranslation);
+ int translationRemaining = totalTranslation - currentTranslation;
+
+ // Flip the sign if we're going from right to left.
+ if (leftAlign) {
+ currentTranslation = -currentTranslation;
+ translationRemaining = -translationRemaining;
+ }
+
+ float opacity;
+ if (isFadingOut) {
+ // The bouncer fades out over the first X%.
+ float fadeOutFraction = MathUtils.constrainedMap(
+ /* rangeMin= */1.0f,
+ /* rangeMax= */0.0f,
+ /* valueMin= */0.0f,
+ /* valueMax= */switchPoint,
+ animation.getAnimatedFraction());
+ opacity = fadeOutInterpolator.getInterpolation(fadeOutFraction);
+
+ // When fading out, the alpha needs to start from the initial opacity of the
+ // view flipper, otherwise we get a weird bit of jank as it ramps back to
+ // 100%.
+ mViewFlipper.setAlpha(opacity * initialAlpha);
+
+ // Animate away from the source.
+ mViewFlipper.setTranslationX(initialTranslation + currentTranslation);
+ } else {
+ // And in again over the remaining (100-X)%.
+ float fadeInFraction = MathUtils.constrainedMap(
+ /* rangeMin= */0.0f,
+ /* rangeMax= */1.0f,
+ /* valueMin= */switchPoint,
+ /* valueMax= */1.0f,
+ animation.getAnimatedFraction());
+
+ opacity = fadeInInterpolator.getInterpolation(fadeInFraction);
+ mViewFlipper.setAlpha(opacity);
+
+ // Fading back in, animate towards the destination.
+ mViewFlipper.setTranslationX(targetTranslation - translationRemaining);
+ }
+ securityAlphaListener.accept(opacity);
+
+ if (animation.getAnimatedFraction() == 1.0f && shouldRestoreLayerType) {
+ mViewFlipper.setLayerType(View.LAYER_TYPE_NONE, /* paint= */null);
+ }
+ });
+
+ mRunningSecurityShiftAnimator.start();
+ } else {
+ mViewFlipper.setTranslationX(targetTranslation);
+ }
+ }
+
+
+ boolean isLeftAligned() {
+ return mGlobalSettings.getInt(Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
+ mDefaultSideSetting)
+ == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
+ }
+
+ protected void updateSideSetting(boolean leftAligned) {
+ mGlobalSettings.putInt(
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
+ leftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
+ : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
+ }
+ }
+
+ /**
* Default bouncer is centered within the space
*/
static class DefaultViewMode implements ViewMode {
@@ -792,7 +1013,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
* User switcher mode will display both the current user icon as well as
* a user switcher, in both portrait and landscape modes.
*/
- static class UserSwitcherViewMode implements ViewMode {
+ static class UserSwitcherViewMode extends SidedSecurityMode {
private ViewGroup mView;
private ViewGroup mUserSwitcherViewGroup;
private KeyguardSecurityViewFlipper mViewFlipper;
@@ -804,11 +1025,15 @@ public class KeyguardSecurityContainer extends FrameLayout {
private UserSwitcherController.UserSwitchCallback mUserSwitchCallback =
this::setupUserSwitcher;
+ private float mAnimationLastAlpha = 1f;
+ private boolean mAnimationWaitsToShift = true;
+
@Override
public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
@NonNull KeyguardSecurityViewFlipper viewFlipper,
@NonNull FalsingManager falsingManager,
@NonNull UserSwitcherController userSwitcherController) {
+ init(v, viewFlipper, globalSettings, /* leftAlignedByDefault= */false);
mView = v;
mViewFlipper = viewFlipper;
mFalsingManager = falsingManager;
@@ -822,9 +1047,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
true);
mUserSwitcherViewGroup = mView.findViewById(R.id.keyguard_bouncer_user_switcher);
}
-
updateSecurityViewLocation();
-
mUserSwitcher = mView.findViewById(R.id.user_switcher_header);
setupUserSwitcher();
mUserSwitcherController.addUserSwitchCallback(mUserSwitchCallback);
@@ -1020,18 +1243,65 @@ public class KeyguardSecurityContainer extends FrameLayout {
@Override
public void updateSecurityViewLocation() {
- int yTrans = mResources.getDimensionPixelSize(R.dimen.bouncer_user_switcher_y_trans);
+ updateSecurityViewLocation(isLeftAligned(), /* animate= */false);
+ }
+ public void updateSecurityViewLocation(boolean leftAlign, boolean animate) {
+ setYTranslation();
+ setGravity();
+ setXTranslation(leftAlign, animate);
+ }
+
+ private void setXTranslation(boolean leftAlign, boolean animate) {
+ if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ mUserSwitcherViewGroup.setTranslationX(0);
+ mViewFlipper.setTranslationX(0);
+ } else {
+ int switcherTargetTranslation = leftAlign
+ ? mView.getMeasuredWidth() - mViewFlipper.getWidth() : 0;
+ if (animate) {
+ mAnimationWaitsToShift = true;
+ mAnimationLastAlpha = 1f;
+ translateSecurityViewLocation(leftAlign, animate, securityAlpha -> {
+ // During the animation security view fades out - alpha goes from 1 to
+ // (almost) 0 - and then fades in - alpha grows back to 1.
+ // If new alpha is bigger than previous one it means we're at inflection
+ // point and alpha is zero or almost zero. That's when we want to do
+ // translation of user switcher, so that it's not visible to the user.
+ boolean fullyFadeOut = securityAlpha == 0.0f
+ || securityAlpha > mAnimationLastAlpha;
+ if (fullyFadeOut && mAnimationWaitsToShift) {
+ mUserSwitcherViewGroup.setTranslationX(switcherTargetTranslation);
+ mAnimationWaitsToShift = false;
+ }
+ mUserSwitcherViewGroup.setAlpha(securityAlpha);
+ mAnimationLastAlpha = securityAlpha;
+ });
+ } else {
+ translateSecurityViewLocation(leftAlign, animate);
+ mUserSwitcherViewGroup.setTranslationX(switcherTargetTranslation);
+ }
+ }
+
+ }
+
+ private void setGravity() {
if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- updateViewGravity(mViewFlipper, Gravity.CENTER_HORIZONTAL);
updateViewGravity(mUserSwitcherViewGroup, Gravity.CENTER_HORIZONTAL);
+ updateViewGravity(mViewFlipper, Gravity.CENTER_HORIZONTAL);
+ } else {
+ // horizontal gravity is the same because we translate these views anyway
+ updateViewGravity(mViewFlipper, Gravity.LEFT | Gravity.BOTTOM);
+ updateViewGravity(mUserSwitcherViewGroup, Gravity.LEFT | Gravity.CENTER_VERTICAL);
+ }
+ }
+ private void setYTranslation() {
+ int yTrans = mResources.getDimensionPixelSize(R.dimen.bouncer_user_switcher_y_trans);
+ if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
mUserSwitcherViewGroup.setTranslationY(yTrans);
mViewFlipper.setTranslationY(-yTrans);
} else {
- updateViewGravity(mViewFlipper, Gravity.RIGHT | Gravity.BOTTOM);
- updateViewGravity(mUserSwitcherViewGroup, Gravity.LEFT | Gravity.CENTER_VERTICAL);
-
// Attempt to reposition a bit higher to make up for this frame being a bit lower
// on the device
mUserSwitcherViewGroup.setTranslationY(-yTrans);
@@ -1050,20 +1320,18 @@ public class KeyguardSecurityContainer extends FrameLayout {
* Logic to enabled one-handed bouncer mode. Supports animating the bouncer
* between alternate sides of the display.
*/
- static class OneHandedViewMode implements ViewMode {
- @Nullable private ValueAnimator mRunningOneHandedAnimator;
+ static class OneHandedViewMode extends SidedSecurityMode {
private ViewGroup mView;
private KeyguardSecurityViewFlipper mViewFlipper;
- private GlobalSettings mGlobalSettings;
@Override
public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
@NonNull KeyguardSecurityViewFlipper viewFlipper,
@NonNull FalsingManager falsingManager,
@NonNull UserSwitcherController userSwitcherController) {
+ init(v, viewFlipper, globalSettings, /* leftAlignedByDefault= */true);
mView = v;
mViewFlipper = viewFlipper;
- mGlobalSettings = globalSettings;
updateSecurityViewGravity();
updateSecurityViewLocation(isLeftAligned(), /* animate= */false);
@@ -1097,159 +1365,13 @@ public class KeyguardSecurityContainer extends FrameLayout {
updateSecurityViewLocation(isTouchOnLeft, /* animate= */false);
}
- boolean isLeftAligned() {
- return mGlobalSettings.getInt(Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
- Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT)
- == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
- }
-
- private void updateSideSetting(boolean leftAligned) {
- mGlobalSettings.putInt(
- Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
- leftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
- : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
- }
-
- /**
- * Determine if a tap on this view is on the other side. If so, will animate positions
- * and record the preference to always show on this side.
- */
- @Override
- public void handleTap(MotionEvent event) {
- float x = event.getX();
- boolean currentlyLeftAligned = isLeftAligned();
- // Did the tap hit the "other" side of the bouncer?
- if ((currentlyLeftAligned && (x > mView.getWidth() / 2f))
- || (!currentlyLeftAligned && (x < mView.getWidth() / 2f))) {
-
- boolean willBeLeftAligned = !currentlyLeftAligned;
- updateSideSetting(willBeLeftAligned);
-
- int keyguardState = willBeLeftAligned
- ? SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SWITCH_LEFT
- : SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SWITCH_RIGHT;
- SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED, keyguardState);
-
- updateSecurityViewLocation(willBeLeftAligned, true /* animate */);
- }
- }
-
@Override
public void updateSecurityViewLocation() {
updateSecurityViewLocation(isLeftAligned(), /* animate= */false);
}
- /**
- * Moves the inner security view to the correct location (in one handed mode) with
- * animation. This is triggered when the user taps on the side of the screen that is not
- * currently occupied by the security view.
- */
- private void updateSecurityViewLocation(boolean leftAlign, boolean animate) {
- if (mRunningOneHandedAnimator != null) {
- mRunningOneHandedAnimator.cancel();
- mRunningOneHandedAnimator = null;
- }
-
- int targetTranslation = leftAlign
- ? 0 : (int) (mView.getMeasuredWidth() - mViewFlipper.getWidth());
-
- if (animate) {
- // This animation is a bit fun to implement. The bouncer needs to move, and fade
- // in/out at the same time. The issue is, the bouncer should only move a short
- // amount (120dp or so), but obviously needs to go from one side of the screen to
- // the other. This needs a pretty custom animation.
- //
- // This works as follows. It uses a ValueAnimation to simply drive the animation
- // progress. This animator is responsible for both the translation of the bouncer,
- // and the current fade. It will fade the bouncer out while also moving it along the
- // 120dp path. Once the bouncer is fully faded out though, it will "snap" the
- // bouncer closer to its destination, then fade it back in again. The effect is that
- // the bouncer will move from 0 -> X while fading out, then
- // (destination - X) -> destination while fading back in again.
- // TODO(b/208250221): Make this animation properly abortable.
- Interpolator positionInterpolator = AnimationUtils.loadInterpolator(
- mView.getContext(), android.R.interpolator.fast_out_extra_slow_in);
- Interpolator fadeOutInterpolator = Interpolators.FAST_OUT_LINEAR_IN;
- Interpolator fadeInInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
-
- mRunningOneHandedAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
- mRunningOneHandedAnimator.setDuration(BOUNCER_HANDEDNESS_ANIMATION_DURATION_MS);
- mRunningOneHandedAnimator.setInterpolator(Interpolators.LINEAR);
-
- int initialTranslation = (int) mViewFlipper.getTranslationX();
- int totalTranslation = (int) mView.getResources().getDimension(
- R.dimen.one_handed_bouncer_move_animation_translation);
-
- final boolean shouldRestoreLayerType = mViewFlipper.hasOverlappingRendering()
- && mViewFlipper.getLayerType() != View.LAYER_TYPE_HARDWARE;
- if (shouldRestoreLayerType) {
- mViewFlipper.setLayerType(View.LAYER_TYPE_HARDWARE, /* paint= */null);
- }
-
- float initialAlpha = mViewFlipper.getAlpha();
-
- mRunningOneHandedAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mRunningOneHandedAnimator = null;
- }
- });
- mRunningOneHandedAnimator.addUpdateListener(animation -> {
- float switchPoint = BOUNCER_HANDEDNESS_ANIMATION_FADE_OUT_PROPORTION;
- boolean isFadingOut = animation.getAnimatedFraction() < switchPoint;
-
- int currentTranslation = (int) (positionInterpolator.getInterpolation(
- animation.getAnimatedFraction()) * totalTranslation);
- int translationRemaining = totalTranslation - currentTranslation;
-
- // Flip the sign if we're going from right to left.
- if (leftAlign) {
- currentTranslation = -currentTranslation;
- translationRemaining = -translationRemaining;
- }
-
- if (isFadingOut) {
- // The bouncer fades out over the first X%.
- float fadeOutFraction = MathUtils.constrainedMap(
- /* rangeMin= */1.0f,
- /* rangeMax= */0.0f,
- /* valueMin= */0.0f,
- /* valueMax= */switchPoint,
- animation.getAnimatedFraction());
- float opacity = fadeOutInterpolator.getInterpolation(fadeOutFraction);
-
- // When fading out, the alpha needs to start from the initial opacity of the
- // view flipper, otherwise we get a weird bit of jank as it ramps back to
- // 100%.
- mViewFlipper.setAlpha(opacity * initialAlpha);
-
- // Animate away from the source.
- mViewFlipper.setTranslationX(initialTranslation + currentTranslation);
- } else {
- // And in again over the remaining (100-X)%.
- float fadeInFraction = MathUtils.constrainedMap(
- /* rangeMin= */0.0f,
- /* rangeMax= */1.0f,
- /* valueMin= */switchPoint,
- /* valueMax= */1.0f,
- animation.getAnimatedFraction());
-
- float opacity = fadeInInterpolator.getInterpolation(fadeInFraction);
- mViewFlipper.setAlpha(opacity);
-
- // Fading back in, animate towards the destination.
- mViewFlipper.setTranslationX(targetTranslation - translationRemaining);
- }
-
- if (animation.getAnimatedFraction() == 1.0f && shouldRestoreLayerType) {
- mViewFlipper.setLayerType(View.LAYER_TYPE_NONE, /* paint= */null);
- }
- });
-
- mRunningOneHandedAnimator.start();
- } else {
- mViewFlipper.setTranslationX(targetTranslation);
- }
+ protected void updateSecurityViewLocation(boolean leftAlign, boolean animate) {
+ translateSecurityViewLocation(leftAlign, animate);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 19a2d9ed5b7b..5ee659be6dd2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import static android.app.StatusBarManager.SESSION_KEYGUARD;
+import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC;
import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS;
@@ -32,11 +33,13 @@ import android.app.admin.DevicePolicyManager;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
+import android.hardware.biometrics.BiometricSourceType;
import android.metrics.LogMaker;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
import android.view.MotionEvent;
+import android.view.View;
import androidx.annotation.Nullable;
@@ -55,6 +58,7 @@ import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
+import com.android.systemui.biometrics.SidefpsController;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -67,6 +71,8 @@ import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.settings.GlobalSettings;
+import java.util.Optional;
+
import javax.inject.Inject;
/** Controller for {@link KeyguardSecurityContainer} */
@@ -93,6 +99,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
private final GlobalSettings mGlobalSettings;
private final FeatureFlags mFeatureFlags;
private final SessionTracker mSessionTracker;
+ private final Optional<SidefpsController> mSidefpsController;
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
@@ -116,12 +123,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
// If we're in one handed mode, the user can tap on the opposite side of the screen
// to move the bouncer across. In that case, inhibit the falsing (otherwise the taps
// to move the bouncer to each screen side can end up closing it instead).
- if (mView.getMode() == KeyguardSecurityContainer.MODE_ONE_HANDED) {
- boolean isLeftAligned = mView.isOneHandedModeLeftAligned();
- if ((isLeftAligned && ev.getX() > mView.getWidth() / 2f)
- || (!isLeftAligned && ev.getX() <= mView.getWidth() / 2f)) {
- mFalsingCollector.avoidGesture();
- }
+ if (mView.isTouchOnTheOtherSideOfSecurity(ev)) {
+ mFalsingCollector.avoidGesture();
}
if (mTouchDown != null) {
@@ -169,8 +172,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
int bouncerSide = SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__DEFAULT;
- if (mView.getMode() == KeyguardSecurityContainer.MODE_ONE_HANDED) {
- bouncerSide = mView.isOneHandedModeLeftAligned()
+ if (mView.isSidedSecurityMode()) {
+ bouncerSide = mView.isSecurityLeftAligned()
? SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__LEFT
: SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__RIGHT;
}
@@ -240,13 +243,27 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
reloadColors();
}
};
+ private boolean mBouncerVisible = false;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
- @Override
- public void onDevicePolicyManagerStateChanged() {
- showPrimarySecurityScreen(false);
- }
- };
+ @Override
+ public void onDevicePolicyManagerStateChanged() {
+ showPrimarySecurityScreen(false);
+ }
+
+ @Override
+ public void onBiometricRunningStateChanged(boolean running,
+ BiometricSourceType biometricSourceType) {
+ if (biometricSourceType == FINGERPRINT) {
+ updateSideFpsVisibility();
+ }
+ }
+
+ @Override
+ public void onStrongAuthStateChanged(int userId) {
+ updateSideFpsVisibility();
+ }
+ };
private KeyguardSecurityContainerController(KeyguardSecurityContainer view,
AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory,
@@ -264,7 +281,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
UserSwitcherController userSwitcherController,
FeatureFlags featureFlags,
GlobalSettings globalSettings,
- SessionTracker sessionTracker) {
+ SessionTracker sessionTracker,
+ Optional<SidefpsController> sidefpsController) {
super(view);
mLockPatternUtils = lockPatternUtils;
mUpdateMonitor = keyguardUpdateMonitor;
@@ -284,6 +302,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mFeatureFlags = featureFlags;
mGlobalSettings = globalSettings;
mSessionTracker = sessionTracker;
+ mSidefpsController = sidefpsController;
}
@Override
@@ -315,8 +334,23 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
getCurrentSecurityController().onPause();
}
mView.onPause();
+ // It might happen that onStartingToHide is not called when the device is locked while on
+ // bouncer.
+ setBouncerVisible(false);
}
+ private void updateSideFpsVisibility() {
+ if (!mSidefpsController.isPresent()) {
+ return;
+ }
+ if (mBouncerVisible && mView.isSidedSecurityMode()
+ && mUpdateMonitor.isFingerprintDetectionRunning()
+ && !mUpdateMonitor.userNeedsStrongAuth()) {
+ mSidefpsController.get().show();
+ } else {
+ mSidefpsController.get().hide();
+ }
+ }
/**
* Shows the primary security screen for the user. This will be either the multi-selector
@@ -367,8 +401,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
public void onResume(int reason) {
if (mCurrentSecurityMode != SecurityMode.None) {
int state = SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN;
- if (mView.getMode() == KeyguardSecurityContainer.MODE_ONE_HANDED) {
- state = mView.isOneHandedModeLeftAligned()
+ if (mView.isSidedSecurityMode()) {
+ state = mView.isSecurityLeftAligned()
? SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN_LEFT
: SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN_RIGHT;
}
@@ -401,6 +435,17 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
if (mCurrentSecurityMode != SecurityMode.None) {
getCurrentSecurityController().onStartingToHide();
}
+ setBouncerVisible(false);
+ }
+
+ /** Called when the bouncer changes visibility. */
+ public void onBouncerVisibilityChanged(@View.Visibility int visibility) {
+ setBouncerVisible(visibility == View.VISIBLE);
+ }
+
+ private void setBouncerVisible(boolean visible) {
+ mBouncerVisible = visible;
+ updateSideFpsVisibility();
}
/**
@@ -659,6 +704,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
private final FeatureFlags mFeatureFlags;
private final UserSwitcherController mUserSwitcherController;
private final SessionTracker mSessionTracker;
+ private final Optional<SidefpsController> mSidefpsController;
@Inject
Factory(KeyguardSecurityContainer view,
@@ -677,7 +723,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
UserSwitcherController userSwitcherController,
FeatureFlags featureFlags,
GlobalSettings globalSettings,
- SessionTracker sessionTracker) {
+ SessionTracker sessionTracker,
+ Optional<SidefpsController> sidefpsController) {
mView = view;
mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
mLockPatternUtils = lockPatternUtils;
@@ -694,6 +741,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mGlobalSettings = globalSettings;
mUserSwitcherController = userSwitcherController;
mSessionTracker = sessionTracker;
+ mSidefpsController = sidefpsController;
}
public KeyguardSecurityContainerController create(
@@ -703,7 +751,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
mConfigurationController, mFalsingCollector, mFalsingManager,
- mUserSwitcherController, mFeatureFlags, mGlobalSettings, mSessionTracker);
+ mUserSwitcherController, mFeatureFlags, mGlobalSettings, mSessionTracker,
+ mSidefpsController);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 47df70b522f7..2f6fa145664e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
+
import android.annotation.NonNull;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
@@ -241,10 +243,9 @@ public class KeyguardSimPinViewController
displayMessage = mView.getResources().getString(
R.string.kg_password_wrong_pin_code_pukked);
} else if (attemptsRemaining > 0) {
- msgId = isDefault ? R.plurals.kg_password_default_pin_message :
- R.plurals.kg_password_wrong_pin_code;
- displayMessage = mView.getResources()
- .getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
+ msgId = isDefault ? R.string.kg_password_default_pin_message :
+ R.string.kg_password_wrong_pin_code;
+ displayMessage = icuMessageFormat(mView.getResources(), msgId, attemptsRemaining);
} else {
msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
displayMessage = mView.getResources().getString(msgId);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 760c2ccb71b5..c0971bf8c16d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
+
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
@@ -57,10 +59,9 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
if (attemptsRemaining == 0) {
displayMessage = getContext().getString(R.string.kg_password_wrong_puk_code_dead);
} else if (attemptsRemaining > 0) {
- int msgId = isDefault ? R.plurals.kg_password_default_puk_message :
- R.plurals.kg_password_wrong_puk_code;
- displayMessage = getContext().getResources()
- .getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
+ int msgId = isDefault ? R.string.kg_password_default_puk_message :
+ R.string.kg_password_wrong_puk_code;
+ displayMessage = icuMessageFormat(getResources(), msgId, attemptsRemaining);
} else {
int msgId = isDefault ? R.string.kg_puk_enter_puk_hint :
R.string.kg_password_puk_failed;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 727d108df339..c19175742ce5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -41,7 +41,6 @@ import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.AlarmManager;
-import android.app.PendingIntent;
import android.app.UserSwitchObserver;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
@@ -150,11 +149,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private static final boolean DEBUG_SPEW = false;
private static final int BIOMETRIC_LOCKOUT_RESET_DELAY_MS = 600;
- private static final String ACTION_FACE_UNLOCK_STARTED
- = "com.android.facelock.FACE_UNLOCK_STARTED";
- private static final String ACTION_FACE_UNLOCK_STOPPED
- = "com.android.facelock.FACE_UNLOCK_STOPPED";
-
// Callback messages
private static final int MSG_TIME_UPDATE = 301;
private static final int MSG_BATTERY_UPDATE = 302;
@@ -165,13 +159,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private static final int MSG_USER_SWITCHING = 310;
private static final int MSG_KEYGUARD_RESET = 312;
private static final int MSG_USER_SWITCH_COMPLETE = 314;
- private static final int MSG_USER_INFO_CHANGED = 317;
private static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318;
private static final int MSG_STARTED_WAKING_UP = 319;
private static final int MSG_FINISHED_GOING_TO_SLEEP = 320;
private static final int MSG_STARTED_GOING_TO_SLEEP = 321;
private static final int MSG_KEYGUARD_BOUNCER_CHANGED = 322;
- private static final int MSG_FACE_UNLOCK_STATE_CHANGED = 327;
private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 328;
private static final int MSG_AIRPLANE_MODE_CHANGED = 329;
private static final int MSG_SERVICE_STATE_CHANGE = 330;
@@ -250,7 +242,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final Context mContext;
private final boolean mIsPrimaryUser;
- private final boolean mIsAutomotive;
private final AuthController mAuthController;
private final StatusBarStateController mStatusBarStateController;
private int mStatusBarState;
@@ -328,8 +319,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final LatencyTracker mLatencyTracker;
private boolean mLogoutEnabled;
private boolean mIsFaceEnrolled;
- // If the user long pressed the lock icon, disabling face auth for the current session.
- private boolean mLockIconPressed;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private final Executor mBackgroundExecutor;
private SensorPrivacyManager mSensorPrivacyManager;
@@ -408,7 +397,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray();
private SparseBooleanArray mUserTrustIsUsuallyManaged = new SparseBooleanArray();
- private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray();
private Map<Integer, Intent> mSecondaryLockscreenRequirement = new HashMap<Integer, Intent>();
@VisibleForTesting
@@ -1135,21 +1123,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
- private void handleFaceUnlockStateChanged(boolean running, int userId) {
- Assert.isMainThread();
- mUserFaceUnlockRunning.put(userId, running);
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onFaceUnlockStateChanged(running, userId);
- }
- }
- }
-
- public boolean isFaceUnlockRunning(int userId) {
- return mUserFaceUnlockRunning.get(userId);
- }
-
public boolean isFingerprintDetectionRunning() {
return mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
}
@@ -1373,16 +1346,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
- static class DisplayClientState {
- public int clientGeneration;
- public boolean clearing;
- public PendingIntent intent;
- public int playbackState;
- public long playbackEventTime;
- }
-
- private DisplayClientState mDisplayClientState = new DisplayClientState();
-
@VisibleForTesting
protected final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -1456,19 +1419,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final String action = intent.getAction();
if (AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equals(action)) {
mHandler.sendEmptyMessage(MSG_TIME_UPDATE);
- } else if (Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_INFO_CHANGED,
- intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()), 0));
- } else if (ACTION_FACE_UNLOCK_STARTED.equals(action)) {
- Trace.beginSection(
- "KeyguardUpdateMonitor.mBroadcastAllReceiver#onReceive "
- + "ACTION_FACE_UNLOCK_STARTED");
- mHandler.sendMessage(mHandler.obtainMessage(MSG_FACE_UNLOCK_STATE_CHANGED, 1,
- getSendingUserId()));
- Trace.endSection();
- } else if (ACTION_FACE_UNLOCK_STOPPED.equals(action)) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_FACE_UNLOCK_STATE_CHANGED, 0,
- getSendingUserId()));
} else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED,
@@ -1767,7 +1717,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
protected void handleStartedGoingToSleep(int arg1) {
Assert.isMainThread();
- mLockIconPressed = false;
clearBiometricRecognized();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1815,16 +1764,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
- private void handleUserInfoChanged(int userId) {
- Assert.isMainThread();
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onUserInfoChanged(userId);
- }
- }
- }
-
private void handleUserUnlocked(int userId) {
Assert.isMainThread();
mUserIsUnlocked.put(userId, true);
@@ -1942,9 +1881,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
case MSG_KEYGUARD_BOUNCER_CHANGED:
handleKeyguardBouncerChanged(msg.arg1, msg.arg2);
break;
- case MSG_USER_INFO_CHANGED:
- handleUserInfoChanged(msg.arg1);
- break;
case MSG_REPORT_EMERGENCY_CALL_ACTION:
handleReportEmergencyCallAction();
break;
@@ -1959,12 +1895,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
handleStartedWakingUp();
Trace.endSection();
break;
- case MSG_FACE_UNLOCK_STATE_CHANGED:
- Trace.beginSection(
- "KeyguardUpdateMonitor#handler MSG_FACE_UNLOCK_STATE_CHANGED");
- handleFaceUnlockStateChanged(msg.arg1 != 0, msg.arg2);
- Trace.endSection();
- break;
case MSG_SIM_SUBSCRIPTION_INFO_CHANGED:
handleSimSubscriptionInfoChanged();
break;
@@ -2052,24 +1982,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
.getServiceStateForSubscriber(subId);
mHandler.sendMessage(
mHandler.obtainMessage(MSG_SERVICE_STATE_CHANGE, subId, 0, serviceState));
-
- // Get initial state. Relying on Sticky behavior until API for getting info.
- if (mBatteryStatus == null) {
- Intent intent = mContext.registerReceiver(
- null,
- new IntentFilter(Intent.ACTION_BATTERY_CHANGED)
- );
- if (intent != null && mBatteryStatus == null) {
- mBroadcastReceiver.onReceive(mContext, intent);
- }
- }
});
final IntentFilter allUserFilter = new IntentFilter();
- allUserFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
allUserFilter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
- allUserFilter.addAction(ACTION_FACE_UNLOCK_STARTED);
- allUserFilter.addAction(ACTION_FACE_UNLOCK_STOPPED);
allUserFilter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
allUserFilter.addAction(ACTION_USER_UNLOCKED);
allUserFilter.addAction(ACTION_USER_STOPPED);
@@ -2127,8 +2043,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFaceManager.addLockoutResetCallback(mFaceLockoutResetCallback);
}
- mIsAutomotive = isAutomotive();
-
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
mUserManager = context.getSystemService(UserManager.class);
mIsPrimaryUser = mUserManager.isPrimaryUser();
@@ -2629,7 +2543,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|| mAuthController.isUdfpsFingerDown()
|| mUdfpsBouncerShowing)
&& !mSwitchingUser && !faceDisabledForUser && becauseCannotSkipBouncer
- && !mKeyguardGoingAway && biometricEnabledForUser && !mLockIconPressed
+ && !mKeyguardGoingAway && biometricEnabledForUser
&& strongAuthAllowsScanning && mIsPrimaryUser
&& (!mSecureCameraLaunched || mOccludingAppRequestingFace)
&& !faceAuthenticated
@@ -2652,7 +2566,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
awakeKeyguard,
mKeyguardGoingAway,
shouldListenForFaceAssistant,
- mLockIconPressed,
mOccludingAppRequestingFace,
mIsPrimaryUser,
strongAuthAllowsScanning,
@@ -2697,18 +2610,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
- /**
- * Whenever the lock icon is long pressed, disabling trust agents.
- * This means that we cannot auth passively (face) until the user presses power.
- */
- public void onLockIconPressed() {
- mLockIconPressed = true;
- final int userId = getCurrentUser();
- mUserFaceAuthenticated.put(userId, null);
- updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
- mStrongAuthTracker.onStrongAuthRequiredChanged(userId);
- }
-
private void startListeningForFingerprint() {
final int userId = getCurrentUser();
final boolean unlockPossible = isUnlockWithFingerprintPossible(userId);
@@ -3179,20 +3080,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
}
- /** Notifies that the occluded state changed. */
- public void onKeyguardOccludedChanged(boolean occluded) {
- Assert.isMainThread();
- if (DEBUG) {
- Log.d(TAG, "onKeyguardOccludedChanged(" + occluded + ")");
- }
- for (int i = 0; i < mCallbacks.size(); i++) {
- KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
- if (cb != null) {
- cb.onKeyguardOccludedChanged(occluded);
- }
- }
- }
-
/**
* Handle {@link #MSG_KEYGUARD_RESET}
*/
@@ -3347,10 +3234,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return false;
}
- private boolean isAutomotive() {
- return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
- }
-
/**
* Remove the given observer's callback.
*
@@ -3413,8 +3296,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
callback.onTimeChanged();
callback.onPhoneStateChanged(mPhoneState);
callback.onRefreshCarrierInfo();
- callback.onClockVisibilityChanged();
- callback.onKeyguardOccludedChanged(mKeyguardOccluded);
callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible);
callback.onTelephonyCapable(mTelephonyCapable);
@@ -3591,10 +3472,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|| state == TelephonyManager.SIM_STATE_PERM_DISABLED);
}
- public DisplayClientState getCachedDisplayClientState() {
- return mDisplayClientState;
- }
-
// TODO: use these callbacks elsewhere in place of the existing notifyScreen*()
// (KeyguardViewMediator, KeyguardHostView)
public void dispatchStartedWakingUp() {
@@ -3643,6 +3520,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mHandler.sendEmptyMessage(MSG_KEYGUARD_DISMISS_ANIMATION_FINISHED);
}
+ /**
+ * @return true when the screen is on (including when a screensaver is showing),
+ * false when the screen is OFF or DOZE (including showing AOD UI)
+ */
public boolean isDeviceInteractive() {
return mDeviceInteractive;
}
@@ -3785,6 +3666,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
pw.println(" mFingerprintLockedOut=" + mFingerprintLockedOut);
pw.println(" mFingerprintLockedOutPermanent=" + mFingerprintLockedOutPermanent);
pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId));
+ pw.println(" mKeyguardOccluded=" + mKeyguardOccluded);
+ pw.println(" mIsDreaming=" + mIsDreaming);
if (isUdfpsSupported()) {
pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
pw.println(" shouldListenForUdfps=" + shouldListenForFingerprint(true));
@@ -3817,9 +3700,5 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
pw.println(" mNeedsSlowUnlockTransition=" + mNeedsSlowUnlockTransition);
}
mListenModels.print(pw);
-
- if (mIsAutomotive) {
- pw.println(" Running on Automotive build");
- }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 051b81e484d8..99e0ce29a8c2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -80,12 +80,6 @@ public class KeyguardUpdateMonitorCallback {
*/
public void onKeyguardVisibilityChanged(boolean showing) { }
- /**
- * Called when the keyguard occluded state changes.
- * @param occluded Indicates if the keyguard is now occluded.
- */
- public void onKeyguardOccludedChanged(boolean occluded) { }
-
public void onKeyguardVisibilityChangedRaw(boolean showing) {
final long now = SystemClock.elapsedRealtime();
if (showing == mShowing
@@ -117,12 +111,6 @@ public class KeyguardUpdateMonitorCallback {
public void onKeyguardDismissAnimationFinished() { }
/**
- * Called when visibility of lockscreen clock changes, such as when
- * obscured by a widget.
- */
- public void onClockVisibilityChanged() { }
-
- /**
* Called when the device becomes provisioned
*/
public void onDeviceProvisioned() { }
@@ -151,11 +139,6 @@ public class KeyguardUpdateMonitorCallback {
public void onSimStateChanged(int subId, int slotId, int simState) { }
/**
- * Called when the user's info changed.
- */
- public void onUserInfoChanged(int userId) { }
-
- /**
* Called when a user got unlocked.
*/
public void onUserUnlocked() { }
@@ -260,11 +243,6 @@ public class KeyguardUpdateMonitorCallback {
BiometricSourceType biometricSourceType) { }
/**
- * Called when the state of face unlock changed.
- */
- public void onFaceUnlockStateChanged(boolean running, int userId) { }
-
- /**
* Called when biometric running state changed.
*/
public void onBiometricRunningStateChanged(boolean running,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index ca8728aecb4c..8293c74c5e75 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -23,10 +23,10 @@ import android.view.ViewRootImpl;
import androidx.annotation.Nullable;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index c1b2aba22b57..06e1828ef9f4 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -23,7 +23,6 @@ import static com.android.keyguard.LockIconView.ICON_LOCK;
import static com.android.keyguard.LockIconView.ICON_UNLOCK;
import static com.android.systemui.classifier.Classifier.LOCK_ICON;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInProgressOffset;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -403,7 +402,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
float offsetY = MathUtils.lerp(0f,
getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
- mMaxBurnInOffsetY, mInterpolatedDarkAmount);
- float progress = MathUtils.lerp(0f, getBurnInProgressOffset(), mInterpolatedDarkAmount);
mView.setTranslationX(offsetX);
mView.setTranslationY(offsetY);
@@ -652,7 +650,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
Process.myUid(),
getContext().getOpPackageName(),
UdfpsController.EFFECT_CLICK,
- "lock-icon-device-entry",
+ "lock-screen-lock-icon-longpress",
TOUCH_VIBRATION_ATTRIBUTES);
mKeyguardViewController.showBouncer(/* scrim */ true);
@@ -677,6 +675,12 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
}
private boolean isActionable() {
+ if (mIsBouncerShowing) {
+ Log.v(TAG, "lock icon long-press ignored, bouncer already showing.");
+ // a long press gestures from AOD may have already triggered the bouncer to show,
+ // so this touch is no longer actionable
+ return false;
+ }
return mUdfpsSupported || mShowUnlockIcon;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 013cdac94ab8..9a0bfc19f848 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -53,8 +53,11 @@ import javax.inject.Inject;
/**
* Manages custom clock faces for AOD and lock screen.
+ *
+ * @deprecated Migrate to ClockRegistry
*/
@SysUISingleton
+@Deprecated
public final class ClockManager {
private static final String TAG = "ClockOptsProvider";
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
index b3c11584bcf8..49e97836b18b 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
@@ -16,6 +16,10 @@
package com.android.keyguard.dagger;
+import static com.android.systemui.biometrics.SidefpsControllerKt.hasSideFpsSensor;
+
+import android.annotation.Nullable;
+import android.hardware.fingerprint.FingerprintManager;
import android.view.LayoutInflater;
import android.view.ViewGroup;
@@ -23,9 +27,14 @@ import com.android.keyguard.KeyguardHostView;
import com.android.keyguard.KeyguardSecurityContainer;
import com.android.keyguard.KeyguardSecurityViewFlipper;
import com.android.systemui.R;
+import com.android.systemui.biometrics.SidefpsController;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import java.util.Optional;
+
+import javax.inject.Provider;
+
import dagger.Module;
import dagger.Provides;
@@ -60,4 +69,16 @@ public interface KeyguardBouncerModule {
KeyguardSecurityContainer containerView) {
return containerView.findViewById(R.id.view_flipper);
}
+
+ /** Provides {@link SidefpsController} if the device has the side fingerprint sensor. */
+ @Provides
+ @KeyguardBouncerScope
+ static Optional<SidefpsController> providesOptionalSidefpsController(
+ @Nullable FingerprintManager fingerprintManager,
+ Provider<SidefpsController> sidefpsControllerProvider) {
+ if (!hasSideFpsSensor(fingerprintManager)) {
+ return Optional.empty();
+ }
+ return Optional.of(sidefpsControllerProvider.get());
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
index 153da4b6695a..d01c98a934ff 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
@@ -17,9 +17,9 @@
package com.android.keyguard.dagger;
import com.android.keyguard.KeyguardStatusViewController;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import dagger.BindsInstance;
import dagger.Subcomponent;
diff --git a/packages/SystemUI/src/com/android/systemui/Gefingerpoken.java b/packages/SystemUI/src/com/android/systemui/Gefingerpoken.java
index b2d5c216dcd1..74d7a8b03b7d 100644
--- a/packages/SystemUI/src/com/android/systemui/Gefingerpoken.java
+++ b/packages/SystemUI/src/com/android/systemui/Gefingerpoken.java
@@ -20,6 +20,13 @@ import android.view.MotionEvent;
// ACHTUNG!
public interface Gefingerpoken {
- boolean onInterceptTouchEvent(MotionEvent ev);
- boolean onTouchEvent(MotionEvent ev);
+ /** Called when a touch is being intercepted in a ViewGroup. */
+ default boolean onInterceptTouchEvent(MotionEvent ev) {
+ return false;
+ }
+
+ /** Called when a touch is being handled by a view. */
+ default boolean onTouchEvent(MotionEvent ev) {
+ return false;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java
new file mode 100644
index 000000000000..fd84543ee50b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.qs.QSUserSwitcherEvent;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+
+import javax.inject.Inject;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
+/**
+ * Manages handling of guest session persistent notification
+ * and actions to reset guest or exit guest session
+ */
+public final class GuestResetOrExitSessionReceiver extends BroadcastReceiver {
+
+ private static final String TAG = GuestResetOrExitSessionReceiver.class.getSimpleName();
+
+ /**
+ * Broadcast sent to the system when guest user needs to be reset.
+ * This is only sent to registered receivers, not manifest receivers.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_GUEST_RESET = "android.intent.action.GUEST_RESET";
+
+ /**
+ * Broadcast sent to the system when guest user needs to exit.
+ * This is only sent to registered receivers, not manifest receivers.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_GUEST_EXIT = "android.intent.action.GUEST_EXIT";
+
+ public AlertDialog mExitSessionDialog;
+ public AlertDialog mResetSessionDialog;
+ private final UserTracker mUserTracker;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final ResetSessionDialog.Factory mResetSessionDialogFactory;
+ private final ExitSessionDialog.Factory mExitSessionDialogFactory;
+
+ @Inject
+ public GuestResetOrExitSessionReceiver(UserTracker userTracker,
+ BroadcastDispatcher broadcastDispatcher,
+ ResetSessionDialog.Factory resetSessionDialogFactory,
+ ExitSessionDialog.Factory exitSessionDialogFactory) {
+ mUserTracker = userTracker;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mResetSessionDialogFactory = resetSessionDialogFactory;
+ mExitSessionDialogFactory = exitSessionDialogFactory;
+ }
+
+ /**
+ * Register this receiver with the {@link BroadcastDispatcher}
+ */
+ public void register() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_GUEST_RESET);
+ intentFilter.addAction(ACTION_GUEST_EXIT);
+ mBroadcastDispatcher.registerReceiver(this, intentFilter, null /* handler */,
+ UserHandle.SYSTEM);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ cancelResetDialog();
+ cancelExitDialog();
+
+ UserInfo currentUser = mUserTracker.getUserInfo();
+ if (!currentUser.isGuest()) {
+ return;
+ }
+
+ if (ACTION_GUEST_RESET.equals(action)) {
+ mResetSessionDialog = mResetSessionDialogFactory.create(currentUser.id);
+ mResetSessionDialog.show();
+ } else if (ACTION_GUEST_EXIT.equals(action)) {
+ mExitSessionDialog = mExitSessionDialogFactory.create(currentUser.id,
+ currentUser.isEphemeral());
+ mExitSessionDialog.show();
+ }
+ }
+
+ private void cancelResetDialog() {
+ if (mResetSessionDialog != null && mResetSessionDialog.isShowing()) {
+ mResetSessionDialog.cancel();
+ mResetSessionDialog = null;
+ }
+ }
+
+ private void cancelExitDialog() {
+ if (mExitSessionDialog != null && mExitSessionDialog.isShowing()) {
+ mExitSessionDialog.cancel();
+ mExitSessionDialog = null;
+ }
+ }
+
+ /**
+ * Dialog shown when asking for confirmation before
+ * reset and restart of guest user.
+ */
+ public static final class ResetSessionDialog extends SystemUIDialog implements
+ DialogInterface.OnClickListener {
+
+ private final UserSwitcherController mUserSwitcherController;
+ private final UiEventLogger mUiEventLogger;
+ private final int mUserId;
+
+ /** Factory class to create guest reset dialog instance */
+ @AssistedFactory
+ public interface Factory {
+ /** Create a guest reset dialog instance */
+ ResetSessionDialog create(int userId);
+ }
+
+ @AssistedInject
+ ResetSessionDialog(Context context,
+ UserSwitcherController userSwitcherController,
+ UiEventLogger uiEventLogger,
+ @Assisted int userId) {
+ super(context);
+
+ setTitle(com.android.settingslib.R.string.guest_reset_and_restart_dialog_title);
+ setMessage(context.getString(
+ com.android.settingslib.R.string.guest_reset_and_restart_dialog_message));
+ setButton(DialogInterface.BUTTON_NEUTRAL,
+ context.getString(android.R.string.cancel), this);
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ context.getString(
+ com.android.settingslib.R.string.guest_reset_guest_confirm_button), this);
+ setCanceledOnTouchOutside(false);
+
+ mUserSwitcherController = userSwitcherController;
+ mUiEventLogger = uiEventLogger;
+ mUserId = userId;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
+ mUserSwitcherController.removeGuestUser(mUserId, UserHandle.USER_NULL);
+ } else if (which == DialogInterface.BUTTON_NEUTRAL) {
+ cancel();
+ }
+ }
+ }
+
+ /**
+ * Dialog shown when asking for confirmation before
+ * exit of guest user.
+ */
+ public static final class ExitSessionDialog extends SystemUIDialog implements
+ DialogInterface.OnClickListener {
+
+ private final UserSwitcherController mUserSwitcherController;
+ private final int mUserId;
+ private boolean mIsEphemeral;
+
+ /** Factory class to create guest exit dialog instance */
+ @AssistedFactory
+ public interface Factory {
+ /** Create a guest exit dialog instance */
+ ExitSessionDialog create(int userId, boolean isEphemeral);
+ }
+
+ @AssistedInject
+ ExitSessionDialog(Context context,
+ UserSwitcherController userSwitcherController,
+ @Assisted int userId,
+ @Assisted boolean isEphemeral) {
+ super(context);
+
+ if (isEphemeral) {
+ setTitle(context.getString(
+ com.android.settingslib.R.string.guest_exit_dialog_title));
+ setMessage(context.getString(
+ com.android.settingslib.R.string.guest_exit_dialog_message));
+ setButton(DialogInterface.BUTTON_NEUTRAL,
+ context.getString(android.R.string.cancel), this);
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ context.getString(
+ com.android.settingslib.R.string.guest_exit_dialog_button), this);
+ } else {
+ setTitle(context.getString(
+ com.android.settingslib
+ .R.string.guest_exit_dialog_title_non_ephemeral));
+ setMessage(context.getString(
+ com.android.settingslib
+ .R.string.guest_exit_dialog_message_non_ephemeral));
+ setButton(DialogInterface.BUTTON_NEUTRAL,
+ context.getString(android.R.string.cancel), this);
+ setButton(DialogInterface.BUTTON_NEGATIVE,
+ context.getString(
+ com.android.settingslib.R.string.guest_exit_clear_data_button), this);
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ context.getString(
+ com.android.settingslib.R.string.guest_exit_save_data_button), this);
+ }
+ setCanceledOnTouchOutside(false);
+
+ mUserSwitcherController = userSwitcherController;
+ mUserId = userId;
+ mIsEphemeral = isEphemeral;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (mIsEphemeral) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ // Ephemeral guest: exit guest, guest is removed by the system
+ // on exit, since its marked ephemeral
+ mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, false);
+ } else if (which == DialogInterface.BUTTON_NEUTRAL) {
+ // Cancel clicked, do nothing
+ cancel();
+ }
+ } else {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ // Non-ephemeral guest: exit guest, guest is not removed by the system
+ // on exit, since its marked non-ephemeral
+ mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, false);
+ } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+ // Non-ephemeral guest: remove guest and then exit
+ mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, true);
+ } else if (which == DialogInterface.BUTTON_NEUTRAL) {
+ // Cancel clicked, do nothing
+ cancel();
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
index 9a6020f8556b..76a7cad15419 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -35,12 +35,18 @@ import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.settings.SecureSettings;
+import javax.inject.Inject;
+
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
/**
* Manages notification when a guest session is resumed.
*/
public class GuestResumeSessionReceiver extends BroadcastReceiver {
- private static final String TAG = "GuestResumeSessionReceiver";
+ private static final String TAG = GuestResumeSessionReceiver.class.getSimpleName();
@VisibleForTesting
public static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
@@ -48,27 +54,31 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
@VisibleForTesting
public AlertDialog mNewSessionDialog;
private final UserTracker mUserTracker;
- private final UserSwitcherController mUserSwitcherController;
- private final UiEventLogger mUiEventLogger;
private final SecureSettings mSecureSettings;
-
- public GuestResumeSessionReceiver(UserSwitcherController userSwitcherController,
- UserTracker userTracker, UiEventLogger uiEventLogger,
- SecureSettings secureSettings) {
- mUserSwitcherController = userSwitcherController;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final ResetSessionDialog.Factory mResetSessionDialogFactory;
+ private final GuestSessionNotification mGuestSessionNotification;
+
+ @Inject
+ public GuestResumeSessionReceiver(
+ UserTracker userTracker,
+ SecureSettings secureSettings,
+ BroadcastDispatcher broadcastDispatcher,
+ GuestSessionNotification guestSessionNotification,
+ ResetSessionDialog.Factory resetSessionDialogFactory) {
mUserTracker = userTracker;
- mUiEventLogger = uiEventLogger;
mSecureSettings = secureSettings;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mGuestSessionNotification = guestSessionNotification;
+ mResetSessionDialogFactory = resetSessionDialogFactory;
}
/**
* Register this receiver with the {@link BroadcastDispatcher}
- *
- * @param broadcastDispatcher to register the receiver.
*/
- public void register(BroadcastDispatcher broadcastDispatcher) {
+ public void register() {
IntentFilter f = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- broadcastDispatcher.registerReceiver(this, f, null /* handler */, UserHandle.SYSTEM);
+ mBroadcastDispatcher.registerReceiver(this, f, null /* handler */, UserHandle.SYSTEM);
}
@Override
@@ -89,14 +99,25 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
return;
}
- int notFirstLogin = mSecureSettings.getIntForUser(
+ int guestLoginState = mSecureSettings.getIntForUser(
SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
- if (notFirstLogin != 0) {
- mNewSessionDialog = new ResetSessionDialog(context, mUserSwitcherController,
- mUiEventLogger, userId);
+
+ if (guestLoginState == 0) {
+ // set 1 to indicate, 1st login
+ guestLoginState = 1;
+ mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState, userId);
+ } else if (guestLoginState == 1) {
+ // set 2 to indicate, 2nd or later login
+ guestLoginState = 2;
+ mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState, userId);
+ }
+
+ mGuestSessionNotification.createPersistentNotification(currentUser,
+ (guestLoginState <= 1));
+
+ if (guestLoginState > 1) {
+ mNewSessionDialog = mResetSessionDialogFactory.create(userId);
mNewSessionDialog.show();
- } else {
- mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, 1, userId);
}
}
}
@@ -124,10 +145,19 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver {
private final UiEventLogger mUiEventLogger;
private final int mUserId;
- ResetSessionDialog(Context context,
+
+ /** Factory class to create guest reset dialog instance */
+ @AssistedFactory
+ public interface Factory {
+ /** Create a guest reset dialog instance */
+ ResetSessionDialog create(int userId);
+ }
+
+ @AssistedInject
+ public ResetSessionDialog(Context context,
UserSwitcherController userSwitcherController,
UiEventLogger uiEventLogger,
- int userId) {
+ @Assisted int userId) {
super(context, false /* dismissOnDeviceLock */);
setTitle(context.getString(R.string.guest_wipe_session_title));
diff --git a/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java b/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java
new file mode 100644
index 000000000000..b0eaab97c5ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.FeatureFlagUtils;
+
+import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.systemui.util.NotificationChannels;
+
+import javax.inject.Inject;
+
+/**
+ * Posts a persistent notification on entry to guest mode
+ */
+public final class GuestSessionNotification {
+
+ private static final String TAG = GuestSessionNotification.class.getSimpleName();
+
+ private final Context mContext;
+ private final NotificationManager mNotificationManager;
+
+ @Inject
+ public GuestSessionNotification(Context context,
+ NotificationManager notificationManager) {
+ mContext = context;
+ mNotificationManager = notificationManager;
+ }
+
+ private void overrideNotificationAppName(Notification.Builder notificationBuilder) {
+ final Bundle extras = new Bundle();
+ String appName = mContext.getString(R.string.guest_notification_app_name);
+
+ extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appName);
+
+ notificationBuilder.addExtras(extras);
+ }
+
+ void createPersistentNotification(UserInfo userInfo, boolean isGuestFirstLogin) {
+ if (!FeatureFlagUtils.isEnabled(mContext,
+ FeatureFlagUtils.SETTINGS_GUEST_MODE_UX_CHANGES)
+ || !userInfo.isGuest()) {
+ // we create a persistent notification only if enabled and only for guests
+ return;
+ }
+ String contentText;
+ if (userInfo.isEphemeral()) {
+ contentText = mContext.getString(R.string.guest_notification_ephemeral);
+ } else if (isGuestFirstLogin) {
+ contentText = mContext.getString(R.string.guest_notification_non_ephemeral);
+ } else {
+ contentText = mContext.getString(
+ R.string.guest_notification_non_ephemeral_non_first_login);
+ }
+
+ final Intent guestExitIntent = new Intent(
+ GuestResetOrExitSessionReceiver.ACTION_GUEST_EXIT);
+ final Intent userSettingsIntent = new Intent(Settings.ACTION_USER_SETTINGS);
+
+ PendingIntent guestExitPendingIntent =
+ PendingIntent.getBroadcastAsUser(mContext, 0, guestExitIntent,
+ PendingIntent.FLAG_IMMUTABLE,
+ UserHandle.SYSTEM);
+
+ PendingIntent userSettingsPendingIntent =
+ PendingIntent.getActivityAsUser(mContext, 0, userSettingsIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
+ null,
+ UserHandle.of(userInfo.id));
+
+ Notification.Builder builder = new Notification.Builder(mContext,
+ NotificationChannels.ALERTS)
+ .setSmallIcon(R.drawable.ic_account_circle)
+ .setContentTitle(mContext.getString(R.string.guest_notification_session_active))
+ .setContentText(contentText)
+ .setPriority(Notification.PRIORITY_DEFAULT)
+ .setOngoing(true)
+ .setContentIntent(userSettingsPendingIntent);
+
+ // we show reset button only if this is a 2nd or later login
+ if (!isGuestFirstLogin) {
+ final Intent guestResetIntent = new Intent(
+ GuestResetOrExitSessionReceiver.ACTION_GUEST_RESET);
+
+ PendingIntent guestResetPendingIntent =
+ PendingIntent.getBroadcastAsUser(mContext, 0, guestResetIntent,
+ PendingIntent.FLAG_IMMUTABLE,
+ UserHandle.SYSTEM);
+
+ builder.addAction(R.drawable.ic_sysbar_home,
+ mContext.getString(
+ com.android.settingslib.R.string.guest_reset_guest_confirm_button),
+ guestResetPendingIntent);
+ }
+ builder.addAction(R.drawable.ic_sysbar_home,
+ mContext.getString(
+ com.android.settingslib.R.string.guest_exit_button),
+ guestExitPendingIntent);
+
+ overrideNotificationAppName(builder);
+
+ mNotificationManager.notifyAsUser(null,
+ SystemMessageProto.SystemMessage.NOTE_GUEST_SESSION,
+ builder.build(),
+ UserHandle.of(userInfo.id));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Somnambulator.java b/packages/SystemUI/src/com/android/systemui/Somnambulator.java
index 0dd6d9283223..25801cfe62b0 100644
--- a/packages/SystemUI/src/com/android/systemui/Somnambulator.java
+++ b/packages/SystemUI/src/com/android/systemui/Somnambulator.java
@@ -17,12 +17,15 @@
package com.android.systemui;
import android.app.Activity;
-import android.content.Intent;
import android.service.dreams.Sandman;
/**
* A simple activity that launches a dream.
* <p>
+ *
+ * This activity has been deprecated and no longer used. The system uses its presence to determine
+ * whether a dock app should be started on dock through intent resolution.
+ *
* Note: This Activity is special. If this class is moved to another package or
* renamed, be sure to update the component name in {@link Sandman}.
* </p>
@@ -34,27 +37,6 @@ public class Somnambulator extends Activity {
@Override
public void onStart() {
super.onStart();
-
- final Intent launchIntent = getIntent();
- final String action = launchIntent.getAction();
- if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
- Intent shortcutIntent = new Intent(this, Somnambulator.class);
- shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_NEW_TASK);
- Intent resultIntent = new Intent();
- resultIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
- Intent.ShortcutIconResource.fromContext(this, R.mipmap.ic_launcher_dreams));
- resultIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
- resultIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getString(R.string.start_dreams));
- setResult(RESULT_OK, resultIntent);
- } else {
- boolean docked = launchIntent.hasCategory(Intent.CATEGORY_DESK_DOCK);
- if (docked) {
- Sandman.startDreamWhenDockedIfAppropriate(this);
- } else {
- Sandman.startDreamByUserRequest(this);
- }
- }
finish();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
index 714d267bb07d..527ce127820e 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
@@ -16,160 +16,22 @@
package com.android.systemui;
-import android.app.Activity;
-import android.app.Application;
-import android.app.Service;
-import android.content.BroadcastReceiver;
-import android.content.ContentProvider;
import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.app.AppComponentFactory;
-
-import com.android.systemui.dagger.ContextComponentHelper;
-import com.android.systemui.dagger.SysUIComponent;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import javax.inject.Inject;
/**
- * Implementation of AppComponentFactory that injects into constructors.
+ * Starts up SystemUI using the AOSP {@link SystemUIInitializerImpl}.
*
- * This class sets up dependency injection when creating our application.
+ * This initializer relies on reflection to start everything up and should be considered deprecated.
+ * Instead, create your own {@link SystemUIAppComponentFactoryBase}, specify it in your
+ * AndroidManifest.xml and construct your own {@link SystemUIInitializer} directly.
*
- * Services support dependency injection into their constructors.
- *
- * ContentProviders support injection into member variables - _not_ constructors.
+ * @deprecated Define your own SystemUIAppComponentFactoryBase implementation and use that. This
+ * implementation may be changed or removed in future releases.
*/
-public class SystemUIAppComponentFactory extends AppComponentFactory {
-
- private static final String TAG = "AppComponentFactory";
- @Inject
- public ContextComponentHelper mComponentHelper;
-
- public SystemUIAppComponentFactory() {
- super();
- }
-
- @NonNull
+@Deprecated
+public class SystemUIAppComponentFactory extends SystemUIAppComponentFactoryBase {
@Override
- public Application instantiateApplicationCompat(
- @NonNull ClassLoader cl, @NonNull String className)
- throws InstantiationException, IllegalAccessException, ClassNotFoundException {
- Application app = super.instantiateApplicationCompat(cl, className);
- if (app instanceof ContextInitializer) {
- ((ContextInitializer) app).setContextAvailableCallback(
- context -> {
- SystemUIFactory.createFromConfig(context);
- SystemUIFactory.getInstance().getSysUIComponent().inject(
- SystemUIAppComponentFactory.this);
- }
- );
- }
-
- return app;
- }
-
- @NonNull
- @Override
- public ContentProvider instantiateProviderCompat(
- @NonNull ClassLoader cl, @NonNull String className)
- throws InstantiationException, IllegalAccessException, ClassNotFoundException {
-
- ContentProvider contentProvider = super.instantiateProviderCompat(cl, className);
- if (contentProvider instanceof ContextInitializer) {
- ((ContextInitializer) contentProvider).setContextAvailableCallback(
- context -> {
- SystemUIFactory.createFromConfig(context);
- SysUIComponent rootComponent =
- SystemUIFactory.getInstance().getSysUIComponent();
- try {
- Method injectMethod = rootComponent.getClass()
- .getMethod("inject", contentProvider.getClass());
- injectMethod.invoke(rootComponent, contentProvider);
- } catch (NoSuchMethodException
- | IllegalAccessException
- | InvocationTargetException e) {
- Log.w(TAG, "No injector for class: " + contentProvider.getClass(), e);
- }
- }
- );
- }
-
- return contentProvider;
- }
-
- @NonNull
- @Override
- public Activity instantiateActivityCompat(@NonNull ClassLoader cl, @NonNull String className,
- @Nullable Intent intent)
- throws InstantiationException, IllegalAccessException, ClassNotFoundException {
- if (mComponentHelper == null) {
- // This shouldn't happen, but is seen on occasion.
- // Bug filed against framework to take a look: http://b/141008541
- SystemUIFactory.getInstance().getSysUIComponent().inject(
- SystemUIAppComponentFactory.this);
- }
- Activity activity = mComponentHelper.resolveActivity(className);
- if (activity != null) {
- return activity;
- }
- return super.instantiateActivityCompat(cl, className, intent);
- }
-
- @NonNull
- @Override
- public Service instantiateServiceCompat(
- @NonNull ClassLoader cl, @NonNull String className, Intent intent)
- throws InstantiationException, IllegalAccessException, ClassNotFoundException {
- if (mComponentHelper == null) {
- // This shouldn't happen, but does when a device is freshly formatted.
- // Bug filed against framework to take a look: http://b/141008541
- SystemUIFactory.getInstance().getSysUIComponent().inject(
- SystemUIAppComponentFactory.this);
- }
- Service service = mComponentHelper.resolveService(className);
- if (service != null) {
- return service;
- }
- return super.instantiateServiceCompat(cl, className, intent);
- }
-
- @NonNull
- @Override
- public BroadcastReceiver instantiateReceiverCompat(@NonNull ClassLoader cl,
- @NonNull String className, @Nullable Intent intent)
- throws InstantiationException, IllegalAccessException, ClassNotFoundException {
- if (mComponentHelper == null) {
- // This shouldn't happen, but does when a device is freshly formatted.
- // Bug filed against framework to take a look: http://b/141008541
- SystemUIFactory.getInstance().getSysUIComponent().inject(
- SystemUIAppComponentFactory.this);
- }
- BroadcastReceiver receiver = mComponentHelper.resolveBroadcastReceiver(className);
- if (receiver != null) {
- return receiver;
- }
-
- return super.instantiateReceiverCompat(cl, className, intent);
- }
-
- /**
- * A callback that receives a Context when one is ready.
- */
- public interface ContextAvailableCallback {
- void onContextAvailable(Context context);
- }
-
- /**
- * Implemented in classes that get started by the system before a context is available.
- */
- public interface ContextInitializer {
- void setContextAvailableCallback(ContextAvailableCallback callback);
+ protected SystemUIInitializer createSystemUIInitializer(Context context) {
+ return SystemUIInitializerFactory.createWithContext(context);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
new file mode 100644
index 000000000000..12108b01ab28
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
+
+import android.app.Activity
+import android.app.Application
+import android.app.Service
+import android.content.BroadcastReceiver
+import android.content.ContentProvider
+import android.content.Context
+import android.content.Intent
+import android.util.Log
+import androidx.core.app.AppComponentFactory
+import com.android.systemui.dagger.ContextComponentHelper
+import java.lang.reflect.InvocationTargetException
+import java.util.concurrent.ExecutionException
+import javax.inject.Inject
+
+/**
+ * Implementation of AppComponentFactory that injects into constructors.
+ *
+ * This class sets up dependency injection when creating our application.
+ *
+ * Activities, Services, and BroadcastReceivers support dependency injection into
+ * their constructors.
+ *
+ * ContentProviders support injection into member variables - _not_ constructors.
+ */
+abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() {
+ companion object {
+ private const val TAG = "AppComponentFactory"
+ // Must be static due to http://b/141008541.
+ var systemUIInitializer: SystemUIInitializer? = null
+ }
+
+ @set:Inject
+ lateinit var componentHelper: ContextComponentHelper
+
+ /**
+ * Returns a new [SystemUIInitializer].
+ *
+ * The returned implementation should be specific to your build.
+ */
+ protected abstract fun createSystemUIInitializer(context: Context): SystemUIInitializer
+
+ private fun createSystemUIInitializerInternal(context: Context): SystemUIInitializer {
+ return systemUIInitializer ?: run {
+ val initializer = createSystemUIInitializer(context.applicationContext)
+ try {
+ initializer.init(false)
+ } catch (exception: ExecutionException) {
+ throw RuntimeException("Failed to initialize SysUI", exception)
+ } catch (exception: InterruptedException) {
+ throw RuntimeException("Failed to initialize SysUI", exception)
+ }
+ initializer.sysUIComponent.inject(
+ this@SystemUIAppComponentFactoryBase
+ )
+
+ systemUIInitializer = initializer
+ return initializer
+ }
+ }
+
+ override fun instantiateApplicationCompat(cl: ClassLoader, className: String): Application {
+ val app = super.instantiateApplicationCompat(cl, className)
+ if (app !is ContextInitializer) {
+ throw RuntimeException("App must implement ContextInitializer")
+ } else {
+ app.setContextAvailableCallback { context ->
+ createSystemUIInitializerInternal(context)
+ }
+ }
+
+ return app
+ }
+
+ override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider {
+ val contentProvider = super.instantiateProviderCompat(cl, className)
+ if (contentProvider is ContextInitializer) {
+ contentProvider.setContextAvailableCallback { context ->
+ val initializer = createSystemUIInitializerInternal(context)
+ val rootComponent = initializer.sysUIComponent
+ try {
+ val injectMethod = rootComponent.javaClass
+ .getMethod("inject", contentProvider.javaClass)
+ injectMethod.invoke(rootComponent, contentProvider)
+ } catch (e: NoSuchMethodException) {
+ Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e)
+ } catch (e: IllegalAccessException) {
+ Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e)
+ } catch (e: InvocationTargetException) {
+ Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e)
+ }
+ initializer
+ }
+ }
+ return contentProvider
+ }
+
+ override fun instantiateActivityCompat(
+ cl: ClassLoader,
+ className: String,
+ intent: Intent?
+ ): Activity {
+ if (!this::componentHelper.isInitialized) {
+ // This shouldn't happen, but is seen on occasion.
+ // Bug filed against framework to take a look: http://b/141008541
+ systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase)
+ }
+ return componentHelper.resolveActivity(className)
+ ?: super.instantiateActivityCompat(cl, className, intent)
+ }
+
+ override fun instantiateServiceCompat(
+ cl: ClassLoader,
+ className: String,
+ intent: Intent?
+ ): Service {
+ if (!this::componentHelper.isInitialized) {
+ // This shouldn't happen, but does when a device is freshly formatted.
+ // Bug filed against framework to take a look: http://b/141008541
+ systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase)
+ }
+ return componentHelper.resolveService(className)
+ ?: super.instantiateServiceCompat(cl, className, intent)
+ }
+
+ override fun instantiateReceiverCompat(
+ cl: ClassLoader,
+ className: String,
+ intent: Intent?
+ ): BroadcastReceiver {
+ if (!this::componentHelper.isInitialized) {
+ // This shouldn't happen, but does when a device is freshly formatted.
+ // Bug filed against framework to take a look: http://b/141008541
+ systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase)
+ }
+ return componentHelper.resolveBroadcastReceiver(className)
+ ?: super.instantiateReceiverCompat(cl, className, intent)
+ }
+
+ /**
+ * An Interface for classes that can be notified when an Application Context becomes available.
+ *
+ * An instance of this will be passed to implementers of [ContextInitializer].
+ */
+ fun interface ContextAvailableCallback {
+ /** Notifies when the Application Context is available. */
+ fun onContextAvailable(context: Context): SystemUIInitializer
+ }
+
+ /**
+ * Interface for classes that can be constructed by the system before a context is available.
+ *
+ * This is intended for [Application] and [ContentProvider] implementations that
+ * either may not have a Context until some point after construction or are themselves
+ * a [Context].
+ *
+ * Implementers will be passed a [ContextAvailableCallback] that they should call as soon
+ * as an Application Context is ready.
+ */
+ interface ContextInitializer {
+ /**
+ * Called to supply the [ContextAvailableCallback] that should be called when an
+ * Application [Context] is available.
+ */
+ fun setContextAvailableCallback(callback: ContextAvailableCallback)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 6d3fd503dff6..9138b2346ab8 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -42,7 +42,6 @@ import android.view.SurfaceControl;
import android.view.ThreadedRenderer;
import com.android.internal.protolog.common.ProtoLog;
-import com.android.systemui.dagger.ContextComponentHelper;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dump.DumpManager;
@@ -65,7 +64,6 @@ public class SystemUIApplication extends Application implements
public static final String TAG = "SystemUIService";
private static final boolean DEBUG = false;
- private ContextComponentHelper mComponentHelper;
private BootCompleteCacheImpl mBootCompleteCache;
private DumpManager mDumpManager;
@@ -80,8 +78,8 @@ public class SystemUIApplication extends Application implements
private CoreStartable[] mServices;
private boolean mServicesStarted;
private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback;
- private GlobalRootComponent mRootComponent;
private SysUIComponent mSysUIComponent;
+ private SystemUIInitializer mInitializer;
public SystemUIApplication() {
super();
@@ -90,6 +88,10 @@ public class SystemUIApplication extends Application implements
ProtoLog.REQUIRE_PROTOLOGTOOL = false;
}
+ protected GlobalRootComponent getRootComponent() {
+ return mInitializer.getRootComponent();
+ }
+
@Override
public void onCreate() {
super.onCreate();
@@ -99,10 +101,8 @@ public class SystemUIApplication extends Application implements
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
Trace.TRACE_TAG_APP);
log.traceBegin("DependencyInjection");
- mContextAvailableCallback.onContextAvailable(this);
- mRootComponent = SystemUIFactory.getInstance().getRootComponent();
- mSysUIComponent = SystemUIFactory.getInstance().getSysUIComponent();
- mComponentHelper = mSysUIComponent.getContextComponentHelper();
+ mInitializer = mContextAvailableCallback.onContextAvailable(this);
+ mSysUIComponent = mInitializer.getSysUIComponent();
mBootCompleteCache = mSysUIComponent.provideBootCacheImpl();
log.traceEnd();
@@ -189,15 +189,14 @@ public class SystemUIApplication extends Application implements
*/
public void startServicesIfNeeded() {
- final String vendorComponent = SystemUIFactory.getInstance()
- .getVendorComponent(getResources());
+ final String vendorComponent = mInitializer.getVendorComponent(getResources());
// Sort the startables so that we get a deterministic ordering.
// TODO: make #start idempotent and require users of CoreStartable to call it.
Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>(
Comparator.comparing(Class::getName));
- sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponents());
- sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponentsPerUser());
+ sortedStartables.putAll(mSysUIComponent.getStartables());
+ sortedStartables.putAll(mSysUIComponent.getPerUserStartables());
startServicesIfNeeded(
sortedStartables, "StartServices", vendorComponent);
}
@@ -212,7 +211,7 @@ public class SystemUIApplication extends Application implements
// Sort the startables so that we get a deterministic ordering.
Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>(
Comparator.comparing(Class::getName));
- sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponentsPerUser());
+ sortedStartables.putAll(mSysUIComponent.getPerUserStartables());
startServicesIfNeeded(
sortedStartables, "StartSecondaryServices", null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
index f5084f5cff76..e9ca0fdbb929 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,97 +11,75 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
package com.android.systemui;
-import android.app.ActivityThread;
import android.content.Context;
-import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.dagger.DaggerGlobalRootComponent;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dagger.WMComponent;
+import com.android.systemui.util.InitializationChecker;
import com.android.wm.shell.dagger.WMShellConcurrencyModule;
-import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider;
-import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
+import com.android.wm.shell.sysui.ShellInterface;
import com.android.wm.shell.transition.ShellTransitions;
-import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
-
-import javax.inject.Provider;
/**
- * Class factory to provide customizable SystemUI components.
+ * Initializer that stands up SystemUI.
+ *
+ * Implementations should override {@link #getGlobalRootComponentBuilder()} to fill in their own
+ * Dagger root component.
*/
-public class SystemUIFactory {
+public abstract class SystemUIInitializer {
private static final String TAG = "SystemUIFactory";
- static SystemUIFactory mFactory;
+ private final Context mContext;
+
private GlobalRootComponent mRootComponent;
private WMComponent mWMComponent;
private SysUIComponent mSysUIComponent;
- private boolean mInitializeComponents;
+ private InitializationChecker mInitializationChecker;
- public static <T extends SystemUIFactory> T getInstance() {
- return (T) mFactory;
+ public SystemUIInitializer(Context context) {
+ mContext = context;
}
- public static void createFromConfig(Context context) {
- createFromConfig(context, false);
- }
-
- @VisibleForTesting
- public static void createFromConfig(Context context, boolean fromTest) {
- if (mFactory != null) {
- return;
- }
-
- final String clsName = context.getString(R.string.config_systemUIFactoryComponent);
- if (clsName == null || clsName.length() == 0) {
- throw new RuntimeException("No SystemUIFactory component configured");
- }
+ protected abstract GlobalRootComponent.Builder getGlobalRootComponentBuilder();
- try {
- Class<?> cls = null;
- cls = context.getClassLoader().loadClass(clsName);
- mFactory = (SystemUIFactory) cls.newInstance();
- mFactory.init(context, fromTest);
- } catch (Throwable t) {
- Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
- throw new RuntimeException(t);
- }
- }
-
- @VisibleForTesting
- static void cleanup() {
- mFactory = null;
+ /**
+ * Prepares the SysUIComponent builder before it is built.
+ * @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method
+ * @param wm the built WMComponent from the root component's getWMComponent() method
+ */
+ protected SysUIComponent.Builder prepareSysUIComponentBuilder(
+ SysUIComponent.Builder sysUIBuilder, WMComponent wm) {
+ return sysUIBuilder;
}
- public SystemUIFactory() {}
+ /**
+ * Starts the initialization process. This stands up the Dagger graph.
+ */
+ public void init(boolean fromTest) throws ExecutionException, InterruptedException {
+ mRootComponent = getGlobalRootComponentBuilder()
+ .context(mContext)
+ .instrumentationTest(fromTest)
+ .build();
- @VisibleForTesting
- public void init(Context context, boolean fromTest)
- throws ExecutionException, InterruptedException {
- // Only initialize components for the main system ui process running as the primary user
- mInitializeComponents = !fromTest
- && android.os.Process.myUserHandle().isSystem()
- && ActivityThread.currentProcessName().equals(ActivityThread.currentPackageName());
- mRootComponent = buildGlobalRootComponent(context);
+ mInitializationChecker = mRootComponent.getInitializationChecker();
+ boolean initializeComponents = mInitializationChecker.initializeComponents();
// Stand up WMComponent
- setupWmComponent(context);
- if (mInitializeComponents) {
+ setupWmComponent(mContext);
+ if (initializeComponents) {
// Only initialize when not starting from tests since this currently initializes some
// components that shouldn't be run in the test environment
mWMComponent.init();
@@ -109,51 +87,39 @@ public class SystemUIFactory {
// And finally, retrieve whatever SysUI needs from WMShell and build SysUI.
SysUIComponent.Builder builder = mRootComponent.getSysUIComponent();
- if (mInitializeComponents) {
+ if (initializeComponents) {
// Only initialize when not starting from tests since this currently initializes some
// components that shouldn't be run in the test environment
builder = prepareSysUIComponentBuilder(builder, mWMComponent)
+ .setShell(mWMComponent.getShell())
.setPip(mWMComponent.getPip())
- .setLegacySplitScreen(mWMComponent.getLegacySplitScreen())
.setSplitScreen(mWMComponent.getSplitScreen())
.setOneHanded(mWMComponent.getOneHanded())
.setBubbles(mWMComponent.getBubbles())
- .setHideDisplayCutout(mWMComponent.getHideDisplayCutout())
- .setShellCommandHandler(mWMComponent.getShellCommandHandler())
- .setAppPairs(mWMComponent.getAppPairs())
.setTaskViewFactory(mWMComponent.getTaskViewFactory())
.setTransitions(mWMComponent.getTransitions())
.setStartingSurface(mWMComponent.getStartingSurface())
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
- .setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper())
.setRecentTasks(mWMComponent.getRecentTasks())
- .setCompatUI(mWMComponent.getCompatUI())
- .setDragAndDrop(mWMComponent.getDragAndDrop())
.setBackAnimation(mWMComponent.getBackAnimation());
} else {
// TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
// is separating this logic into newly creating SystemUITestsFactory.
builder = prepareSysUIComponentBuilder(builder, mWMComponent)
+ .setShell(new ShellInterface() {})
.setPip(Optional.ofNullable(null))
- .setLegacySplitScreen(Optional.ofNullable(null))
.setSplitScreen(Optional.ofNullable(null))
.setOneHanded(Optional.ofNullable(null))
.setBubbles(Optional.ofNullable(null))
- .setHideDisplayCutout(Optional.ofNullable(null))
- .setShellCommandHandler(Optional.ofNullable(null))
- .setAppPairs(Optional.ofNullable(null))
.setTaskViewFactory(Optional.ofNullable(null))
.setTransitions(new ShellTransitions() {})
.setDisplayAreaHelper(Optional.ofNullable(null))
.setStartingSurface(Optional.ofNullable(null))
- .setTaskSurfaceHelper(Optional.ofNullable(null))
.setRecentTasks(Optional.ofNullable(null))
- .setCompatUI(Optional.ofNullable(null))
- .setDragAndDrop(Optional.ofNullable(null))
.setBackAnimation(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
- if (mInitializeComponents) {
+ if (initializeComponents) {
mSysUIComponent.init();
}
@@ -171,7 +137,8 @@ public class SystemUIFactory {
*/
private void setupWmComponent(Context context) {
WMComponent.Builder wmBuilder = mRootComponent.getWMComponentBuilder();
- if (!mInitializeComponents || !WMShellConcurrencyModule.enableShellMainThread(context)) {
+ if (!mInitializationChecker.initializeComponents()
+ || !WMShellConcurrencyModule.enableShellMainThread(context)) {
// If running under tests or shell thread is not enabled, we don't need anything special
mWMComponent = wmBuilder.build();
return;
@@ -193,26 +160,6 @@ public class SystemUIFactory {
}
}
- /**
- * Prepares the SysUIComponent builder before it is built.
- * @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method
- * @param wm the built WMComponent from the root component's getWMComponent() method
- */
- protected SysUIComponent.Builder prepareSysUIComponentBuilder(
- SysUIComponent.Builder sysUIBuilder, WMComponent wm) {
- return sysUIBuilder;
- }
-
- protected GlobalRootComponent buildGlobalRootComponent(Context context) {
- return DaggerGlobalRootComponent.builder()
- .context(context)
- .build();
- }
-
- protected boolean shouldInitializeComponents() {
- return mInitializeComponents;
- }
-
public GlobalRootComponent getRootComponent() {
return mRootComponent;
}
@@ -226,42 +173,9 @@ public class SystemUIFactory {
}
/**
- * Returns the list of {@link CoreStartable} components that should be started at startup.
- */
- public Map<Class<?>, Provider<CoreStartable>> getStartableComponents() {
- return mSysUIComponent.getStartables();
- }
-
- /**
* Returns the list of additional system UI components that should be started.
*/
public String getVendorComponent(Resources resources) {
return resources.getString(R.string.config_systemUIVendorServiceComponent);
}
-
- /**
- * Returns the list of {@link CoreStartable} components that should be started per user.
- */
- public Map<Class<?>, Provider<CoreStartable>> getStartableComponentsPerUser() {
- return mSysUIComponent.getPerUserStartables();
- }
-
- /**
- * Creates an instance of ScreenshotNotificationSmartActionsProvider.
- * This method is overridden in vendor specific implementation of Sys UI.
- */
- public ScreenshotNotificationSmartActionsProvider
- createScreenshotNotificationSmartActionsProvider(
- Context context, Executor executor, Handler uiHandler) {
- return new ScreenshotNotificationSmartActionsProvider();
- }
-
- /**
- * Creates an instance of BackGestureTfClassifierProvider.
- * This method is overridden in vendor specific implementation of Sys UI.
- */
- public BackGestureTfClassifierProvider createBackGestureTfClassifierProvider(
- AssetManager am, String modelName) {
- return new BackGestureTfClassifierProvider();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt
new file mode 100644
index 000000000000..b9454e8c3be8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.Log
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.util.Assert
+
+/**
+ * Factory to reflectively lookup a [SystemUIInitializer] to start SystemUI with.
+ */
+@Deprecated("Provide your own {@link SystemUIAppComponentFactoryBase} that doesn't need this.")
+object SystemUIInitializerFactory {
+ private const val TAG = "SysUIInitializerFactory"
+ @SuppressLint("StaticFieldLeak")
+ private var initializer: SystemUIInitializer? = null
+
+ /**
+ * Instantiate a [SystemUIInitializer] reflectively.
+ */
+ @JvmStatic
+ fun createWithContext(context: Context): SystemUIInitializer {
+ return createFromConfig(context)
+ }
+
+ /**
+ * Instantiate a [SystemUIInitializer] reflectively.
+ */
+ @JvmStatic
+ private fun createFromConfig(context: Context): SystemUIInitializer {
+ Assert.isMainThread()
+
+ return createFromConfigNoAssert(context)
+ }
+
+ @JvmStatic
+ @VisibleForTesting
+ fun createFromConfigNoAssert(context: Context): SystemUIInitializer {
+
+ return initializer ?: run {
+ val className = context.getString(R.string.config_systemUIFactoryComponent)
+ if (className.isEmpty()) {
+ throw RuntimeException("No SystemUIFactory component configured")
+ }
+ try {
+ val cls = context.classLoader.loadClass(className)
+ val constructor = cls.getConstructor(Context::class.java)
+ (constructor.newInstance(context) as SystemUIInitializer).apply {
+ initializer = this
+ }
+ } catch (t: Throwable) {
+ Log.w(TAG, "Error creating SystemUIInitializer component: $className", t)
+ throw t
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
new file mode 100644
index 000000000000..8920c928da09
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
+
+import android.content.Context
+import com.android.systemui.dagger.DaggerGlobalRootComponent
+import com.android.systemui.dagger.GlobalRootComponent
+
+/**
+ * {@link SystemUIInitializer} that stands up AOSP SystemUI.
+ */
+class SystemUIInitializerImpl(context: Context) : SystemUIInitializer(context) {
+ override fun getGlobalRootComponentBuilder(): GlobalRootComponent.Builder {
+ return DaggerGlobalRootComponent.builder()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index bd8e44ceab80..448b99b6e5d0 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -199,8 +199,9 @@ public class SystemActions extends CoreStartable {
mNotificationShadeController = notificationShadeController;
// Saving in instance variable since to prevent GC since
// NotificationShadeWindowController.registerCallback() only keeps weak references.
- mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing, mDozing) ->
- registerOrUnregisterDismissNotificationShadeAction();
+ mNotificationShadeCallback =
+ (keyguardShowing, keyguardOccluded, bouncerShowing, mDozing, panelExpanded) ->
+ registerOrUnregisterDismissNotificationShadeAction();
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
index d2703f5e73a2..aff0b1fe287c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
@@ -353,6 +353,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout
}
mIsShowing = false;
+ mDragAnimator.cancel();
mWindowManager.removeView(this);
setOnApplyWindowInsetsListener(null);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index bc1c5f4baceb..84e1c3d4c8f0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -90,6 +90,8 @@ public class AuthContainerView extends LinearLayout
private static final int STATE_ANIMATING_OUT = 4;
private static final int STATE_GONE = 5;
+ private static final float BACKGROUND_DIM_AMOUNT = 0.5f;
+
/** Shows biometric prompt dialog animation. */
private static final String SHOW = "show";
/** Dismiss biometric prompt dialog animation. */
@@ -247,13 +249,13 @@ public class AuthContainerView extends LinearLayout
break;
case AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN:
mFailedModalities.clear();
- mConfig.mCallback.onTryAgainPressed();
+ mConfig.mCallback.onTryAgainPressed(getRequestId());
break;
case AuthBiometricView.Callback.ACTION_ERROR:
animateAway(AuthDialogCallback.DISMISSED_ERROR);
break;
case AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL:
- mConfig.mCallback.onDeviceCredentialPressed();
+ mConfig.mCallback.onDeviceCredentialPressed(getRequestId());
mHandler.postDelayed(() -> {
addCredentialView(false /* animatePanel */, true /* animateContents */);
}, mConfig.mSkipAnimation ? 0 : AuthDialog.ANIMATE_CREDENTIAL_START_DELAY_MS);
@@ -371,7 +373,7 @@ public class AuthContainerView extends LinearLayout
void sendEarlyUserCanceled() {
mConfig.mCallback.onSystemEvent(
- BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL);
+ BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL, getRequestId());
}
@Override
@@ -754,6 +756,16 @@ public class AuthContainerView extends LinearLayout
.setDuration(animateDuration)
.setInterpolator(mLinearOutSlowIn)
.setListener(getJankListener(this, DISMISS, animateDuration))
+ .setUpdateListener(animation -> {
+ if (mWindowManager == null || getViewRootImpl() == null) {
+ Log.w(TAG, "skip updateViewLayout() for dim animation.");
+ return;
+ }
+ final WindowManager.LayoutParams lp = getViewRootImpl().mWindowAttributes;
+ lp.dimAmount = (1.0f - (Float) animation.getAnimatedValue())
+ * BACKGROUND_DIM_AMOUNT;
+ mWindowManager.updateViewLayout(this, lp);
+ })
.withLayer()
.start();
});
@@ -762,7 +774,8 @@ public class AuthContainerView extends LinearLayout
private void sendPendingCallbackIfNotNull() {
Log.d(TAG, "pendingCallback: " + mPendingCallbackReason);
if (mPendingCallbackReason != null) {
- mConfig.mCallback.onDismissed(mPendingCallbackReason, mCredentialAttestation);
+ mConfig.mCallback.onDismissed(mPendingCallbackReason,
+ mCredentialAttestation, getRequestId());
mPendingCallbackReason = null;
}
}
@@ -792,7 +805,7 @@ public class AuthContainerView extends LinearLayout
}
mContainerState = STATE_SHOWING;
if (mBiometricView != null) {
- mConfig.mCallback.onDialogAnimatedIn();
+ mConfig.mCallback.onDialogAnimatedIn(getRequestId());
mBiometricView.onDialogAnimatedIn();
}
}
@@ -800,7 +813,8 @@ public class AuthContainerView extends LinearLayout
@VisibleForTesting
static WindowManager.LayoutParams getLayoutParams(IBinder windowToken, CharSequence title) {
final int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
- | WindowManager.LayoutParams.FLAG_SECURE;
+ | WindowManager.LayoutParams.FLAG_SECURE
+ | WindowManager.LayoutParams.FLAG_DIM_BEHIND;
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
@@ -811,6 +825,7 @@ public class AuthContainerView extends LinearLayout
lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~WindowInsets.Type.ime());
lp.setTitle("BiometricPrompt");
lp.accessibilityTitle = title;
+ lp.dimAmount = BACKGROUND_DIM_AMOUNT;
lp.token = windowToken;
return lp;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index a097c5e76149..47ff59cfc281 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -340,11 +340,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
@Override
- public void onTryAgainPressed() {
+ public void onTryAgainPressed(long requestId) {
if (mReceiver == null) {
Log.e(TAG, "onTryAgainPressed: Receiver is null");
return;
}
+
+ if (requestId != mCurrentDialog.getRequestId()) {
+ Log.w(TAG, "requestId doesn't match, skip onTryAgainPressed");
+ return;
+ }
+
try {
mReceiver.onTryAgainPressed();
} catch (RemoteException e) {
@@ -353,11 +359,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
@Override
- public void onDeviceCredentialPressed() {
+ public void onDeviceCredentialPressed(long requestId) {
if (mReceiver == null) {
Log.e(TAG, "onDeviceCredentialPressed: Receiver is null");
return;
}
+
+ if (requestId != mCurrentDialog.getRequestId()) {
+ Log.w(TAG, "requestId doesn't match, skip onDeviceCredentialPressed");
+ return;
+ }
+
try {
mReceiver.onDeviceCredentialPressed();
} catch (RemoteException e) {
@@ -366,11 +378,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
@Override
- public void onSystemEvent(int event) {
+ public void onSystemEvent(int event, long requestId) {
if (mReceiver == null) {
Log.e(TAG, "onSystemEvent(" + event + "): Receiver is null");
return;
}
+
+ if (requestId != mCurrentDialog.getRequestId()) {
+ Log.w(TAG, "requestId doesn't match, skip onSystemEvent");
+ return;
+ }
+
try {
mReceiver.onSystemEvent(event);
} catch (RemoteException e) {
@@ -379,12 +397,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
@Override
- public void onDialogAnimatedIn() {
+ public void onDialogAnimatedIn(long requestId) {
if (mReceiver == null) {
Log.e(TAG, "onDialogAnimatedIn: Receiver is null");
return;
}
+ if (requestId != mCurrentDialog.getRequestId()) {
+ Log.w(TAG, "requestId doesn't match, skip onDialogAnimatedIn");
+ return;
+ }
+
try {
mReceiver.onDialogAnimatedIn();
} catch (RemoteException e) {
@@ -393,7 +416,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
@Override
- public void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation) {
+ public void onDismissed(@DismissedReason int reason,
+ @Nullable byte[] credentialAttestation, long requestId) {
+
+ if (mCurrentDialog != null && requestId != mCurrentDialog.getRequestId()) {
+ Log.w(TAG, "requestId doesn't match, skip onDismissed");
+ return;
+ }
+
switch (reason) {
case AuthDialogCallback.DISMISSED_USER_CANCELED:
sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
index a7d2901b21c3..bbe461aaf6d9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java
@@ -47,27 +47,28 @@ public interface AuthDialogCallback {
* @param reason
* @param credentialAttestation the HAT received from LockSettingsService upon verification
*/
- void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation);
+ void onDismissed(@DismissedReason int reason,
+ @Nullable byte[] credentialAttestation, long requestId);
/**
* Invoked when the "try again" button is clicked
*/
- void onTryAgainPressed();
+ void onTryAgainPressed(long requestId);
/**
* Invoked when the "use password" button is clicked
*/
- void onDeviceCredentialPressed();
+ void onDeviceCredentialPressed(long requestId);
/**
* See {@link android.hardware.biometrics.BiometricPrompt.Builder
* #setReceiveSystemEvents(boolean)}
* @param event
*/
- void onSystemEvent(int event);
+ void onSystemEvent(int event, long requestId);
/**
* Notifies when the dialog has finished animating.
*/
- void onDialogAnimatedIn();
+ void onDialogAnimatedIn(long requestId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 378ae14f0327..fef7383634f0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -29,8 +29,7 @@ import android.view.View
import android.view.animation.PathInterpolator
import com.android.internal.graphics.ColorUtils
import com.android.systemui.animation.Interpolators
-import com.android.systemui.statusbar.charging.DwellRippleShader
-import com.android.systemui.statusbar.charging.RippleShader
+import com.android.systemui.ripple.RippleShader
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
@@ -298,7 +297,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
unlockedRippleInProgress = true
- rippleShader.shouldFadeOutRipple = true
+ rippleShader.rippleFill = false
drawRipple = true
visibility = VISIBLE
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt b/packages/SystemUI/src/com/android/systemui/biometrics/DwellRippleShader.kt
index 236129f8eb50..979fe33fb25b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/DwellRippleShader.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.charging
+package com.android.systemui.biometrics
import android.graphics.PointF
import android.graphics.RuntimeShader
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
index 04e2dccda528..bbffb73b7503 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
@@ -34,16 +34,16 @@ import android.hardware.fingerprint.ISidefpsController
import android.os.Handler
import android.util.Log
import android.util.RotationUtils
-import android.view.View.AccessibilityDelegate
-import android.view.accessibility.AccessibilityEvent
import android.view.Display
import android.view.Gravity
import android.view.LayoutInflater
import android.view.Surface
import android.view.View
+import android.view.View.AccessibilityDelegate
import android.view.ViewPropertyAnimator
import android.view.WindowInsets
import android.view.WindowManager
+import android.view.accessibility.AccessibilityEvent
import androidx.annotation.RawRes
import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieProperty
@@ -70,13 +70,12 @@ class SidefpsController @Inject constructor(
private val activityTaskManager: ActivityTaskManager,
overviewProxyService: OverviewProxyService,
displayManager: DisplayManager,
- @Main mainExecutor: DelayableExecutor,
+ @Main private val mainExecutor: DelayableExecutor,
@Main private val handler: Handler
) {
@VisibleForTesting
val sensorProps: FingerprintSensorPropertiesInternal = fingerprintManager
- ?.sensorPropertiesInternal
- ?.firstOrNull { it.isAnySidefpsType }
+ ?.sideFpsSensorProperties
?: throw IllegalStateException("no side fingerprint sensor")
@VisibleForTesting
@@ -135,25 +134,34 @@ class SidefpsController @Inject constructor(
}
init {
- fingerprintManager?.setSidefpsController(object : ISidefpsController.Stub() {
- override fun show(
- sensorId: Int,
- @BiometricOverlayConstants.ShowReason reason: Int
- ) = if (reason.isReasonToShow(activityTaskManager)) doShow() else hide(sensorId)
-
- private fun doShow() = mainExecutor.execute {
- if (overlayView == null) {
- createOverlayForDisplay()
- } else {
- Log.v(TAG, "overlay already shown")
- }
- }
+ fingerprintManager?.setSidefpsController(
+ object : ISidefpsController.Stub() {
+ override fun show(
+ sensorId: Int,
+ @BiometricOverlayConstants.ShowReason reason: Int
+ ) = if (reason.isReasonToShow(activityTaskManager)) show() else hide()
- override fun hide(sensorId: Int) = mainExecutor.execute { overlayView = null }
- })
+ override fun hide(sensorId: Int) = hide()
+ })
overviewProxyService.addCallback(overviewProxyListener)
}
+ /** Shows the side fps overlay if not already shown. */
+ fun show() {
+ mainExecutor.execute {
+ if (overlayView == null) {
+ createOverlayForDisplay()
+ } else {
+ Log.v(TAG, "overlay already shown")
+ }
+ }
+ }
+
+ /** Hides the fps overlay if shown. */
+ fun hide() {
+ mainExecutor.execute { overlayView = null }
+ }
+
private fun onOrientationChanged() {
if (overlayView != null) {
createOverlayForDisplay()
@@ -266,6 +274,12 @@ class SidefpsController @Inject constructor(
}
}
+private val FingerprintManager?.sideFpsSensorProperties: FingerprintSensorPropertiesInternal?
+ get() = this?.sensorPropertiesInternal?.firstOrNull { it.isAnySidefpsType }
+
+/** Returns [True] when the device has a side fingerprint sensor. */
+fun FingerprintManager?.hasSideFpsSensor(): Boolean = this?.sideFpsSensorProperties != null
+
@BiometricOverlayConstants.ShowReason
private fun Int.isReasonToShow(activityTaskManager: ActivityTaskManager): Boolean = when (this) {
REASON_AUTH_KEYGUARD -> false
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index e5564b7f1f26..cf50f7f8524b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -20,6 +20,7 @@ import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPR
import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.systemui.classifier.Classifier.LOCK_ICON;
import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
import android.annotation.NonNull;
@@ -167,11 +168,16 @@ public class UdfpsController implements DozeReceiver {
private final Set<Callback> mCallbacks = new HashSet<>();
@VisibleForTesting
- public static final VibrationAttributes VIBRATION_ATTRIBUTES =
+ public static final VibrationAttributes UDFPS_VIBRATION_ATTRIBUTES =
new VibrationAttributes.Builder()
// vibration will bypass battery saver mode:
.setUsage(VibrationAttributes.USAGE_COMMUNICATION_REQUEST)
.build();
+ @VisibleForTesting
+ public static final VibrationAttributes LOCK_ICON_VIBRATION_ATTRIBUTES =
+ new VibrationAttributes.Builder()
+ .setUsage(VibrationAttributes.USAGE_TOUCH)
+ .build();
// haptic to use for successful device entry
public static final VibrationEffect EFFECT_CLICK =
@@ -603,7 +609,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull SystemUIDialogManager dialogManager,
@NonNull LatencyTracker latencyTracker,
@NonNull ActivityLaunchAnimator activityLaunchAnimator,
- @NonNull Optional<AlternateUdfpsTouchProvider> aternateTouchProvider,
+ @NonNull Optional<AlternateUdfpsTouchProvider> alternateTouchProvider,
@BiometricsBackground Executor biometricsExecutor) {
mContext = context;
mExecution = execution;
@@ -633,7 +639,7 @@ public class UdfpsController implements DozeReceiver {
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mLatencyTracker = latencyTracker;
mActivityLaunchAnimator = activityLaunchAnimator;
- mAlternateTouchProvider = aternateTouchProvider.orElse(null);
+ mAlternateTouchProvider = alternateTouchProvider.orElse(null);
mBiometricExecutor = biometricsExecutor;
mOrientationListener = new BiometricDisplayListener(
@@ -671,7 +677,7 @@ public class UdfpsController implements DozeReceiver {
mContext.getOpPackageName(),
EFFECT_CLICK,
"udfps-onStart-click",
- VIBRATION_ATTRIBUTES);
+ UDFPS_VIBRATION_ATTRIBUTES);
}
}
@@ -748,7 +754,19 @@ public class UdfpsController implements DozeReceiver {
}
if (!mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
+ if (mFalsingManager.isFalseTouch(LOCK_ICON)) {
+ Log.v(TAG, "aod lock icon long-press rejected by the falsing manager.");
+ return;
+ }
mKeyguardViewManager.showBouncer(true);
+
+ // play the same haptic as the LockIconViewController longpress
+ mVibrator.vibrate(
+ Process.myUid(),
+ mContext.getOpPackageName(),
+ UdfpsController.EFFECT_CLICK,
+ "aod-lock-icon-longpress",
+ LOCK_ICON_VIBRATION_ATTRIBUTES);
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index ec4cf2fd8bd4..24b893340ae0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -510,6 +510,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
mKeyguardViewManager.isBouncerInTransit() ? BouncerPanelExpansionCalculator
.aboutToShowBouncerProgress(fraction) : fraction;
updateAlpha();
+ updatePauseAuth();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java
new file mode 100644
index 000000000000..9b7d49883222
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java
@@ -0,0 +1,134 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.bluetooth;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
+import com.android.systemui.media.MediaDataUtils;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+/**
+ * Dialog for showing le audio broadcasting dialog.
+ */
+public class BroadcastDialog extends SystemUIDialog {
+
+ private static final String TAG = "BroadcastDialog";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private Context mContext;
+ private UiEventLogger mUiEventLogger;
+ @VisibleForTesting
+ protected View mDialogView;
+ private MediaOutputDialogFactory mMediaOutputDialogFactory;
+ private String mSwitchBroadcastApp;
+ private String mOutputPackageName;
+
+ public BroadcastDialog(Context context, MediaOutputDialogFactory mediaOutputDialogFactory,
+ String switchBroadcastApp, String outputPkgName, UiEventLogger uiEventLogger) {
+ super(context);
+ if (DEBUG) {
+ Log.d(TAG, "Init BroadcastDialog");
+ }
+
+ mContext = getContext();
+ mMediaOutputDialogFactory = mediaOutputDialogFactory;
+ mSwitchBroadcastApp = switchBroadcastApp;
+ mOutputPackageName = outputPkgName;
+ mUiEventLogger = uiEventLogger;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (DEBUG) {
+ Log.d(TAG, "onCreate");
+ }
+
+ mUiEventLogger.log(BroadcastDialogEvent.BROADCAST_DIALOG_SHOW);
+ mDialogView = LayoutInflater.from(mContext).inflate(R.layout.broadcast_dialog, null);
+ final Window window = getWindow();
+ window.setContentView(mDialogView);
+
+ TextView title = mDialogView.requireViewById(R.id.dialog_title);
+ TextView subTitle = mDialogView.requireViewById(R.id.dialog_subtitle);
+ title.setText(
+ mContext.getString(R.string.bt_le_audio_broadcast_dialog_title,
+ MediaDataUtils.getAppLabel(mContext, mOutputPackageName,
+ mContext.getString(
+ R.string.bt_le_audio_broadcast_dialog_unknown_name))));
+ subTitle.setText(
+ mContext.getString(R.string.bt_le_audio_broadcast_dialog_sub_title,
+ mSwitchBroadcastApp));
+
+ Button switchBroadcast = mDialogView.requireViewById(R.id.switch_broadcast);
+ Button changeOutput = mDialogView.requireViewById(R.id.change_output);
+ Button cancelBtn = mDialogView.requireViewById(R.id.cancel);
+ switchBroadcast.setText(mContext.getString(
+ R.string.bt_le_audio_broadcast_dialog_switch_app, mSwitchBroadcastApp), null);
+ changeOutput.setOnClickListener((view) -> {
+ mMediaOutputDialogFactory.create(mOutputPackageName, true, null);
+ dismiss();
+ });
+ cancelBtn.setOnClickListener((view) -> {
+ if (DEBUG) {
+ Log.d(TAG, "BroadcastDialog dismiss.");
+ }
+ dismiss();
+ });
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ if (!hasFocus && isShowing()) {
+ dismiss();
+ }
+ }
+
+ public enum BroadcastDialogEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "The Broadcast dialog became visible on the screen.")
+ BROADCAST_DIALOG_SHOW(1062);
+
+ private final int mId;
+
+ BroadcastDialogEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogController.java b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogController.java
new file mode 100644
index 000000000000..8a54345ae28c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogController.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.bluetooth;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
+
+import javax.inject.Inject;
+
+/**
+ * Controller to create BroadcastDialog objects.
+ */
+@SysUISingleton
+public class BroadcastDialogController {
+
+ private Context mContext;
+ private UiEventLogger mUiEventLogger;
+ private DialogLaunchAnimator mDialogLaunchAnimator;
+ private MediaOutputDialogFactory mMediaOutputDialogFactory;
+
+ @Inject
+ public BroadcastDialogController(Context context, UiEventLogger uiEventLogger,
+ DialogLaunchAnimator dialogLaunchAnimator,
+ MediaOutputDialogFactory mediaOutputDialogFactory) {
+ mContext = context;
+ mUiEventLogger = uiEventLogger;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
+ mMediaOutputDialogFactory = mediaOutputDialogFactory;
+ }
+
+ public void createBroadcastDialog(String switchAppName, String outputPkgName,
+ boolean aboveStatusBar, View view) {
+ BroadcastDialog broadcastDialog = new BroadcastDialog(mContext, mMediaOutputDialogFactory,
+ switchAppName, outputPkgName, mUiEventLogger);
+ if (view != null) {
+ mDialogLaunchAnimator.showFromView(broadcastDialog, view);
+ } else {
+ broadcastDialog.show();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
new file mode 100644
index 000000000000..cccd3a482ef0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.camera
+
+import android.app.ActivityManager
+import android.app.ActivityOptions
+import android.app.IActivityTaskManager
+import android.content.ContentResolver
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.os.RemoteException
+import android.os.UserHandle
+import android.util.Log
+import android.view.WindowManager
+import androidx.annotation.VisibleForTesting
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.shared.system.ActivityManagerKt.isInForeground
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.PanelViewController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Helps with handling camera-related gestures (for example, double-tap the power button to launch
+ * the camera).
+ */
+class CameraGestureHelper @Inject constructor(
+ private val context: Context,
+ private val centralSurfaces: CentralSurfaces,
+ private val keyguardStateController: KeyguardStateController,
+ private val packageManager: PackageManager,
+ private val activityManager: ActivityManager,
+ private val activityStarter: ActivityStarter,
+ private val activityIntentHelper: ActivityIntentHelper,
+ private val activityTaskManager: IActivityTaskManager,
+ private val cameraIntents: CameraIntentsWrapper,
+ private val contentResolver: ContentResolver,
+ @Main private val uiExecutor: Executor,
+) {
+ /**
+ * Whether the camera application can be launched for the camera launch gesture.
+ */
+ fun canCameraGestureBeLaunched(statusBarState: Int): Boolean {
+ if (!centralSurfaces.isCameraAllowedByAdmin) {
+ return false
+ }
+
+ val resolveInfo: ResolveInfo? = packageManager.resolveActivityAsUser(
+ getStartCameraIntent(),
+ PackageManager.MATCH_DEFAULT_ONLY,
+ KeyguardUpdateMonitor.getCurrentUser()
+ )
+ val resolvedPackage = resolveInfo?.activityInfo?.packageName
+ return (resolvedPackage != null &&
+ (statusBarState != StatusBarState.SHADE ||
+ !activityManager.isInForeground(resolvedPackage)))
+ }
+
+ /**
+ * Launches the camera.
+ *
+ * @param source The source of the camera launch, to be passed to the camera app via [Intent]
+ */
+ fun launchCamera(source: Int) {
+ val intent: Intent = getStartCameraIntent()
+ intent.putExtra(EXTRA_CAMERA_LAUNCH_SOURCE, source)
+ val wouldLaunchResolverActivity = activityIntentHelper.wouldLaunchResolverActivity(
+ intent, KeyguardUpdateMonitor.getCurrentUser()
+ )
+ if (CameraIntents.isSecureCameraIntent(intent) && !wouldLaunchResolverActivity) {
+ uiExecutor.execute {
+ // Normally an activity will set its requested rotation animation on its window.
+ // However when launching an activity causes the orientation to change this is too
+ // late. In these cases, the default animation is used. This doesn't look good for
+ // the camera (as it rotates the camera contents out of sync with physical reality).
+ // Therefore, we ask the WindowManager to force the cross-fade animation if an
+ // orientation change happens to occur during the launch.
+ val activityOptions = ActivityOptions.makeBasic()
+ activityOptions.setDisallowEnterPictureInPictureWhileLaunching(true)
+ activityOptions.rotationAnimationHint =
+ WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
+ try {
+ activityTaskManager.startActivityAsUser(
+ null,
+ context.basePackageName,
+ context.attributionTag,
+ intent,
+ intent.resolveTypeIfNeeded(contentResolver),
+ null,
+ null,
+ 0,
+ Intent.FLAG_ACTIVITY_NEW_TASK,
+ null,
+ activityOptions.toBundle(),
+ UserHandle.CURRENT.identifier,
+ )
+ } catch (e: RemoteException) {
+ Log.w(
+ PanelViewController.TAG,
+ "Unable to start camera activity",
+ e
+ )
+ }
+ }
+ } else {
+ // We need to delay starting the activity because ResolverActivity finishes itself if
+ // launched from behind the lock-screen.
+ activityStarter.startActivity(intent, false /* dismissShade */)
+ }
+
+ // Call this to make sure that the keyguard returns if the app that is being launched
+ // crashes after a timeout.
+ centralSurfaces.startLaunchTransitionTimeout()
+ // Call this to make sure the keyguard is ready to be dismissed once the next intent is
+ // handled by the OS (in our case it is the activity we started right above)
+ centralSurfaces.readyForKeyguardDone()
+ }
+
+ /**
+ * Returns an [Intent] that can be used to start the camera app such that it occludes the
+ * lock-screen, if needed.
+ */
+ private fun getStartCameraIntent(): Intent {
+ val isLockScreenDismissible = keyguardStateController.canDismissLockScreen()
+ val isSecure = keyguardStateController.isMethodSecure
+ return if (isSecure && !isLockScreenDismissible) {
+ cameraIntents.getSecureCameraIntent()
+ } else {
+ cameraIntents.getInsecureCameraIntent()
+ }
+ }
+
+ companion object {
+ @VisibleForTesting
+ const val EXTRA_CAMERA_LAUNCH_SOURCE = "com.android.systemui.camera_launch_source"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt
new file mode 100644
index 000000000000..cf02f8fb4a3c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.camera
+
+import android.content.Context
+import android.content.Intent
+import javax.inject.Inject
+
+/** Injectable wrapper around [CameraIntents]. */
+class CameraIntentsWrapper @Inject constructor(
+ private val context: Context,
+) {
+
+ /**
+ * Returns an [Intent] that can be used to start the camera, suitable for when the device is
+ * already unlocked
+ */
+ fun getSecureCameraIntent(): Intent {
+ return CameraIntents.getSecureCameraIntent(context)
+ }
+
+ /**
+ * Returns an [Intent] that can be used to start the camera, suitable for when the device is not
+ * already unlocked
+ */
+ fun getInsecureCameraIntent(): Intent {
+ return CameraIntents.getInsecureCameraIntent(context)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index 5df593b64c24..8292e52b1ffd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.charging
+package com.android.systemui.charging
import android.content.Context
import android.content.res.Configuration
@@ -32,6 +32,7 @@ import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.ripple.RippleView
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
@@ -61,7 +62,7 @@ class WiredChargingRippleController @Inject constructor(
private val systemClock: SystemClock,
private val uiEventLogger: UiEventLogger
) {
- private var pluggedIn: Boolean? = null
+ private var pluggedIn: Boolean = false
private val rippleEnabled: Boolean = featureFlags.isEnabled(Flags.CHARGING_RIPPLE) &&
!SystemProperties.getBoolean("persist.debug.suppress-charging-ripple", false)
private var normalizedPortPosX: Float = context.resources.getFloat(
@@ -84,7 +85,7 @@ class WiredChargingRippleController @Inject constructor(
private var debounceLevel = 0
@VisibleForTesting
- var rippleView: ChargingRippleView = ChargingRippleView(context, attrs = null)
+ var rippleView: RippleView = RippleView(context, attrs = null)
init {
pluggedIn = batteryController.isPluggedIn
@@ -99,15 +100,17 @@ class WiredChargingRippleController @Inject constructor(
nowPluggedIn: Boolean,
charging: Boolean
) {
- // Suppresses the ripple when the state change comes from wireless charging.
- if (batteryController.isPluggedInWireless) {
+ // Suppresses the ripple when the state change comes from wireless charging or
+ // its dock.
+ if (batteryController.isPluggedInWireless ||
+ batteryController.isChargingSourceDock) {
return
}
- val wasPluggedIn = pluggedIn
- pluggedIn = nowPluggedIn
- if ((wasPluggedIn == null || !wasPluggedIn) && nowPluggedIn) {
+
+ if (!pluggedIn && nowPluggedIn) {
startRippleWithDebounce()
}
+ pluggedIn = nowPluggedIn
}
}
batteryController.addCallback(batteryStateChangeCallback)
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index 0d3e2ae9a776..f6368ee19e8f 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -34,7 +34,7 @@ import android.widget.TextView;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
-import com.android.systemui.statusbar.charging.ChargingRippleView;
+import com.android.systemui.ripple.RippleView;
import java.text.NumberFormat;
@@ -46,7 +46,7 @@ public class WirelessChargingLayout extends FrameLayout {
private static final long RIPPLE_ANIMATION_DURATION = 1500;
private static final int SCRIM_COLOR = 0x4C000000;
private static final int SCRIM_FADE_DURATION = 300;
- private ChargingRippleView mRippleView;
+ private RippleView mRippleView;
public WirelessChargingLayout(Context context) {
super(context);
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
index 3f78f97ba563..1fa9ac574c7e 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
@@ -74,6 +74,7 @@ public class EditTextActivity extends Activity
}
mEditText.setText(clip.getItemAt(0).getText());
mEditText.requestFocus();
+ mEditText.setSelection(0);
mSensitive = clip.getDescription().getExtras() != null
&& clip.getDescription().getExtras()
.getBoolean(ClipDescription.EXTRA_IS_SENSITIVE);
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index bed553e6e4d6..50ce9d4ec0f4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -20,13 +20,11 @@ import android.app.PendingIntent
import android.app.backup.BackupManager
import android.content.BroadcastReceiver
import android.content.ComponentName
-import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.database.ContentObserver
import android.net.Uri
-import android.os.Environment
import android.os.UserHandle
import android.service.controls.Control
import android.service.controls.actions.ControlAction
@@ -43,6 +41,7 @@ import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
+import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_FILE
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_SEEDING_COMPLETED
@@ -61,6 +60,7 @@ class ControlsControllerImpl @Inject constructor (
private val bindingController: ControlsBindingController,
private val listingController: ControlsListingController,
private val broadcastDispatcher: BroadcastDispatcher,
+ private val userFileManager: UserFileManager,
optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>,
dumpManager: DumpManager,
userTracker: UserTracker
@@ -84,15 +84,12 @@ class ControlsControllerImpl @Inject constructor (
override val currentUserId
get() = currentUser.identifier
- private val contentResolver: ContentResolver
- get() = context.contentResolver
-
private val persistenceWrapper: ControlsFavoritePersistenceWrapper
@VisibleForTesting
internal var auxiliaryPersistenceWrapper: AuxiliaryPersistenceWrapper
init {
- userStructure = UserStructure(context, currentUser)
+ userStructure = UserStructure(context, currentUser, userFileManager)
persistenceWrapper = optionalWrapper.orElseGet {
ControlsFavoritePersistenceWrapper(
@@ -111,7 +108,7 @@ class ControlsControllerImpl @Inject constructor (
private fun setValuesForUser(newUser: UserHandle) {
Log.d(TAG, "Changing to user: $newUser")
currentUser = newUser
- userStructure = UserStructure(context, currentUser)
+ userStructure = UserStructure(context, currentUser, userFileManager)
persistenceWrapper.changeFileAndBackupManager(
userStructure.file,
BackupManager(userStructure.userContext)
@@ -187,8 +184,11 @@ class ControlsControllerImpl @Inject constructor (
// When a component is uninstalled, allow seeding to happen again if the user
// reinstalls the app
- val prefs = userStructure.userContext.getSharedPreferences(
- PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
+ val prefs = userFileManager.getSharedPreferences(
+ PREFS_CONTROLS_FILE,
+ Context.MODE_PRIVATE,
+ userTracker.userId
+ )
val completedSeedingPackageSet = prefs.getStringSet(
PREFS_CONTROLS_SEEDING_COMPLETED, mutableSetOf<String>())
val servicePackageSet = serviceInfoSet.map { it.packageName }
@@ -575,18 +575,12 @@ class ControlsControllerImpl @Inject constructor (
}
}
-class UserStructure(context: Context, user: UserHandle) {
+class UserStructure(context: Context, user: UserHandle, userFileManager: UserFileManager) {
val userContext = context.createContextAsUser(user, 0)
-
- val file = Environment.buildPath(
- userContext.filesDir,
- ControlsFavoritePersistenceWrapper.FILE_NAME
- )
-
- val auxiliaryFile = Environment.buildPath(
- userContext.filesDir,
- AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME
- )
+ val file = userFileManager.getFile(ControlsFavoritePersistenceWrapper.FILE_NAME,
+ user.identifier)
+ val auxiliaryFile = userFileManager.getFile(AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME,
+ user.identifier)
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
index 0bc6579739db..a174ed0312c8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
@@ -28,6 +28,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import com.android.systemui.R
import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.util.icuMessageFormat
import java.text.Collator
import java.util.concurrent.Executor
@@ -74,8 +75,10 @@ class AppAdapter(
}
override fun onCreateViewHolder(parent: ViewGroup, i: Int): Holder {
- return Holder(layoutInflater.inflate(R.layout.controls_app_item, parent, false),
- favoritesRenderer)
+ return Holder(
+ layoutInflater.inflate(R.layout.controls_app_item, parent, false),
+ favoritesRenderer
+ )
}
override fun getItemCount() = listOfServices.size
@@ -116,10 +119,10 @@ class FavoritesRenderer(
fun renderFavoritesForComponent(component: ComponentName): String? {
val qty = favoriteFunction(component)
- if (qty != 0) {
- return resources.getQuantityString(R.plurals.controls_number_of_favorites, qty, qty)
+ return if (qty != 0) {
+ icuMessageFormat(resources, R.string.controls_number_of_favorites, qty)
} else {
- return null
+ null
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index 0cf3333d12a6..8ba6f1c4a411 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -18,6 +18,8 @@ package com.android.systemui.dagger;
import android.content.BroadcastReceiver;
+import com.android.systemui.GuestResetOrExitSessionReceiver;
+import com.android.systemui.GuestResumeSessionReceiver;
import com.android.systemui.media.dialog.MediaOutputDialogReceiver;
import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
@@ -89,4 +91,21 @@ public abstract class DefaultBroadcastReceiverBinder {
public abstract BroadcastReceiver bindPeopleSpaceWidgetProvider(
PeopleSpaceWidgetProvider broadcastReceiver);
+ /**
+ *
+ */
+ @Binds
+ @IntoMap
+ @ClassKey(GuestResumeSessionReceiver.class)
+ public abstract BroadcastReceiver bindGuestResumeSessionReceiver(
+ GuestResumeSessionReceiver broadcastReceiver);
+
+ /**
+ *
+ */
+ @Binds
+ @IntoMap
+ @ClassKey(GuestResetOrExitSessionReceiver.class)
+ public abstract BroadcastReceiver bindGuestResetOrExitSessionReceiver(
+ GuestResetOrExitSessionReceiver broadcastReceiver);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index cd8ca0553add..4096ed4283e5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -42,9 +42,11 @@ import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
+import android.content.res.AssetManager;
import android.content.res.Resources;
import android.hardware.SensorManager;
import android.hardware.SensorPrivacyManager;
+import android.hardware.camera2.CameraManager;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.ColorDisplayManager;
@@ -90,6 +92,7 @@ import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.LatencyTracker;
import com.android.systemui.Prefs;
+import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.TestHarness;
@@ -404,6 +407,12 @@ public class FrameworkServicesModule {
}
@Provides
+ @Application
+ static AssetManager provideAssetManager(@Application Context context) {
+ return context.getAssets();
+ }
+
+ @Provides
@Singleton
static RoleManager provideRoleManager(Context context) {
return context.getSystemService(RoleManager.class);
@@ -553,4 +562,10 @@ public class FrameworkServicesModule {
static SafetyCenterManager provideSafetyCenterManager(Context context) {
return context.getSystemService(SafetyCenterManager.class);
}
+
+ @Provides
+ @Singleton
+ static CameraManager provideCameraManager(Context context) {
+ return context.getSystemService(CameraManager.class);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index 4f55ba496b6b..9e33ee1faab3 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -18,6 +18,9 @@ package com.android.systemui.dagger;
import android.content.Context;
+import com.android.systemui.dagger.qualifiers.InstrumentationTest;
+import com.android.systemui.util.InitializationChecker;
+
import javax.inject.Singleton;
import dagger.BindsInstance;
@@ -37,7 +40,8 @@ public interface GlobalRootComponent {
interface Builder {
@BindsInstance
Builder context(Context context);
-
+ @BindsInstance
+ Builder instrumentationTest(@InstrumentationTest boolean test);
GlobalRootComponent build();
}
@@ -50,4 +54,9 @@ public interface GlobalRootComponent {
* Builder for a {@link SysUIComponent}, which makes it a subcomponent of this class.
*/
SysUIComponent.Builder getSysUIComponent();
+
+ /**
+ * Returns an {@link InitializationChecker}.
+ */
+ InitializationChecker getInitializationChecker();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 4e48a5261f6b..2c1463d285f6 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -35,6 +35,7 @@ import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.media.dagger.MediaModule;
+import com.android.systemui.navigationbar.gestural.GestureModule;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
@@ -43,6 +44,7 @@ import com.android.systemui.qs.dagger.QSModule;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImplementation;
+import com.android.systemui.screenshot.ReferenceScreenshotModule;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
@@ -94,9 +96,11 @@ import dagger.Provides;
* SystemUI code that variants of SystemUI _must_ include to function correctly.
*/
@Module(includes = {
+ GestureModule.class,
MediaModule.class,
PowerModule.class,
QSModule.class,
+ ReferenceScreenshotModule.class,
StartCentralSurfacesModule.class,
VolumeModule.class
})
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 5d34a6987b66..029cabb0bc0b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -21,7 +21,7 @@ import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
import com.android.systemui.InitController;
-import com.android.systemui.SystemUIAppComponentFactory;
+import com.android.systemui.SystemUIAppComponentFactoryBase;
import com.android.systemui.dagger.qualifiers.PerUser;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
@@ -37,22 +37,16 @@ import com.android.systemui.unfold.FoldStateLoggingProvider;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.unfold.UnfoldLatencyTracker;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
-import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.TaskViewFactory;
-import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
-import com.android.wm.shell.compatui.CompatUI;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
-import com.android.wm.shell.draganddrop.DragAndDrop;
-import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
-import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
+import com.android.wm.shell.sysui.ShellInterface;
import com.android.wm.shell.transition.ShellTransitions;
import java.util.Map;
@@ -83,18 +77,15 @@ public interface SysUIComponent {
@Subcomponent.Builder
interface Builder {
@BindsInstance
- Builder setPip(Optional<Pip> p);
+ Builder setShell(ShellInterface s);
@BindsInstance
- Builder setLegacySplitScreen(Optional<LegacySplitScreen> s);
+ Builder setPip(Optional<Pip> p);
@BindsInstance
Builder setSplitScreen(Optional<SplitScreen> s);
@BindsInstance
- Builder setAppPairs(Optional<AppPairs> s);
-
- @BindsInstance
Builder setOneHanded(Optional<OneHanded> o);
@BindsInstance
@@ -104,12 +95,6 @@ public interface SysUIComponent {
Builder setTaskViewFactory(Optional<TaskViewFactory> t);
@BindsInstance
- Builder setHideDisplayCutout(Optional<HideDisplayCutout> h);
-
- @BindsInstance
- Builder setShellCommandHandler(Optional<ShellCommandHandler> shellDump);
-
- @BindsInstance
Builder setTransitions(ShellTransitions t);
@BindsInstance
@@ -119,18 +104,9 @@ public interface SysUIComponent {
Builder setDisplayAreaHelper(Optional<DisplayAreaHelper> h);
@BindsInstance
- Builder setTaskSurfaceHelper(Optional<TaskSurfaceHelper> t);
-
- @BindsInstance
Builder setRecentTasks(Optional<RecentTasks> r);
@BindsInstance
- Builder setCompatUI(Optional<CompatUI> s);
-
- @BindsInstance
- Builder setDragAndDrop(Optional<DragAndDrop> d);
-
- @BindsInstance
Builder setBackAnimation(Optional<BackAnimation> b);
SysUIComponent build();
@@ -249,7 +225,7 @@ public interface SysUIComponent {
/**
* Member injection into the supplied argument.
*/
- void inject(SystemUIAppComponentFactory factory);
+ void inject(SystemUIAppComponentFactoryBase factory);
/**
* Member injection into the supplied argument.
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index a9f340854689..6db3e82a77b0 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -33,6 +33,7 @@ import com.android.systemui.log.SessionTracker
import com.android.systemui.media.RingtonePlayer
import com.android.systemui.power.PowerUI
import com.android.systemui.recents.Recents
+import com.android.systemui.settings.dagger.MultiUserUtilsModule
import com.android.systemui.shortcut.ShortcutKeyDispatcher
import com.android.systemui.statusbar.notification.InstantAppNotifier
import com.android.systemui.statusbar.phone.KeyguardLiftController
@@ -51,7 +52,7 @@ import dagger.multibindings.IntoMap
/**
* Collection of {@link CoreStartable}s that should be run on AOSP.
*/
-@Module
+@Module(includes = [MultiUserUtilsModule::class])
abstract class SystemUICoreStartableModule {
/** Inject into AuthController. */
@Binds
@@ -205,4 +206,4 @@ abstract class SystemUICoreStartableModule {
@IntoMap
@ClassKey(KeyguardLiftController::class)
abstract fun bindKeyguardLiftController(sysui: KeyguardLiftController): CoreStartable
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 366ef2651a92..fe9622250e67 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -26,7 +26,6 @@ import com.android.keyguard.clock.ClockModule;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.BootCompleteCache;
import com.android.systemui.BootCompleteCacheImpl;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
@@ -43,20 +42,20 @@ import com.android.systemui.flags.FlagsModule;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.lowlightclock.LowLightClockController;
+import com.android.systemui.media.dagger.MediaProjectionModule;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarComponent;
+import com.android.systemui.people.PeopleModule;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import com.android.systemui.privacy.PrivacyModule;
import com.android.systemui.recents.Recents;
import com.android.systemui.screenshot.dagger.ScreenshotModule;
-import com.android.systemui.settings.dagger.SettingsModule;
+import com.android.systemui.settings.dagger.MultiUserUtilsModule;
import com.android.systemui.smartspace.dagger.SmartspaceModule;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.QsFrameTranslateModule;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
@@ -83,6 +82,7 @@ import com.android.systemui.unfold.SysUIUnfoldModule;
import com.android.systemui.user.UserModule;
import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
import com.android.systemui.util.dagger.UtilModule;
+import com.android.systemui.util.kotlin.CoroutinesModule;
import com.android.systemui.util.sensors.SensorModule;
import com.android.systemui.util.settings.SettingsUtilModule;
import com.android.systemui.util.time.SystemClock;
@@ -91,6 +91,7 @@ import com.android.systemui.wallet.dagger.WalletModule;
import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.dagger.DynamicOverride;
+import com.android.wm.shell.sysui.ShellController;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -115,19 +116,22 @@ import dagger.Provides;
AssistModule.class,
BiometricsModule.class,
ClockModule.class,
+ CoroutinesModule.class,
DreamModule.class,
ControlsModule.class,
DemoModeModule.class,
FalsingModule.class,
FlagsModule.class,
LogModule.class,
+ MediaProjectionModule.class,
PeopleHubModule.class,
+ PeopleModule.class,
PluginModule.class,
PrivacyModule.class,
QsFrameTranslateModule.class,
ScreenshotModule.class,
SensorModule.class,
- SettingsModule.class,
+ MultiUserUtilsModule.class,
SettingsUtilModule.class,
SmartRepliesInflationModule.class,
SmartspaceModule.class,
@@ -198,11 +202,6 @@ public abstract class SystemUIModule {
@Binds
abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
- @Provides
- static SystemUIFactory getSystemUIFactory() {
- return SystemUIFactory.getInstance();
- }
-
// TODO: This should provided by the WM component
/** Provides Optional of BubbleManager */
@SysUISingleton
@@ -212,7 +211,6 @@ public abstract class SystemUIModule {
NotificationShadeWindowController notificationShadeWindowController,
KeyguardStateController keyguardStateController,
ShadeController shadeController,
- ConfigurationController configurationController,
@Nullable IStatusBarService statusBarService,
INotificationManager notificationManager,
NotificationVisibilityProvider visibilityProvider,
@@ -220,11 +218,9 @@ public abstract class SystemUIModule {
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManagerLegacy groupManager,
- NotificationEntryManager entryManager,
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
- NotifPipelineFlags notifPipelineFlags,
DumpManager dumpManager,
@Main Executor sysuiMainExecutor) {
return Optional.ofNullable(BubblesManager.create(context,
@@ -232,7 +228,6 @@ public abstract class SystemUIModule {
notificationShadeWindowController,
keyguardStateController,
shadeController,
- configurationController,
statusBarService,
notificationManager,
visibilityProvider,
@@ -240,11 +235,9 @@ public abstract class SystemUIModule {
zenModeController,
notifUserManager,
groupManager,
- entryManager,
notifCollection,
notifPipeline,
sysUiState,
- notifPipelineFlags,
dumpManager,
sysuiMainExecutor));
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index b02074a65e9a..78a45f9d3310 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -21,29 +21,24 @@ import android.os.HandlerThread;
import androidx.annotation.Nullable;
-import com.android.systemui.SystemUIFactory;
+import com.android.systemui.SystemUIInitializerFactory;
import com.android.systemui.tv.TvWMComponent;
-import com.android.wm.shell.ShellCommandHandler;
-import com.android.wm.shell.ShellInit;
+import com.android.wm.shell.sysui.ShellCommandHandler;
+import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.TaskViewFactory;
-import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.compatui.CompatUI;
import com.android.wm.shell.dagger.TvWMShellModule;
import com.android.wm.shell.dagger.WMShellModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
-import com.android.wm.shell.draganddrop.DragAndDrop;
-import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
-import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
+import com.android.wm.shell.sysui.ShellInterface;
import com.android.wm.shell.transition.ShellTransitions;
import java.util.Optional;
@@ -54,7 +49,7 @@ import dagger.Subcomponent;
/**
* Dagger Subcomponent for WindowManager. This class explicitly describes the interfaces exported
* from the WM component into the SysUI component (in
- * {@link SystemUIFactory#init(Context, boolean)}), and references the specific dependencies
+ * {@link SystemUIInitializerFactory#init(Context, boolean)}), and references the specific dependencies
* provided by its particular device/form-factor SystemUI implementation.
*
* ie. {@link WMComponent} includes {@link WMShellModule}
@@ -80,14 +75,24 @@ public interface WMComponent {
* Initializes all the WMShell components before starting any of the SystemUI components.
*/
default void init() {
- getShellInit().init();
+ // TODO(238217847): To be removed once the dependencies are inverted and ShellController can
+ // inject these classes directly, otherwise, it's currently needed to ensure that these
+ // classes are created and set on the controller before onInit() is called
+ getShellInit();
+ getShellCommandHandler();
+ getShell().onInit();
}
@WMSingleton
+ ShellInterface getShell();
+
+ // TODO(238217847): To be removed once ShellController can inject ShellInit directly
+ @WMSingleton
ShellInit getShellInit();
+ // TODO(238217847): To be removed once ShellController can inject ShellCommandHandler directly
@WMSingleton
- Optional<ShellCommandHandler> getShellCommandHandler();
+ ShellCommandHandler getShellCommandHandler();
@WMSingleton
Optional<OneHanded> getOneHanded();
@@ -96,21 +101,12 @@ public interface WMComponent {
Optional<Pip> getPip();
@WMSingleton
- Optional<LegacySplitScreen> getLegacySplitScreen();
-
- @WMSingleton
Optional<SplitScreen> getSplitScreen();
@WMSingleton
- Optional<AppPairs> getAppPairs();
-
- @WMSingleton
Optional<Bubbles> getBubbles();
@WMSingleton
- Optional<HideDisplayCutout> getHideDisplayCutout();
-
- @WMSingleton
Optional<TaskViewFactory> getTaskViewFactory();
@WMSingleton
@@ -123,17 +119,8 @@ public interface WMComponent {
Optional<DisplayAreaHelper> getDisplayAreaHelper();
@WMSingleton
- Optional<TaskSurfaceHelper> getTaskSurfaceHelper();
-
- @WMSingleton
Optional<RecentTasks> getRecentTasks();
@WMSingleton
- Optional<CompatUI> getCompatUI();
-
- @WMSingleton
- Optional<DragAndDrop> getDragAndDrop();
-
- @WMSingleton
Optional<BackAnimation> getBackAnimation();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDrop.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/InstrumentationTest.java
index edeff6e37182..a803a39f114f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDrop.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/InstrumentationTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,21 +14,21 @@
* limitations under the License.
*/
-package com.android.wm.shell.draganddrop;
+package com.android.systemui.dagger.qualifiers;
-import android.content.res.Configuration;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
-import com.android.wm.shell.common.annotations.ExternalThread;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
-/**
- * Interface for telling DragAndDrop stuff.
- */
-@ExternalThread
-public interface DragAndDrop {
+import javax.inject.Qualifier;
- /** Called when the theme changes. */
- void onThemeChanged();
- /** Called when the configuration changes. */
- void onConfigChanged(Configuration newConfig);
+/**
+ * An annotation for injecting whether or not we are running in a test environment.
+ */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface InstrumentationTest {
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 19287c133651..4161cf6d2657 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -332,6 +332,20 @@ public class DozeLog implements Dumpable {
}
/**
+ * Logs the car mode started event.
+ */
+ public void traceCarModeStarted() {
+ mLogger.logCarModeStarted();
+ }
+
+ /**
+ * Logs the car mode ended event.
+ */
+ public void traceCarModeEnded() {
+ mLogger.logCarModeEnded();
+ }
+
+ /**
* Appends power save changes that may cause a new doze state
* @param powerSaveActive true if power saving is active
* @param nextState the state that we'll transition to
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index 4c81563e4f93..4b279ec8f008 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -105,7 +105,7 @@ class DozeLogger @Inject constructor(
bool4 = screenOnFromTouch
}, {
"Fling expand=$bool1 aboveThreshold=$bool2 thresholdNeeded=$bool3 " +
- "screenOnFromTouch=$bool4"
+ "screenOnFromTouch=$bool4"
})
}
@@ -151,7 +151,7 @@ class DozeLogger @Inject constructor(
long2 = triggerAt
}, {
"Time tick scheduledAt=${DATE_FORMAT.format(Date(long1))} " +
- "triggerAt=${DATE_FORMAT.format(Date(long2))}"
+ "triggerAt=${DATE_FORMAT.format(Date(long2))}"
})
}
@@ -220,7 +220,7 @@ class DozeLogger @Inject constructor(
str1 = partUpdated
}, {
"Posture changed, posture=${DevicePostureController.devicePostureToString(int1)}" +
- " partUpdated=$str1"
+ " partUpdated=$str1"
})
}
@@ -299,6 +299,18 @@ class DozeLogger @Inject constructor(
"Doze aod dimming scrim opacity set, opacity=$long1"
})
}
+
+ fun logCarModeEnded() {
+ buffer.log(TAG, INFO, {}, {
+ "Doze car mode ended"
+ })
+ }
+
+ fun logCarModeStarted() {
+ buffer.log(TAG, INFO, {}, {
+ "Doze car mode started"
+ })
+ }
}
private const val TAG = "DozeLog"
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 5779bb307790..32bc9de40f5b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -20,6 +20,8 @@ import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWA
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
import android.annotation.MainThread;
+import android.app.UiModeManager;
+import android.content.res.Configuration;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Trace;
import android.os.UserHandle;
@@ -33,7 +35,6 @@ import com.android.systemui.doze.dagger.WrappedService;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.Assert;
import com.android.systemui.util.wakelock.WakeLock;
@@ -66,6 +67,8 @@ public class DozeMachine {
INITIALIZED,
/** Regular doze. Device is asleep and listening for pulse triggers. */
DOZE,
+ /** Deep doze. Device is asleep and is not listening for pulse triggers. */
+ DOZE_SUSPEND_TRIGGERS,
/** Always-on doze. Device is asleep, showing UI and listening for pulse triggers. */
DOZE_AOD,
/** Pulse has been requested. Device is awake and preparing UI */
@@ -125,6 +128,7 @@ public class DozeMachine {
: Display.STATE_ON;
case DOZE_AOD_PAUSED:
case DOZE:
+ case DOZE_SUSPEND_TRIGGERS:
return Display.STATE_OFF;
case DOZE_PULSING:
case DOZE_PULSING_BRIGHT:
@@ -143,26 +147,27 @@ public class DozeMachine {
private final WakeLock mWakeLock;
private final AmbientDisplayConfiguration mConfig;
private final WakefulnessLifecycle mWakefulnessLifecycle;
- private final BatteryController mBatteryController;
private final DozeHost mDozeHost;
- private Part[] mParts;
+ private final UiModeManager mUiModeManager;
+ private final DockManager mDockManager;
+ private final Part[] mParts;
private final ArrayList<State> mQueuedRequests = new ArrayList<>();
private State mState = State.UNINITIALIZED;
private int mPulseReason;
private boolean mWakeLockHeldForCurrentState = false;
- private DockManager mDockManager;
@Inject
public DozeMachine(@WrappedService Service service, AmbientDisplayConfiguration config,
WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle,
- BatteryController batteryController, DozeLog dozeLog, DockManager dockManager,
+ UiModeManager uiModeManager,
+ DozeLog dozeLog, DockManager dockManager,
DozeHost dozeHost, Part[] parts) {
mDozeService = service;
mConfig = config;
mWakefulnessLifecycle = wakefulnessLifecycle;
mWakeLock = wakeLock;
- mBatteryController = batteryController;
+ mUiModeManager = uiModeManager;
mDozeLog = dozeLog;
mDockManager = dockManager;
mDozeHost = dozeHost;
@@ -244,7 +249,7 @@ public class DozeMachine {
Assert.isMainThread();
if (isExecutingTransition()) {
throw new IllegalStateException("Cannot get state because there were pending "
- + "transitions: " + mQueuedRequests.toString());
+ + "transitions: " + mQueuedRequests);
}
return mState;
}
@@ -313,11 +318,8 @@ public class DozeMachine {
}
mDozeLog.traceDozeStateSendComplete(newState);
- switch (newState) {
- case FINISH:
- mDozeService.finish();
- break;
- default:
+ if (newState == State.FINISH) {
+ mDozeService.finish();
}
}
@@ -357,6 +359,12 @@ public class DozeMachine {
if (mState == State.FINISH) {
return State.FINISH;
}
+ if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
+ && (requestedState.canPulse() || requestedState.staysAwake())) {
+ Log.i(TAG, "Doze is suppressed with all triggers disabled as car mode is active");
+ mDozeLog.traceCarModeStarted();
+ return State.DOZE_SUSPEND_TRIGGERS;
+ }
if (mDozeHost.isAlwaysOnSuppressed() && requestedState.isAlwaysOn()) {
Log.i(TAG, "Doze is suppressed by an app. Suppressing state: " + requestedState);
mDozeLog.traceAlwaysOnSuppressed(requestedState, "app");
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 83220cab7149..60227ee95fc2 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -147,6 +147,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
setLightSensorEnabled(true);
break;
case DOZE:
+ case DOZE_SUSPEND_TRIGGERS:
setLightSensorEnabled(false);
resetBrightnessToDefault();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java
index 89f50ad9fc21..7ed4b35e1ee7 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java
@@ -17,6 +17,7 @@
package com.android.systemui.doze;
import static android.app.UiModeManager.ACTION_ENTER_CAR_MODE;
+import static android.app.UiModeManager.ACTION_EXIT_CAR_MODE;
import android.app.UiModeManager;
import android.content.BroadcastReceiver;
@@ -96,6 +97,7 @@ public class DozeSuppressor implements DozeMachine.Part {
registerBroadcastReceiver();
mDozeHost.addCallback(mHostCallback);
checkShouldImmediatelyEndDoze();
+ checkShouldImmediatelySuspendDoze();
break;
case FINISH:
destroy();
@@ -110,11 +112,16 @@ public class DozeSuppressor implements DozeMachine.Part {
mDozeHost.removeCallback(mHostCallback);
}
+ private void checkShouldImmediatelySuspendDoze() {
+ if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) {
+ mDozeLog.traceCarModeStarted();
+ mMachine.requestState(DozeMachine.State.DOZE_SUSPEND_TRIGGERS);
+ }
+ }
+
private void checkShouldImmediatelyEndDoze() {
String reason = null;
- if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) {
- reason = "car_mode";
- } else if (!mDozeHost.isProvisioned()) {
+ if (!mDozeHost.isProvisioned()) {
reason = "device_unprovisioned";
} else if (mBiometricUnlockControllerLazy.get().hasPendingAuthentication()) {
reason = "has_pending_auth";
@@ -141,6 +148,7 @@ public class DozeSuppressor implements DozeMachine.Part {
return;
}
IntentFilter filter = new IntentFilter(ACTION_ENTER_CAR_MODE);
+ filter.addAction(ACTION_EXIT_CAR_MODE);
mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter);
mBroadcastReceiverRegistered = true;
}
@@ -156,9 +164,14 @@ public class DozeSuppressor implements DozeMachine.Part {
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
- mDozeLog.traceImmediatelyEndDoze("car_mode");
- mMachine.requestState(DozeMachine.State.FINISH);
+ String action = intent.getAction();
+ if (ACTION_ENTER_CAR_MODE.equals(action)) {
+ mDozeLog.traceCarModeStarted();
+ mMachine.requestState(DozeMachine.State.DOZE_SUSPEND_TRIGGERS);
+ } else if (ACTION_EXIT_CAR_MODE.equals(action)) {
+ mDozeLog.traceCarModeEnded();
+ mMachine.requestState(mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)
+ ? DozeMachine.State.DOZE_AOD : DozeMachine.State.DOZE);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 1cc5df5d04cf..0014d6bbaf24 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -16,6 +16,10 @@
package com.android.systemui.doze;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_SUSPEND_TRIGGERS;
+import static com.android.systemui.doze.DozeMachine.State.FINISH;
+import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
+
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -437,14 +441,18 @@ public class DozeTriggers implements DozeMachine.Part {
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
+ if (oldState == DOZE_SUSPEND_TRIGGERS && (newState != FINISH
+ && newState != UNINITIALIZED)) {
+ // Register callbacks that were unregistered when we switched to
+ // DOZE_SUSPEND_TRIGGERS state.
+ registerCallbacks();
+ }
switch (newState) {
case INITIALIZED:
mAodInterruptRunnable = null;
sWakeDisplaySensorState = true;
- mBroadcastReceiver.register(mBroadcastDispatcher);
- mDockManager.addListener(mDockEventListener);
+ registerCallbacks();
mDozeSensors.requestTemporaryDisable();
- mDozeHost.addCallback(mHostCallback);
break;
case DOZE:
case DOZE_AOD:
@@ -472,21 +480,36 @@ public class DozeTriggers implements DozeMachine.Part {
case DOZE_PULSE_DONE:
mDozeSensors.requestTemporaryDisable();
break;
+ case DOZE_SUSPEND_TRIGGERS:
case FINISH:
- mBroadcastReceiver.unregister(mBroadcastDispatcher);
- mDozeHost.removeCallback(mHostCallback);
- mDockManager.removeListener(mDockEventListener);
- mDozeSensors.setListening(false, false);
- mDozeSensors.setProxListening(false);
- mWantSensors = false;
- mWantProxSensor = false;
- mWantTouchScreenSensors = false;
+ stopListeningToAllTriggers();
break;
default:
}
mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors);
}
+ private void registerCallbacks() {
+ mBroadcastReceiver.register(mBroadcastDispatcher);
+ mDockManager.addListener(mDockEventListener);
+ mDozeHost.addCallback(mHostCallback);
+ }
+
+ private void unregisterCallbacks() {
+ mBroadcastReceiver.unregister(mBroadcastDispatcher);
+ mDozeHost.removeCallback(mHostCallback);
+ mDockManager.removeListener(mDockEventListener);
+ }
+
+ private void stopListeningToAllTriggers() {
+ unregisterCallbacks();
+ mDozeSensors.setListening(false, false);
+ mDozeSensors.setProxListening(false);
+ mWantSensors = false;
+ mWantProxSensor = false;
+ mWantTouchScreenSensors = false;
+ }
+
@Override
public void onScreenState(int state) {
mDozeSensors.onScreenState(state);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index e568b8282856..7c816cec08f2 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -26,8 +26,6 @@ import android.os.SystemClock;
import android.text.format.Formatter;
import android.util.Log;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -44,8 +42,6 @@ import javax.inject.Inject;
*/
@DozeScope
public class DozeUi implements DozeMachine.Part {
- // if enabled, calls dozeTimeTick() whenever the time changes:
- private static final boolean BURN_IN_TESTING_ENABLED = false;
private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
private final Context mContext;
private final DozeHost mHost;
@@ -57,26 +53,13 @@ public class DozeUi implements DozeMachine.Part {
private final DozeParameters mDozeParameters;
private final DozeLog mDozeLog;
private final StatusBarStateController mStatusBarStateController;
- private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback =
- new KeyguardUpdateMonitorCallback() {
- @Override
- public void onTimeChanged() {
- if (BURN_IN_TESTING_ENABLED && mStatusBarStateController.isDozing()) {
- // update whenever the time changes for manual burn in testing
- mHost.dozeTimeTick();
-
- // Keep wakelock until a frame has been pushed.
- mHandler.post(mWakeLock.wrap(() -> {}));
- }
- }
- };
private long mLastTimeTickElapsed = 0;
@Inject
public DozeUi(Context context, AlarmManager alarmManager,
WakeLock wakeLock, DozeHost host, @Main Handler handler,
- DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
+ DozeParameters params,
StatusBarStateController statusBarStateController,
DozeLog dozeLog) {
mContext = context;
@@ -86,7 +69,6 @@ public class DozeUi implements DozeMachine.Part {
mCanAnimateTransition = !params.getDisplayNeedsBlanking();
mDozeParameters = params;
mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler);
- keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
mDozeLog = dozeLog;
mStatusBarStateController = statusBarStateController;
}
@@ -139,6 +121,7 @@ public class DozeUi implements DozeMachine.Part {
break;
case DOZE:
case DOZE_AOD_PAUSED:
+ case DOZE_SUSPEND_TRIGGERS:
unscheduleTimeTick();
break;
case DOZE_REQUEST_PULSE:
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 74949d094e33..d7b7777559da 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -183,22 +183,22 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
}
private void updateBurnInOffsets() {
- int burnInOffset = mMaxBurnInOffset;
-
// Make sure the offset starts at zero, to avoid a big jump in the overlay when it first
// appears.
- long millisSinceStart = System.currentTimeMillis() - mJitterStartTimeMillis;
+ final long millisSinceStart = System.currentTimeMillis() - mJitterStartTimeMillis;
+ final int burnInOffset;
if (millisSinceStart < mMillisUntilFullJitter) {
float lerpAmount = (float) millisSinceStart / (float) mMillisUntilFullJitter;
- burnInOffset = Math.round(MathUtils.lerp(0f, burnInOffset, lerpAmount));
+ burnInOffset = Math.round(MathUtils.lerp(0f, mMaxBurnInOffset, lerpAmount));
+ } else {
+ burnInOffset = mMaxBurnInOffset;
}
// These translation values change slowly, and the set translation methods are idempotent,
// so no translation occurs when the values don't change.
- int burnInOffsetX = getBurnInOffset(burnInOffset * 2, true)
- - burnInOffset;
- int burnInOffsetY = getBurnInOffset(burnInOffset * 2, false)
- - burnInOffset;
+ final int halfBurnInOffset = burnInOffset / 2;
+ final int burnInOffsetX = getBurnInOffset(burnInOffset, true) - halfBurnInOffset;
+ final int burnInOffsetY = getBurnInOffset(burnInOffset, false) - halfBurnInOffset;
mView.setTranslationX(burnInOffsetX);
mView.setTranslationY(burnInOffsetY);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayNotificationCountProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayNotificationCountProvider.java
index 6589f26dbde2..f9fc1f3ca184 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayNotificationCountProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayNotificationCountProvider.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
-import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
@@ -33,13 +32,10 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
-import javax.inject.Inject;
-
/***
* {@link DreamOverlayNotificationCountProvider} provides the current notification count to
- * registered callbacks.
+ * registered callbacks. Ongoing notifications are not included in the count.
*/
-@SysUISingleton
public class DreamOverlayNotificationCountProvider
implements CallbackController<DreamOverlayNotificationCountProvider.Callback> {
private final Set<String> mNotificationKeys = new HashSet<>();
@@ -49,6 +45,10 @@ public class DreamOverlayNotificationCountProvider
@Override
public void onNotificationPosted(
StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) {
+ if (sbn.isOngoing()) {
+ // Don't count ongoing notifications.
+ return;
+ }
mNotificationKeys.add(sbn.getKey());
reportNotificationCountChanged();
}
@@ -78,7 +78,6 @@ public class DreamOverlayNotificationCountProvider
}
};
- @Inject
public DreamOverlayNotificationCountProvider(
NotificationListener notificationListener,
@Background Executor bgExecutor) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
index 994c63002ac2..99ca3c76cf8d 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.os.PatternMatcher;
import android.os.RemoteException;
@@ -31,7 +32,6 @@ import android.service.dreams.IDreamManager;
import android.util.Log;
import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import javax.inject.Inject;
@@ -66,24 +66,16 @@ public class DreamOverlayRegistrant extends CoreStartable {
final int enabledState =
packageManager.getComponentEnabledSetting(mOverlayServiceComponent);
+ // The overlay service is only registered when its component setting is enabled.
+ boolean register = false;
- // TODO(b/204626521): We should not have to set the component enabled setting if the
- // enabled config flag is properly applied based on the RRO.
- if (enabledState != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
- final int overlayState = mResources.getBoolean(R.bool.config_dreamOverlayServiceEnabled)
- ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
- : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
-
- if (overlayState != enabledState) {
- packageManager
- .setComponentEnabledSetting(mOverlayServiceComponent, overlayState, 0);
- }
+ try {
+ register = packageManager.getServiceInfo(mOverlayServiceComponent,
+ PackageManager.GET_META_DATA).enabled;
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "could not find dream overlay service");
}
- // The overlay service is only registered when its component setting is enabled.
- boolean register = packageManager.getComponentEnabledSetting(mOverlayServiceComponent)
- == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-
if (mCurrentRegisteredState == register) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index fc71e2fb2329..69e41ba9b284 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -101,6 +101,9 @@ public class DreamOverlayStateController implements
public void addComplication(Complication complication) {
mExecutor.execute(() -> {
if (mComplications.add(complication)) {
+ if (DEBUG) {
+ Log.d(TAG, "addComplication: added " + complication);
+ }
mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged());
}
});
@@ -112,6 +115,9 @@ public class DreamOverlayStateController implements
public void removeComplication(Complication complication) {
mExecutor.execute(() -> {
if (mComplications.remove(complication)) {
+ if (DEBUG) {
+ Log.d(TAG, "removeComplication: removed " + complication);
+ }
mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged());
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
index 59a17bad5069..a25257d6cf42 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
@@ -43,6 +43,8 @@ public class DreamOverlayStatusBarView extends ConstraintLayout {
STATUS_ICON_NOTIFICATIONS,
STATUS_ICON_WIFI_UNAVAILABLE,
STATUS_ICON_ALARM_SET,
+ STATUS_ICON_CAMERA_DISABLED,
+ STATUS_ICON_MIC_DISABLED,
STATUS_ICON_MIC_CAMERA_DISABLED,
STATUS_ICON_PRIORITY_MODE_ON
})
@@ -50,8 +52,10 @@ public class DreamOverlayStatusBarView extends ConstraintLayout {
public static final int STATUS_ICON_NOTIFICATIONS = 0;
public static final int STATUS_ICON_WIFI_UNAVAILABLE = 1;
public static final int STATUS_ICON_ALARM_SET = 2;
- public static final int STATUS_ICON_MIC_CAMERA_DISABLED = 3;
- public static final int STATUS_ICON_PRIORITY_MODE_ON = 4;
+ public static final int STATUS_ICON_CAMERA_DISABLED = 3;
+ public static final int STATUS_ICON_MIC_DISABLED = 4;
+ public static final int STATUS_ICON_MIC_CAMERA_DISABLED = 5;
+ public static final int STATUS_ICON_PRIORITY_MODE_ON = 6;
private final Map<Integer, View> mStatusIcons = new HashMap<>();
@@ -80,6 +84,10 @@ public class DreamOverlayStatusBarView extends ConstraintLayout {
fetchStatusIconForResId(R.id.dream_overlay_wifi_status));
mStatusIcons.put(STATUS_ICON_ALARM_SET,
fetchStatusIconForResId(R.id.dream_overlay_alarm_set));
+ mStatusIcons.put(STATUS_ICON_CAMERA_DISABLED,
+ fetchStatusIconForResId(R.id.dream_overlay_camera_off));
+ mStatusIcons.put(STATUS_ICON_MIC_DISABLED,
+ fetchStatusIconForResId(R.id.dream_overlay_mic_off));
mStatusIcons.put(STATUS_ICON_MIC_CAMERA_DISABLED,
fetchStatusIconForResId(R.id.dream_overlay_camera_mic_off));
mStatusIcons.put(STATUS_ICON_NOTIFICATIONS,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index e878b22f61ed..de7bf28c01d6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -49,6 +49,7 @@ import com.android.systemui.util.time.DateFormatUtil;
import java.util.Locale;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -65,7 +66,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
private final Resources mResources;
private final DateFormatUtil mDateFormatUtil;
private final IndividualSensorPrivacyController mSensorPrivacyController;
- private final DreamOverlayNotificationCountProvider mDreamOverlayNotificationCountProvider;
+ private final Optional<DreamOverlayNotificationCountProvider>
+ mDreamOverlayNotificationCountProvider;
private final ZenModeController mZenModeController;
private final Executor mMainExecutor;
@@ -125,7 +127,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
NextAlarmController nextAlarmController,
DateFormatUtil dateFormatUtil,
IndividualSensorPrivacyController sensorPrivacyController,
- DreamOverlayNotificationCountProvider dreamOverlayNotificationCountProvider,
+ Optional<DreamOverlayNotificationCountProvider> dreamOverlayNotificationCountProvider,
ZenModeController zenModeController,
StatusBarWindowStateController statusBarWindowStateController) {
super(view);
@@ -161,7 +163,9 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
mZenModeController.addCallback(mZenModeCallback);
updatePriorityModeStatusIcon();
- mDreamOverlayNotificationCountProvider.addCallback(mNotificationCountCallback);
+ mDreamOverlayNotificationCountProvider.ifPresent(
+ provider -> provider.addCallback(mNotificationCountCallback));
+
mTouchInsetSession.addViewToTracking(mView);
}
@@ -171,7 +175,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
mSensorPrivacyController.removeCallback(mSensorCallback);
mNextAlarmController.removeCallback(mNextAlarmCallback);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
- mDreamOverlayNotificationCountProvider.removeCallback(mNotificationCountCallback);
+ mDreamOverlayNotificationCountProvider.ifPresent(
+ provider -> provider.removeCallback(mNotificationCountCallback));
mTouchInsetSession.clear();
mIsAttached = false;
@@ -209,9 +214,17 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE);
final boolean cameraBlocked = mSensorPrivacyController
.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA);
- showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
- micBlocked && cameraBlocked);
+ @DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
+ if (micBlocked && cameraBlocked) {
+ iconType = DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED;
+ } else if (!micBlocked && cameraBlocked) {
+ iconType = DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED;
+ } else if (micBlocked && !cameraBlocked) {
+ iconType = DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED;
+ }
+ if (iconType != Resources.ID_NULL) {
+ showIcon(iconType, true);
+ }
}
private String buildNotificationsContentDescription(int notificationCount) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java
index a83e006dfa2f..be94e5031917 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java
@@ -26,7 +26,7 @@ import com.android.systemui.CoreStartable;
import com.android.systemui.dreams.complication.Complication;
import com.android.systemui.dreams.complication.ComplicationLayoutParams;
import com.android.systemui.dreams.complication.ComplicationViewModel;
-import com.android.systemui.dreams.smartspace.DreamsSmartspaceController;
+import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import java.util.List;
@@ -43,7 +43,7 @@ public class SmartSpaceComplication implements Complication {
* SystemUI.
*/
public static class Registrant extends CoreStartable {
- private final DreamsSmartspaceController mSmartSpaceController;
+ private final DreamSmartspaceController mSmartSpaceController;
private final DreamOverlayStateController mDreamOverlayStateController;
private final SmartSpaceComplication mComplication;
@@ -66,7 +66,7 @@ public class SmartSpaceComplication implements Complication {
public Registrant(Context context,
DreamOverlayStateController dreamOverlayStateController,
SmartSpaceComplication smartSpaceComplication,
- DreamsSmartspaceController smartSpaceController) {
+ DreamSmartspaceController smartSpaceController) {
super(context);
mDreamOverlayStateController = dreamOverlayStateController;
mComplication = smartSpaceComplication;
@@ -82,6 +82,7 @@ public class SmartSpaceComplication implements Complication {
mSmartSpaceController.addListener(mSmartspaceListener);
} else {
mSmartSpaceController.removeListener(mSmartspaceListener);
+ mDreamOverlayStateController.removeComplication(mComplication);
}
}
});
@@ -89,25 +90,30 @@ public class SmartSpaceComplication implements Complication {
}
private static class SmartSpaceComplicationViewHolder implements ViewHolder {
+ private View mView = null;
private static final int SMARTSPACE_COMPLICATION_WEIGHT = 10;
- private final DreamsSmartspaceController mSmartSpaceController;
+ private final DreamSmartspaceController mSmartSpaceController;
private final Context mContext;
protected SmartSpaceComplicationViewHolder(
Context context,
- DreamsSmartspaceController smartSpaceController) {
+ DreamSmartspaceController smartSpaceController) {
mSmartSpaceController = smartSpaceController;
mContext = context;
}
@Override
public View getView() {
+ if (mView != null) {
+ return mView;
+ }
final FrameLayout smartSpaceContainer = new FrameLayout(mContext);
smartSpaceContainer.addView(
mSmartSpaceController.buildAndConnectView(smartSpaceContainer),
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
+ mView = smartSpaceContainer;
return smartSpaceContainer;
}
@@ -120,12 +126,12 @@ public class SmartSpaceComplication implements Complication {
}
}
- private final DreamsSmartspaceController mSmartSpaceController;
+ private final DreamSmartspaceController mSmartSpaceController;
private final Context mContext;
@Inject
public SmartSpaceComplication(Context context,
- DreamsSmartspaceController smartSpaceController) {
+ DreamSmartspaceController smartSpaceController) {
mContext = context;
mSmartSpaceController = smartSpaceController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
index 4c0154faeb5a..fd6cfc0700ad 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationHostViewController.java
@@ -21,6 +21,7 @@ import static com.android.systemui.dreams.complication.dagger.ComplicationModule
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Debug;
import android.util.Log;
import android.view.View;
@@ -44,7 +45,8 @@ import javax.inject.Named;
* a {@link ComplicationLayoutEngine}.
*/
public class ComplicationHostViewController extends ViewController<ConstraintLayout> {
- public static final String TAG = "ComplicationHostViewController";
+ private static final String TAG = "ComplicationHostVwCtrl";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final ComplicationLayoutEngine mLayoutEngine;
private final LifecycleOwner mLifecycleOwner;
@@ -90,6 +92,11 @@ public class ComplicationHostViewController extends ViewController<ConstraintLay
}
private void updateComplications(Collection<ComplicationViewModel> complications) {
+ if (DEBUG) {
+ Log.d(TAG, "updateComplications called. Callers = " + Debug.getCallers(25));
+ Log.d(TAG, " mComplications = " + mComplications.toString());
+ Log.d(TAG, " complications = " + complications.toString());
+ }
final Collection<ComplicationId> ids = complications.stream()
.map(complicationViewModel -> complicationViewModel.getId())
.collect(Collectors.toSet());
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
index ded61a8b80d9..9cd149b9bfee 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
@@ -54,7 +54,7 @@ import javax.inject.Named;
*/
@DreamOverlayComponent.DreamOverlayScope
public class ComplicationLayoutEngine implements Complication.VisibilityController {
- public static final String TAG = "ComplicationLayoutEngine";
+ public static final String TAG = "ComplicationLayoutEng";
/**
* {@link ViewEntry} is an internal container, capturing information necessary for working with
@@ -529,7 +529,7 @@ public class ComplicationLayoutEngine implements Complication.VisibilityControll
*/
public void addComplication(ComplicationId id, View view,
ComplicationLayoutParams lp, @Complication.Category int category) {
- Log.d(TAG, "engine: " + this + " addComplication");
+ Log.d(TAG, "@" + Integer.toHexString(this.hashCode()) + " addComplication: " + id);
// If the complication is present, remove.
if (mEntries.containsKey(id)) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationViewModel.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationViewModel.java
index f0239371ee63..00cf58c0c665 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationViewModel.java
@@ -64,4 +64,9 @@ public class ComplicationViewModel extends ViewModel {
public void exitDream() {
mHost.requestExitDream();
}
+
+ @Override
+ public String toString() {
+ return mId + "=" + mComplication.toString();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java
deleted file mode 100644
index f5c5a434a077..000000000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.dreams.complication;
-
-import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS;
-import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_VIEW;
-
-import android.app.smartspace.SmartspaceAction;
-import android.app.smartspace.SmartspaceTarget;
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.text.TextUtils;
-import android.widget.TextView;
-
-import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
-import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent;
-import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener;
-import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
-import com.android.systemui.util.ViewController;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-/**
- * Weather Complication that produce Weather view holder.
- */
-public class DreamWeatherComplication implements Complication {
- DreamWeatherComplicationComponent.Factory mComponentFactory;
-
- /**
- * Default constructor for {@link DreamWeatherComplication}.
- */
- @Inject
- public DreamWeatherComplication(
- DreamWeatherComplicationComponent.Factory componentFactory) {
- mComponentFactory = componentFactory;
- }
-
- @Override
- public int getRequiredTypeAvailability() {
- return COMPLICATION_TYPE_WEATHER;
- }
-
- /**
- * Create {@link DreamWeatherViewHolder}.
- */
- @Override
- public ViewHolder createView(ComplicationViewModel model) {
- return mComponentFactory.create().getViewHolder();
- }
-
- /**
- * {@link CoreStartable} for registering {@link DreamWeatherComplication} with SystemUI.
- */
- public static class Registrant extends CoreStartable {
- private final LockscreenSmartspaceController mSmartSpaceController;
- private final DreamOverlayStateController mDreamOverlayStateController;
- private final DreamWeatherComplication mComplication;
-
- /**
- * Default constructor to register {@link DreamWeatherComplication}.
- */
- @Inject
- public Registrant(Context context,
- LockscreenSmartspaceController smartspaceController,
- DreamOverlayStateController dreamOverlayStateController,
- DreamWeatherComplication dreamWeatherComplication) {
- super(context);
- mSmartSpaceController = smartspaceController;
- mDreamOverlayStateController = dreamOverlayStateController;
- mComplication = dreamWeatherComplication;
- }
-
- @Override
- public void start() {
- if (mSmartSpaceController.isEnabled()) {
- mDreamOverlayStateController.addComplication(mComplication);
- }
- }
- }
-
- /**
- * ViewHolder to contain value/logic associated with a Weather Complication View.
- */
- public static class DreamWeatherViewHolder implements ViewHolder {
- private final TextView mView;
- private final ComplicationLayoutParams mLayoutParams;
- private final DreamWeatherViewController mViewController;
-
- @Inject
- DreamWeatherViewHolder(
- @Named(DREAM_WEATHER_COMPLICATION_VIEW) TextView view,
- DreamWeatherViewController controller,
- @Named(DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS)
- ComplicationLayoutParams layoutParams) {
- mView = view;
- mLayoutParams = layoutParams;
- mViewController = controller;
- mViewController.init();
- }
-
- @Override
- public TextView getView() {
- return mView;
- }
-
- @Override
- public ComplicationLayoutParams getLayoutParams() {
- return mLayoutParams;
- }
- }
-
- /**
- * ViewController to contain value/logic associated with a Weather Complication View.
- */
- static class DreamWeatherViewController extends ViewController<TextView> {
- private final LockscreenSmartspaceController mSmartSpaceController;
- private SmartspaceTargetListener mSmartspaceTargetListener;
-
- @Inject
- DreamWeatherViewController(
- @Named(DREAM_WEATHER_COMPLICATION_VIEW) TextView view,
- LockscreenSmartspaceController smartspaceController
- ) {
- super(view);
- mSmartSpaceController = smartspaceController;
- }
-
- @Override
- protected void onViewAttached() {
- mSmartspaceTargetListener = targets -> targets.forEach(
- t -> {
- if (t instanceof SmartspaceTarget
- && ((SmartspaceTarget) t).getFeatureType()
- == SmartspaceTarget.FEATURE_WEATHER) {
- final SmartspaceTarget target = (SmartspaceTarget) t;
- final SmartspaceAction headerAction = target.getHeaderAction();
- if (headerAction == null || TextUtils.isEmpty(
- headerAction.getTitle())) {
- return;
- }
-
- String temperature = headerAction.getTitle().toString();
- mView.setText(temperature);
- final Icon icon = headerAction.getIcon();
- if (icon != null) {
- final int iconSize =
- getResources().getDimensionPixelSize(
- R.dimen.smart_action_button_icon_size);
- final Drawable iconDrawable = icon.loadDrawable(getContext());
- iconDrawable.setBounds(0, 0, iconSize, iconSize);
- mView.setCompoundDrawables(iconDrawable, null, null, null);
- mView.setCompoundDrawablePadding(
- getResources().getDimensionPixelSize(
- R.dimen.smart_action_button_icon_padding));
-
- }
- }
- });
- mSmartSpaceController.addListener(mSmartspaceTargetListener);
- }
-
- @Override
- protected void onViewDetached() {
- mSmartSpaceController.removeListener(mSmartspaceTargetListener);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationModule.java
index eb2fc5d1a93e..3ab26ceeb076 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationModule.java
@@ -41,7 +41,7 @@ public interface DreamClockDateComplicationModule {
"clock_date_complication_layout_params";
// Order weight of insert into parent container
//TODO(b/217199227): move to a single location.
- int INSERT_ORDER_WEIGHT = 2;
+ int INSERT_ORDER_WEIGHT = 3;
/**
* Provides the complication view.
@@ -66,6 +66,6 @@ public interface DreamClockDateComplicationModule {
ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
ComplicationLayoutParams.DIRECTION_END,
- INSERT_ORDER_WEIGHT);
+ INSERT_ORDER_WEIGHT, /* snapToGuide= */ true);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java
deleted file mode 100644
index 536f3dcd2850..000000000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.dreams.complication.dagger;
-
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import com.android.internal.util.Preconditions;
-import com.android.systemui.R;
-import com.android.systemui.dreams.complication.ComplicationLayoutParams;
-import com.android.systemui.dreams.complication.DreamWeatherComplication.DreamWeatherViewHolder;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-
-import javax.inject.Named;
-import javax.inject.Scope;
-
-import dagger.Module;
-import dagger.Provides;
-import dagger.Subcomponent;
-
-/**
- * {@link DreamWeatherComplicationComponent} is responsible for generating dependencies surrounding
- * the
- * Clock Date {@link com.android.systemui.dreams.complication.Complication}, such as the layout
- * details.
- */
-@Subcomponent(modules = {
- DreamWeatherComplicationComponent.DreamWeatherComplicationModule.class,
-})
-@DreamWeatherComplicationComponent.DreamWeatherComplicationScope
-public interface DreamWeatherComplicationComponent {
- /**
- * Creates {@link DreamWeatherViewHolder}.
- */
- DreamWeatherViewHolder getViewHolder();
-
- @Documented
- @Retention(RUNTIME)
- @Scope
- @interface DreamWeatherComplicationScope {
- }
-
- /**
- * Generates {@link DreamWeatherComplicationComponent}.
- */
- @Subcomponent.Factory
- interface Factory {
- DreamWeatherComplicationComponent create();
- }
-
- /**
- * Scoped values for {@link DreamWeatherComplicationComponent}.
- */
- @Module
- interface DreamWeatherComplicationModule {
- String DREAM_WEATHER_COMPLICATION_VIEW = "weather_complication_view";
- String DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS =
- "weather_complication_layout_params";
- // Order weight of insert into parent container
- int INSERT_ORDER_WEIGHT = 1;
-
- /**
- * Provides the complication view.
- */
- @Provides
- @DreamWeatherComplicationScope
- @Named(DREAM_WEATHER_COMPLICATION_VIEW)
- static TextView provideComplicationView(LayoutInflater layoutInflater) {
- return Preconditions.checkNotNull((TextView)
- layoutInflater.inflate(R.layout.dream_overlay_complication_weather,
- null, false),
- "R.layout.dream_overlay_complication_weather did not properly inflated");
- }
-
- /**
- * Provides the layout parameters for the complication view.
- */
- @Provides
- @DreamWeatherComplicationScope
- @Named(DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS)
- static ComplicationLayoutParams provideLayoutParams() {
- return new ComplicationLayoutParams(0,
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ComplicationLayoutParams.POSITION_BOTTOM
- | ComplicationLayoutParams.POSITION_START,
- ComplicationLayoutParams.DIRECTION_END,
- INSERT_ORDER_WEIGHT);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
index 62a4140c6745..e45437dedd6e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
@@ -27,9 +27,6 @@ import dagger.Module;
@Module(includes = {
DreamClockDateComplicationModule.class,
DreamClockTimeComplicationModule.class,
- },
- subcomponents = {
- DreamWeatherComplicationComponent.class,
})
public interface RegisteredComplicationsModule {
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index c1dff248818f..2dd2098a78b9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -17,10 +17,18 @@
package com.android.systemui.dreams.dagger;
import android.content.Context;
+import android.content.res.Resources;
import com.android.settingslib.dream.DreamBackend;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.DreamOverlayNotificationCountProvider;
import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule;
+import java.util.Optional;
+
+import javax.inject.Named;
+
import dagger.Module;
import dagger.Provides;
@@ -34,6 +42,10 @@ import dagger.Provides;
DreamOverlayComponent.class,
})
public interface DreamModule {
+ String DREAM_ONLY_ENABLED_FOR_SYSTEM_USER = "dream_only_enabled_for_system_user";
+
+ String DREAM_SUPPORTED = "dream_supported";
+
/**
* Provides an instance of the dream backend.
*/
@@ -41,4 +53,31 @@ public interface DreamModule {
static DreamBackend providesDreamBackend(Context context) {
return DreamBackend.getInstance(context);
}
+
+ /**
+ * Provides an instance of a {@link DreamOverlayNotificationCountProvider}.
+ */
+ @SysUISingleton
+ @Provides
+ static Optional<DreamOverlayNotificationCountProvider>
+ providesDreamOverlayNotificationCountProvider() {
+ // If we decide to bring this back, we should gate it on a config that can be changed in
+ // an overlay.
+ return Optional.empty();
+ }
+
+ /** */
+ @Provides
+ @Named(DREAM_ONLY_ENABLED_FOR_SYSTEM_USER)
+ static boolean providesDreamOnlyEnabledForSystemUser(@Main Resources resources) {
+ return resources.getBoolean(
+ com.android.internal.R.bool.config_dreamsOnlyEnabledForSystemUser);
+ }
+
+ /** */
+ @Provides
+ @Named(DREAM_SUPPORTED)
+ static boolean providesDreamSupported(@Main Resources resources) {
+ return resources.getBoolean(com.android.internal.R.bool.config_dreamsSupported);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamsSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt
index a3095472783b..63f63a5093d2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamsSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt
@@ -19,6 +19,7 @@ package com.android.systemui.dreams.smartspace
import android.app.smartspace.SmartspaceConfig
import android.app.smartspace.SmartspaceManager
import android.app.smartspace.SmartspaceSession
+import android.app.smartspace.SmartspaceTarget
import android.content.Context
import android.graphics.Color
import android.util.Log
@@ -46,7 +47,7 @@ import javax.inject.Named
* Controller for managing the smartspace view on the dream
*/
@SysUISingleton
-class DreamsSmartspaceController @Inject constructor(
+class DreamSmartspaceController @Inject constructor(
private val context: Context,
private val smartspaceManager: SmartspaceManager,
private val execution: Execution,
@@ -58,7 +59,7 @@ class DreamsSmartspaceController @Inject constructor(
@Named(DREAM_SMARTSPACE_DATA_PLUGIN) optionalPlugin: Optional<BcSmartspaceDataPlugin>
) {
companion object {
- private const val TAG = "DreamsSmartspaceCtrlr"
+ private const val TAG = "DreamSmartspaceCtrlr"
}
private var session: SmartspaceSession? = null
@@ -66,7 +67,9 @@ class DreamsSmartspaceController @Inject constructor(
private var targetFilter: SmartspaceTargetFilter? = optionalTargetFilter.orElse(null)
// A shadow copy of listeners is maintained to track whether the session should remain open.
- private var listeners = mutableSetOf<BcSmartspaceDataPlugin.SmartspaceTargetListener>()
+ private var listeners = mutableSetOf<SmartspaceTargetListener>()
+
+ private var unfilteredListeners = mutableSetOf<SmartspaceTargetListener>()
// Smartspace can be used on multiple displays, such as when the user casts their screen
private var smartspaceViews = mutableSetOf<SmartspaceView>()
@@ -113,6 +116,7 @@ class DreamsSmartspaceController @Inject constructor(
private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
execution.assertIsMainThread()
+ onTargetsAvailableUnfiltered(targets)
val filteredTargets = targets.filter { targetFilter?.filterSmartspaceTarget(it) ?: true }
plugin?.onTargetsAvailable(filteredTargets)
}
@@ -137,13 +141,10 @@ class DreamsSmartspaceController @Inject constructor(
private fun buildView(parent: ViewGroup): View? {
return if (plugin != null) {
var view = smartspaceViewComponentFactory.create(parent, plugin, stateChangeListener)
- .getView()
+ .getView()
if (view !is View) {
return null
}
-
- view.setIsDreaming(true)
-
return view
} else {
null
@@ -151,7 +152,8 @@ class DreamsSmartspaceController @Inject constructor(
}
private fun hasActiveSessionListeners(): Boolean {
- return smartspaceViews.isNotEmpty() || listeners.isNotEmpty()
+ return smartspaceViews.isNotEmpty() || listeners.isNotEmpty() ||
+ unfilteredListeners.isNotEmpty()
}
private fun connectSession() {
@@ -164,13 +166,15 @@ class DreamsSmartspaceController @Inject constructor(
}
val newSession = smartspaceManager.createSmartspaceSession(
- SmartspaceConfig.Builder(context, "dream").build())
+ SmartspaceConfig.Builder(context, "dream").build()
+ )
Log.d(TAG, "Starting smartspace session for dream")
newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener)
this.session = newSession
plugin.registerSmartspaceEventNotifier {
- e -> session?.notifySmartspaceEvent(e)
+ e ->
+ session?.notifySmartspaceEvent(e)
}
reloadSmartspace()
@@ -218,4 +222,22 @@ class DreamsSmartspaceController @Inject constructor(
private fun reloadSmartspace() {
session?.requestSmartspaceUpdate()
}
+
+ private fun onTargetsAvailableUnfiltered(targets: List<SmartspaceTarget>) {
+ unfilteredListeners.forEach { it.onSmartspaceTargetsUpdated(targets) }
+ }
+
+ /**
+ * Adds a listener for the raw, unfiltered list of smartspace targets. This should be used
+ * carefully, as it doesn't filter out targets which the user may not want shown.
+ */
+ fun addUnfilteredListener(listener: SmartspaceTargetListener) {
+ unfilteredListeners.add(listener)
+ connectSession()
+ }
+
+ fun removeUnfilteredListener(listener: SmartspaceTargetListener) {
+ unfilteredListeners.remove(listener)
+ disconnect()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index fbca7b1d86d3..f769a2355409 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -44,6 +44,7 @@ import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent
import com.android.wm.shell.animation.FlingAnimationUtils;
import java.util.Optional;
+
import javax.inject.Inject;
import javax.inject.Named;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/HideComplicationTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/HideComplicationTouchHandler.java
index 4965c9dfd00b..3087cdfd0cc0 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/HideComplicationTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/HideComplicationTouchHandler.java
@@ -25,6 +25,7 @@ import android.view.View;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.complication.Complication;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.touch.TouchInsetManager;
import com.google.common.util.concurrent.ListenableFuture;
@@ -50,6 +51,7 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
private final Complication.VisibilityController mVisibilityController;
private final int mRestoreTimeout;
+ private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final Handler mHandler;
private final Executor mExecutor;
private final TouchInsetManager mTouchInsetManager;
@@ -65,10 +67,12 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
HideComplicationTouchHandler(Complication.VisibilityController visibilityController,
@Named(COMPLICATIONS_RESTORE_TIMEOUT) int restoreTimeout,
TouchInsetManager touchInsetManager,
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@Main Executor executor,
@Main Handler handler) {
mVisibilityController = visibilityController;
mRestoreTimeout = restoreTimeout;
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mHandler = handler;
mTouchInsetManager = touchInsetManager;
mExecutor = executor;
@@ -80,10 +84,13 @@ public class HideComplicationTouchHandler implements DreamTouchHandler {
Log.d(TAG, "onSessionStart");
}
+ final boolean bouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing();
+
// If other sessions are interested in this touch, do not fade out elements.
- if (session.getActiveSessionCount() > 1) {
+ if (session.getActiveSessionCount() > 1 || bouncerShowing) {
if (DEBUG) {
- Log.d(TAG, "multiple active touch sessions, not fading");
+ Log.d(TAG, "not fading. Active session count: " + session.getActiveSessionCount()
+ + ". Bouncer showing: " + bouncerShowing);
}
session.pop();
return;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
index 9d6e3c295100..2cee2520ce55 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
@@ -29,6 +29,9 @@ interface FeatureFlags : FlagListenable {
fun isEnabled(flag: ResourceBooleanFlag): Boolean
/** Returns a boolean value for the given flag. */
+ fun isEnabled(flag: DeviceConfigBooleanFlag): Boolean
+
+ /** Returns a boolean value for the given flag. */
fun isEnabled(flag: SysPropBooleanFlag): Boolean
/** Returns a string value for the given flag. */
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index c4531b573d22..c5221cd9641b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -44,6 +44,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.commandline.Command;
import com.android.systemui.statusbar.commandline.CommandRegistry;
+import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.settings.SecureSettings;
import java.io.PrintWriter;
@@ -81,6 +82,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
private final SecureSettings mSecureSettings;
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
+ private final DeviceConfigProxy mDeviceConfigProxy;
private final Map<Integer, Flag<?>> mAllFlags;
private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>();
private final Map<Integer, String> mStringFlagCache = new TreeMap<>();
@@ -94,6 +96,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
SystemPropertiesHelper systemProperties,
@Main Resources resources,
DumpManager dumpManager,
+ DeviceConfigProxy deviceConfigProxy,
@Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
CommandRegistry commandRegistry,
IStatusBarService barService) {
@@ -101,6 +104,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
mSecureSettings = secureSettings;
mResources = resources;
mSystemProperties = systemProperties;
+ mDeviceConfigProxy = deviceConfigProxy;
mAllFlags = allFlags;
mBarService = barService;
@@ -138,6 +142,18 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
}
@Override
+ public boolean isEnabled(@NonNull DeviceConfigBooleanFlag flag) {
+ int id = flag.getId();
+ if (!mBooleanFlagCache.containsKey(id)) {
+ boolean deviceConfigValue = mDeviceConfigProxy.getBoolean(flag.getNamespace(),
+ flag.getName(), flag.getDefault());
+ mBooleanFlagCache.put(id, readFlagValue(id, deviceConfigValue));
+ }
+
+ return mBooleanFlagCache.get(id);
+ }
+
+ @Override
public boolean isEnabled(@NonNull SysPropBooleanFlag flag) {
int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
@@ -180,7 +196,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
/** Specific override for Boolean flags that checks against the teamfood list.*/
private boolean readFlagValue(int id, boolean defaultValue) {
- Boolean result = readFlagValueInternal(id, BooleanFlagSerializer.INSTANCE);
+ Boolean result = readBooleanFlagOverride(id);
// Only check for teamfood if the default is false.
if (!defaultValue && result == null && id != Flags.TEAMFOOD.getId()) {
if (mAllFlags.containsKey(id) && mAllFlags.get(id).getTeamfood()) {
@@ -191,6 +207,10 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
return result == null ? defaultValue : result;
}
+ private Boolean readBooleanFlagOverride(int id) {
+ return readFlagValueInternal(id, BooleanFlagSerializer.INSTANCE);
+ }
+
@NonNull
private <T> T readFlagValue(int id, @NonNull T defaultValue, FlagSerializer<T> serializer) {
requireNonNull(defaultValue, "defaultValue");
@@ -293,6 +313,8 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
} else if (flag instanceof ResourceBooleanFlag) {
setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
+ } else if (flag instanceof DeviceConfigBooleanFlag) {
+ setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
} else if (flag instanceof SysPropBooleanFlag) {
// Store SysProp flags in SystemProperties where they can read by outside parties.
mSystemProperties.setBoolean(((SysPropBooleanFlag) flag).getName(), value);
@@ -388,16 +410,30 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
@Nullable
private ParcelableFlag<?> toParcelableFlag(Flag<?> f) {
if (f instanceof BooleanFlag) {
- return new BooleanFlag(f.getId(), isEnabled((BooleanFlag) f), f.getTeamfood());
+ return new BooleanFlag(
+ f.getId(),
+ isEnabled((BooleanFlag) f),
+ f.getTeamfood(),
+ readBooleanFlagOverride(f.getId()) != null);
}
if (f instanceof ResourceBooleanFlag) {
return new BooleanFlag(
- f.getId(), isEnabled((ResourceBooleanFlag) f), f.getTeamfood());
+ f.getId(),
+ isEnabled((ResourceBooleanFlag) f),
+ f.getTeamfood(),
+ readBooleanFlagOverride(f.getId()) != null);
+ }
+ if (f instanceof DeviceConfigBooleanFlag) {
+ return new BooleanFlag(
+ f.getId(), isEnabled((DeviceConfigBooleanFlag) f), f.getTeamfood());
}
if (f instanceof SysPropBooleanFlag) {
// TODO(b/223379190): Teamfood not supported for sysprop flags yet.
return new BooleanFlag(
- f.getId(), isEnabled((SysPropBooleanFlag) f), false);
+ f.getId(),
+ ((SysPropBooleanFlag) f).getDefault(),
+ false,
+ !mSystemProperties.get(((SysPropBooleanFlag) f).getName()).isEmpty());
}
// TODO: add support for other flag types.
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index cacef161a862..1492a2bc0ceb 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -19,6 +19,7 @@ package com.android.systemui.flags;
import static java.util.Objects.requireNonNull;
import android.content.res.Resources;
+import android.provider.DeviceConfig;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -28,6 +29,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.util.DeviceConfigProxy;
import java.io.PrintWriter;
import java.util.Map;
@@ -44,6 +46,7 @@ import javax.inject.Inject;
public class FeatureFlagsRelease implements FeatureFlags, Dumpable {
private final Resources mResources;
private final SystemPropertiesHelper mSystemProperties;
+ private final DeviceConfigProxy mDeviceConfigProxy;
SparseBooleanArray mBooleanCache = new SparseBooleanArray();
SparseArray<String> mStringCache = new SparseArray<>();
@@ -51,9 +54,11 @@ public class FeatureFlagsRelease implements FeatureFlags, Dumpable {
public FeatureFlagsRelease(
@Main Resources resources,
SystemPropertiesHelper systemProperties,
+ DeviceConfigProxy deviceConfigProxy,
DumpManager dumpManager) {
mResources = resources;
mSystemProperties = systemProperties;
+ mDeviceConfigProxy = deviceConfigProxy;
dumpManager.registerDumpable("SysUIFlags", this);
}
@@ -79,6 +84,18 @@ public class FeatureFlagsRelease implements FeatureFlags, Dumpable {
}
@Override
+ public boolean isEnabled(@NonNull DeviceConfigBooleanFlag flag) {
+ int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
+ if (cacheIndex < 0) {
+ boolean deviceConfigValue = mDeviceConfigProxy.getBoolean(flag.getNamespace(),
+ flag.getName(), flag.getDefault());
+ return isEnabled(flag.getId(), deviceConfigValue);
+ }
+
+ return mBooleanCache.valueAt(cacheIndex);
+ }
+
+ @Override
public boolean isEnabled(SysPropBooleanFlag flag) {
int cacheIndex = mBooleanCache.indexOfKey(flag.getId());
if (cacheIndex < 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 2c798307c196..a566984748a7 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -16,6 +16,8 @@
package com.android.systemui.flags;
+import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER;
+
import com.android.internal.annotations.Keep;
import com.android.systemui.R;
@@ -43,9 +45,6 @@ public class Flags {
/***************************************/
// 100 - notification
- public static final BooleanFlag NEW_NOTIFICATION_PIPELINE_RENDERING =
- new BooleanFlag(101, true);
-
public static final BooleanFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
new BooleanFlag(103, false);
@@ -61,6 +60,12 @@ public class Flags {
public static final ResourceBooleanFlag NOTIFICATION_DRAG_TO_CONTENTS =
new ResourceBooleanFlag(108, R.bool.config_notificationToContents);
+ public static final BooleanFlag REMOVE_UNRANKED_NOTIFICATIONS =
+ new BooleanFlag(109, false);
+
+ public static final BooleanFlag FSI_REQUIRES_KEYGUARD =
+ new BooleanFlag(110, false, true);
+
/***************************************/
// 200 - keyguard/lockscreen
@@ -82,6 +87,11 @@ public class Flags {
public static final ResourceBooleanFlag FACE_SCANNING_ANIM =
new ResourceBooleanFlag(205, R.bool.config_enableFaceScanningAnimation);
+ /**
+ * Whether the KeyguardBottomArea(View|Controller) should use the modern architecture or the old
+ * one.
+ */
+ public static final BooleanFlag MODERN_BOTTOM_AREA = new BooleanFlag(206, false);
/***************************************/
// 300 - power menu
@@ -135,6 +145,9 @@ public class Flags {
public static final ResourceBooleanFlag STATUS_BAR_USER_SWITCHER =
new ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip);
+ public static final BooleanFlag STATUS_BAR_LETTERBOX_APPEARANCE =
+ new BooleanFlag(603, false);
+
/***************************************/
// 700 - dialer/calls
public static final BooleanFlag ONGOING_CALL_STATUS_BAR_CHIP =
@@ -153,7 +166,7 @@ public class Flags {
/***************************************/
// 900 - media
- public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, false);
+ public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false);
public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true);
public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
@@ -161,12 +174,26 @@ public class Flags {
// 1000 - dock
public static final BooleanFlag SIMULATE_DOCK_THROUGH_CHARGING =
new BooleanFlag(1000, true);
+ public static final BooleanFlag DOCK_SETUP_ENABLED = new BooleanFlag(1001, true);
+
// 1100 - windowing
@Keep
public static final SysPropBooleanFlag WM_ENABLE_SHELL_TRANSITIONS =
new SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false);
+ /**
+ * b/170163464: animate bubbles expanded view collapse with home gesture
+ */
+ @Keep
+ public static final SysPropBooleanFlag BUBBLES_HOME_GESTURE =
+ new SysPropBooleanFlag(1101, "persist.wm.debug.bubbles_home_gesture", true);
+
+ @Keep
+ public static final DeviceConfigBooleanFlag WM_ENABLE_PARTIAL_SCREEN_SHARING =
+ new DeviceConfigBooleanFlag(1102, "record_task_content",
+ NAMESPACE_WINDOW_MANAGER, false, true);
+
// 1200 - predictive back
@Keep
public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag(
@@ -178,6 +205,9 @@ public class Flags {
public static final SysPropBooleanFlag WM_ALWAYS_ENFORCE_PREDICTIVE_BACK =
new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false);
+ public static final BooleanFlag NEW_BACK_AFFORDANCE =
+ new BooleanFlag(1203, false /* default */, true /* teamfood */);
+
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
// | |
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index acb080a2eaaa..ab30db297fce 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -54,6 +54,7 @@ import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -99,6 +100,7 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -110,6 +112,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.MultiListLayout;
import com.android.systemui.MultiListLayout.MultiListAdapter;
+import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -154,6 +157,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private static final String TAG = "GlobalActionsDialogLite";
+ private static final String INTERACTION_JANK_TAG = "global_actions";
+
private static final boolean SHOW_SILENT_TOGGLE = true;
/* Valid settings for global actions keys.
@@ -499,7 +504,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mDialog.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
if (view != null) {
- mDialogLaunchAnimator.showFromView(mDialog, view);
+ mDialogLaunchAnimator.showFromView(mDialog, view,
+ new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+ INTERACTION_JANK_TAG));
} else {
mDialog.show();
}
@@ -1038,7 +1045,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
@Override
public boolean showBeforeProvisioning() {
- return false;
+ return Build.isDebuggable() && mGlobalSettings.getInt(
+ Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index a724d87e5c08..3eb3c80f4081 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -55,6 +55,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.Trace;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.view.IRemoteAnimationFinishedCallback;
@@ -157,7 +158,7 @@ public class KeyguardService extends Service {
Rect localBounds = new Rect(change.getEndAbsBounds());
localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y);
- out.add(new RemoteAnimationTarget(
+ final RemoteAnimationTarget target = new RemoteAnimationTarget(
taskId,
newModeToLegacyMode(change.getMode()),
change.getLeash(),
@@ -168,7 +169,15 @@ public class KeyguardService extends Service {
info.getChanges().size() - i,
new Point(), localBounds, new Rect(change.getEndAbsBounds()),
windowConfiguration, isNotInRecents, null /* startLeash */,
- change.getStartAbsBounds(), taskInfo, false /* allowEnterPip */));
+ change.getStartAbsBounds(), taskInfo, false /* allowEnterPip */);
+ // Use hasAnimatingParent to mark the anything below root task
+ if (taskId != -1 && change.getParent() != null) {
+ final TransitionInfo.Change parentChange = info.getChange(change.getParent());
+ if (parentChange != null && parentChange.getTaskInfo() != null) {
+ target.hasAnimatingParent = true;
+ }
+ }
+ out.add(target);
}
return out.toArray(new RemoteAnimationTarget[out.size()]);
}
@@ -189,8 +198,12 @@ public class KeyguardService extends Service {
}
}
+ // Wrap Keyguard going away animation
private static IRemoteTransition wrap(IRemoteAnimationRunner runner) {
return new IRemoteTransition.Stub() {
+ final ArrayMap<IBinder, IRemoteTransitionFinishedCallback> mFinishCallbacks =
+ new ArrayMap<>();
+
@Override
public void startAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
@@ -200,16 +213,37 @@ public class KeyguardService extends Service {
final RemoteAnimationTarget[] wallpapers = wrap(info, true /* wallpapers */);
final RemoteAnimationTarget[] nonApps = new RemoteAnimationTarget[0];
- // TODO: Remove this, and update alpha value in the IAnimationRunner.
- for (TransitionInfo.Change change : info.getChanges()) {
- t.setAlpha(change.getLeash(), 1.0f);
+ // Sets the alpha to 0 for the opening root task for fade in animation. And since
+ // the fade in animation can only apply on the first opening app, so set alpha to 1
+ // for anything else.
+ boolean foundOpening = false;
+ for (RemoteAnimationTarget target : apps) {
+ if (target.taskId != -1
+ && target.mode == RemoteAnimationTarget.MODE_OPENING
+ && !target.hasAnimatingParent) {
+ if (foundOpening) {
+ Log.w(TAG, "More than one opening target");
+ t.setAlpha(target.leash, 1.0f);
+ continue;
+ }
+ t.setAlpha(target.leash, 0.0f);
+ foundOpening = true;
+ } else {
+ t.setAlpha(target.leash, 1.0f);
+ }
}
t.apply();
+ synchronized (mFinishCallbacks) {
+ mFinishCallbacks.put(transition, finishCallback);
+ }
runner.onAnimationStart(getTransitionOldType(info.getType(), info.getFlags(), apps),
apps, wallpapers, nonApps,
new IRemoteAnimationFinishedCallback.Stub() {
@Override
public void onAnimationFinished() throws RemoteException {
+ synchronized (mFinishCallbacks) {
+ if (mFinishCallbacks.remove(transition) == null) return;
+ }
Slog.d(TAG, "Finish IRemoteAnimationRunner.");
finishCallback.onTransitionFinished(null /* wct */, null /* t */);
}
@@ -220,6 +254,20 @@ public class KeyguardService extends Service {
public void mergeAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t, IBinder mergeTarget,
IRemoteTransitionFinishedCallback finishCallback) {
+ try {
+ final IRemoteTransitionFinishedCallback origFinishCB;
+ synchronized (mFinishCallbacks) {
+ origFinishCB = mFinishCallbacks.remove(transition);
+ }
+ if (origFinishCB == null) {
+ // already finished (or not started yet), so do nothing.
+ return;
+ }
+ runner.onAnimationCancelled(false /* isKeyguardOccluded */);
+ origFinishCB.onTransitionFinished(null /* wct */, null /* t */);
+ } catch (RemoteException e) {
+ // nothing, we'll just let it finish on its own I guess.
+ }
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 99b57200a397..382323f3aed9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -26,6 +26,7 @@ import android.os.Handler
import android.os.RemoteException
import android.util.Log
import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
import android.view.SyncRtSurfaceTransactionApplier
import android.view.View
import androidx.annotation.VisibleForTesting
@@ -293,6 +294,8 @@ class KeyguardUnlockAnimationController @Inject constructor(
private val handler = Handler()
+ private val tmpFloat = FloatArray(9)
+
init {
with(surfaceBehindAlphaAnimator) {
duration = SURFACE_BEHIND_SWIPE_FADE_DURATION_MS
@@ -723,13 +726,27 @@ class KeyguardUnlockAnimationController @Inject constructor(
if (keyguardStateController.isSnappingKeyguardBackAfterSwipe) amount
else surfaceBehindAlpha
- applyParamsToSurface(
- SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
- surfaceBehindRemoteAnimationTarget!!.leash)
- .withMatrix(surfaceBehindMatrix)
- .withCornerRadius(roundedCornerRadius)
- .withAlpha(animationAlpha)
- .build())
+ // SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is unable
+ // to draw
+ val sc: SurfaceControl? = surfaceBehindRemoteAnimationTarget?.leash
+ if (keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE &&
+ sc?.isValid == true) {
+ with(SurfaceControl.Transaction()) {
+ setMatrix(sc, surfaceBehindMatrix, tmpFloat)
+ setCornerRadius(sc, roundedCornerRadius)
+ setAlpha(sc, animationAlpha)
+ apply()
+ }
+ } else {
+ applyParamsToSurface(
+ SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
+ surfaceBehindRemoteAnimationTarget!!.leash)
+ .withMatrix(surfaceBehindMatrix)
+ .withCornerRadius(roundedCornerRadius)
+ .withAlpha(animationAlpha)
+ .build()
+ )
+ }
}
/**
@@ -744,8 +761,11 @@ class KeyguardUnlockAnimationController @Inject constructor(
handler.removeCallbacksAndMessages(null)
// Make sure we made the surface behind fully visible, just in case. It should already be
- // fully visible. If the launcher is doing its own animation, let it continue without
- // forcing it to 1f.
+ // fully visible. The exit animation is finished, and we should not hold the leash anymore,
+ // so forcing it to 1f.
+ surfaceBehindAlphaAnimator.cancel()
+ surfaceBehindEntryAnimator.cancel()
+ surfaceBehindAlpha = 1f
setSurfaceBehindAppearAmount(1f)
launcherUnlockController?.setUnlockAmount(1f, false /* forceIfAnimating */)
@@ -910,4 +930,4 @@ class KeyguardUnlockAnimationController @Inject constructor(
return context.resources.getIntArray(R.array.config_foldedDeviceStates).isNotEmpty()
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 59c8aa0f20ad..e913d2b86306 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -40,6 +40,7 @@ import android.app.ActivityTaskManager;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.app.WindowConfiguration;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -121,6 +122,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -130,7 +132,6 @@ import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -555,15 +556,6 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
@Override
- public void onUserInfoChanged(int userId) {
- }
-
- @Override
- public void onClockVisibilityChanged() {
- adjustStatusBarLocked();
- }
-
- @Override
public void onDeviceProvisioned() {
sendUserPresentBroadcast();
synchronized (KeyguardViewMediator.this) {
@@ -933,6 +925,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
}
final RemoteAnimationTarget primary = apps[0];
+ final boolean isDream = (apps[0].taskInfo.topActivityType
+ == WindowConfiguration.ACTIVITY_TYPE_DREAM);
final SyncRtSurfaceTransactionApplier applier =
new SyncRtSurfaceTransactionApplier(
@@ -954,20 +948,24 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
final float surfaceHeight = primary.screenSpaceBounds.height();
- mUnoccludeMatrix.setTranslate(
- 0f,
- (1f - animatedValue)
- * surfaceHeight
- * UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT);
-
- SyncRtSurfaceTransactionApplier.SurfaceParams params =
+ // Fade for all types of activities.
+ SyncRtSurfaceTransactionApplier.SurfaceParams.Builder
+ paramsBuilder =
new SyncRtSurfaceTransactionApplier.SurfaceParams
.Builder(primary.leash)
- .withMatrix(mUnoccludeMatrix)
- .withCornerRadius(mWindowCornerRadius)
- .withAlpha(animatedValue)
- .build();
- applier.scheduleApply(params);
+ .withAlpha(animatedValue);
+ // Set translate if the occluding activity isn't Dream.
+ if (!isDream) {
+ mUnoccludeMatrix.setTranslate(
+ 0f,
+ (1f - animatedValue)
+ * surfaceHeight
+ * UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT);
+
+ paramsBuilder.withMatrix(mUnoccludeMatrix).withCornerRadius(
+ mWindowCornerRadius);
+ }
+ applier.scheduleApply(paramsBuilder.build());
});
mUnoccludeAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -1190,6 +1188,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mSystemReady = true;
doKeyguardLocked(null);
mUpdateMonitor.registerCallback(mUpdateCallback);
+ adjustStatusBarLocked();
mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback);
}
// Most services aren't available until the system reaches the ready state, so we
@@ -2497,10 +2496,18 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mInteractionJankMonitor.begin(
createInteractionJankMonitorConf("DismissPanel"));
+ // Apply the opening animation on root task if exists
+ RemoteAnimationTarget aniTarget = apps[0];
+ for (RemoteAnimationTarget tmpTarget : apps) {
+ if (tmpTarget.taskId != -1 && !tmpTarget.hasAnimatingParent) {
+ aniTarget = tmpTarget;
+ break;
+ }
+ }
// Pass the surface and metadata to the unlock animation controller.
mKeyguardUnlockAnimationControllerLazy.get()
.notifyStartSurfaceBehindRemoteAnimation(
- apps[0], startTime, mSurfaceBehindRemoteAnimationRequested);
+ aniTarget, startTime, mSurfaceBehindRemoteAnimationRequested);
} else {
mInteractionJankMonitor.begin(
createInteractionJankMonitorConf("RemoteAnimationDisabled"));
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/WindowAddedViewLifecycleOwner.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/WindowAddedViewLifecycleOwner.kt
new file mode 100644
index 000000000000..55c7ac9fb0cc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/WindowAddedViewLifecycleOwner.kt
@@ -0,0 +1,114 @@
+package com.android.systemui.lifecycle
+
+import android.view.View
+import android.view.ViewTreeObserver
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
+
+/**
+ * [LifecycleOwner] for Window-added Views.
+ *
+ * These are [View] instances that are added to a `Window` using the `WindowManager` API.
+ *
+ * This implementation goes to:
+ * * The <b>CREATED</b> `Lifecycle.State` when the view gets attached to the window but the window
+ * is not yet visible
+ * * The <b>STARTED</b> `Lifecycle.State` when the view is attached to the window and the window is
+ * visible
+ * * The <b>RESUMED</b> `Lifecycle.State` when the view is attached to the window and the window is
+ * visible and the window receives focus
+ *
+ * In table format:
+ * ```
+ * | ----------------------------------------------------------------------------- |
+ * | View attached to window | Window visible | Window has focus | Lifecycle state |
+ * | ----------------------------------------------------------------------------- |
+ * | not attached | Any | INITIALIZED |
+ * | ----------------------------------------------------------------------------- |
+ * | | not visible | Any | CREATED |
+ * | ----------------------------------------------------- |
+ * | attached | | not focused | STARTED |
+ * | | is visible |----------------------------------- |
+ * | | | has focus | RESUMED |
+ * | ----------------------------------------------------------------------------- |
+ * ```
+ * ### Notes
+ * * [dispose] must be invoked when the [LifecycleOwner] is done and won't be reused
+ * * It is always better for [LifecycleOwner] implementations to be more explicit than just
+ * listening to the state of the `Window`. E.g. if the code that added the `View` to the `Window`
+ * already has access to the correct state to know when that `View` should become visible and when
+ * it is ready to receive interaction from the user then it already knows when to move to `STARTED`
+ * and `RESUMED`, respectively. In that case, it's better to implement your own `LifecycleOwner`
+ * instead of relying on the `Window` callbacks.
+ */
+class WindowAddedViewLifecycleOwner
+@JvmOverloads
+constructor(
+ private val view: View,
+ registryFactory: (LifecycleOwner) -> LifecycleRegistry = { LifecycleRegistry(it) },
+) : LifecycleOwner {
+
+ private val windowAttachListener =
+ object : ViewTreeObserver.OnWindowAttachListener {
+ override fun onWindowAttached() {
+ updateCurrentState()
+ }
+
+ override fun onWindowDetached() {
+ updateCurrentState()
+ }
+ }
+ private val windowFocusListener =
+ ViewTreeObserver.OnWindowFocusChangeListener { updateCurrentState() }
+ private val windowVisibilityListener =
+ ViewTreeObserver.OnWindowVisibilityChangeListener { updateCurrentState() }
+
+ private val registry = registryFactory(this)
+
+ init {
+ setCurrentState(Lifecycle.State.INITIALIZED)
+
+ with(view.viewTreeObserver) {
+ addOnWindowAttachListener(windowAttachListener)
+ addOnWindowVisibilityChangeListener(windowVisibilityListener)
+ addOnWindowFocusChangeListener(windowFocusListener)
+ }
+
+ updateCurrentState()
+ }
+
+ override fun getLifecycle(): Lifecycle {
+ return registry
+ }
+
+ /**
+ * Disposes of this [LifecycleOwner], performing proper clean-up.
+ *
+ * <p>Invoke this when the instance is finished and won't be reused.
+ */
+ fun dispose() {
+ with(view.viewTreeObserver) {
+ removeOnWindowAttachListener(windowAttachListener)
+ removeOnWindowVisibilityChangeListener(windowVisibilityListener)
+ removeOnWindowFocusChangeListener(windowFocusListener)
+ }
+ }
+
+ private fun updateCurrentState() {
+ val state =
+ when {
+ !view.isAttachedToWindow -> Lifecycle.State.INITIALIZED
+ view.windowVisibility != View.VISIBLE -> Lifecycle.State.CREATED
+ !view.hasWindowFocus() -> Lifecycle.State.STARTED
+ else -> Lifecycle.State.RESUMED
+ }
+ setCurrentState(state)
+ }
+
+ private fun setCurrentState(state: Lifecycle.State) {
+ if (registry.currentState != state) {
+ registry.currentState = state
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index eff025f771a5..d0da18aaba05 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -27,6 +27,8 @@ import com.android.systemui.log.LogBufferFactory;
import com.android.systemui.log.LogcatEchoTracker;
import com.android.systemui.log.LogcatEchoTrackerDebug;
import com.android.systemui.log.LogcatEchoTrackerProd;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
+import com.android.systemui.util.Compile;
import dagger.Module;
import dagger.Provides;
@@ -48,11 +50,17 @@ public class LogModule {
@Provides
@SysUISingleton
@NotificationLog
- public static LogBuffer provideNotificationsLogBuffer(LogBufferFactory factory) {
- return factory.create("NotifLog", 1000 /* maxSize */, false /* systrace */);
+ public static LogBuffer provideNotificationsLogBuffer(
+ LogBufferFactory factory,
+ NotifPipelineFlags notifPipelineFlags) {
+ int maxSize = 1000;
+ if (Compile.IS_DEBUG && notifPipelineFlags.isDevLoggingEnabled()) {
+ maxSize *= 10;
+ }
+ return factory.create("NotifLog", maxSize, false /* systrace */);
}
- /** Provides a logging buffer for all logs related to the data layer of notifications. */
+ /** Provides a logging buffer for logs related to heads up presentation of notifications. */
@Provides
@SysUISingleton
@NotificationHeadsUpLog
@@ -60,6 +68,14 @@ public class LogModule {
return factory.create("NotifHeadsUpLog", 1000);
}
+ /** Provides a logging buffer for notification interruption calculations. */
+ @Provides
+ @SysUISingleton
+ @NotificationInterruptLog
+ public static LogBuffer provideNotificationInterruptLogBuffer(LogBufferFactory factory) {
+ return factory.create("NotifInterruptLog", 100);
+ }
+
/** Provides a logging buffer for all logs for lockscreen to shade transition events. */
@Provides
@SysUISingleton
@@ -251,4 +267,14 @@ public class LogModule {
return new LogcatEchoTrackerProd();
}
}
+
+ /**
+ * Provides a {@link LogBuffer} for use by the status bar network controller.
+ */
+ @Provides
+ @SysUISingleton
+ @StatusBarNetworkControllerLog
+ public static LogBuffer provideStatusBarNetworkControllerBuffer(LogBufferFactory factory) {
+ return factory.create("StatusBarNetworkControllerLog", 20);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationInterruptLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationInterruptLog.java
new file mode 100644
index 000000000000..760fbf3928b6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationInterruptLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for notification interruption logging. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface NotificationInterruptLog {
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java b/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarNetworkControllerLog.java
index 60123ab97fd7..f26b3164f488 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarNetworkControllerLog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,23 +14,22 @@
* limitations under the License.
*/
-package com.android.wm.shell.hidedisplaycutout;
+package com.android.systemui.log.dagger;
-import android.content.res.Configuration;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
-import androidx.annotation.NonNull;
+import com.android.systemui.log.LogBuffer;
-import com.android.wm.shell.common.annotations.ExternalThread;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
-import java.io.PrintWriter;
+import javax.inject.Qualifier;
/**
- * Interface to engage hide display cutout feature.
+ * A {@link LogBuffer} for {@link com.android.systemui.statusbar.connectivity.NetworkController}
*/
-@ExternalThread
-public interface HideDisplayCutout {
- /**
- * Notifies {@link Configuration} changed.
- */
- void onConfigurationChanged(Configuration newConfig);
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface StatusBarNetworkControllerLog {
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
index f1d5e94bfbf3..d0826553ad2c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
@@ -17,20 +17,17 @@
package com.android.systemui.media
import android.animation.ArgbEvaluator
-import android.animation.ValueAnimator.AnimatorUpdateListener
import android.animation.ValueAnimator
+import android.animation.ValueAnimator.AnimatorUpdateListener
import android.content.Context
import android.content.res.ColorStateList
-import android.graphics.Color
-import android.graphics.drawable.GradientDrawable
-import android.graphics.drawable.RippleDrawable
import android.content.res.Configuration
import android.content.res.Configuration.UI_MODE_NIGHT_YES
+import android.graphics.drawable.RippleDrawable
import com.android.internal.R
import com.android.internal.annotations.VisibleForTesting
import com.android.settingslib.Utils
import com.android.systemui.monet.ColorScheme
-import com.android.systemui.util.getColorWithAlpha
/**
* A [ColorTransition] is an object that updates the colors of views each time [updateColorScheme]
@@ -106,7 +103,6 @@ class ColorSchemeTransition internal constructor(
constructor(context: Context, mediaViewHolder: MediaViewHolder) :
this(context, mediaViewHolder, ::AnimatingColorTransition)
- private var isGradientEnabled = true
val bgColor = context.getColor(com.android.systemui.R.color.material_dynamic_secondary95)
val surfaceColor = animatingColorTransitionFactory(
bgColor,
@@ -187,16 +183,6 @@ class ColorSchemeTransition internal constructor(
mediaViewHolder.seekBar.progressBackgroundTintList = ColorStateList.valueOf(textTertiary)
}
- val bgGradientStart = animatingColorTransitionFactory(
- bgColor,
- albumGradientPicker(::backgroundStartFromScheme, 0.25f)
- ) { _ -> updateAlbumGradient() }
-
- val bgGradientEnd = animatingColorTransitionFactory(
- bgColor,
- albumGradientPicker(::backgroundEndFromScheme, 0.9f)
- ) { _ -> updateAlbumGradient() }
-
val colorTransitions = arrayOf(
surfaceColor,
colorSeamless,
@@ -206,37 +192,13 @@ class ColorSchemeTransition internal constructor(
textPrimaryInverse,
textSecondary,
textTertiary,
- bgGradientStart,
- bgGradientEnd
)
- private fun updateAlbumGradient() {
- val gradient = mediaViewHolder.albumView.foreground?.mutate()
- if (gradient is GradientDrawable) {
- gradient.colors = intArrayOf(
- bgGradientStart?.currentColor ?: 0,
- bgGradientEnd?.currentColor ?: 0)
- }
- }
-
- private fun albumGradientPicker(
- inner: (ColorScheme) -> Int,
- targetAlpha: Float
- ): (ColorScheme) -> Int {
- return { scheme ->
- if (isGradientEnabled)
- getColorWithAlpha(inner(scheme), targetAlpha)
- else
- Color.TRANSPARENT
- }
- }
-
private fun loadDefaultColor(id: Int): Int {
return Utils.getColorAttr(context, id).defaultColor
}
- fun updateColorScheme(colorScheme: ColorScheme?, enableGradient: Boolean) {
- isGradientEnabled = enableGradient
+ fun updateColorScheme(colorScheme: ColorScheme?) {
colorTransitions.forEach { it.updateColorScheme(colorScheme) }
colorScheme?.let { mediaViewHolder.gutsViewHolder.colorScheme = colorScheme }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 159784575b69..012d76651b23 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -38,7 +38,9 @@ import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.Icon;
+import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.TransitionDrawable;
import android.media.session.MediaController;
import android.media.session.MediaSession;
@@ -70,6 +72,7 @@ import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.GhostedViewLaunchAnimatorController;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.bluetooth.BroadcastDialogController;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -81,6 +84,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.ColorUtilKt;
import com.android.systemui.util.animation.TransitionLayout;
import com.android.systemui.util.time.SystemClock;
@@ -188,6 +192,11 @@ public class MediaControlPanel {
private final SeekBarViewModel.EnabledChangeListener mEnabledChangeListener =
this::setIsSeekBarEnabled;
+ private final BroadcastDialogController mBroadcastDialogController;
+ private boolean mIsCurrentBroadcastedApp = false;
+ private boolean mShowBroadcastDialogButton = false;
+ private String mSwitchBroadcastApp;
+
/**
* Initialize a new control panel
*
@@ -213,7 +222,8 @@ public class MediaControlPanel {
MediaUiEventLogger logger,
KeyguardStateController keyguardStateController,
ActivityIntentHelper activityIntentHelper,
- NotificationLockscreenUserManager lockscreenUserManager) {
+ NotificationLockscreenUserManager lockscreenUserManager,
+ BroadcastDialogController broadcastDialogController) {
mContext = context;
mBackgroundExecutor = backgroundExecutor;
mMainExecutor = mainExecutor;
@@ -230,6 +240,7 @@ public class MediaControlPanel {
mKeyguardStateController = keyguardStateController;
mActivityIntentHelper = activityIntentHelper;
mLockscreenUserManager = lockscreenUserManager;
+ mBroadcastDialogController = broadcastDialogController;
mSeekBarViewModel.setLogSeek(() -> {
if (mPackageName != null && mInstanceId != null) {
@@ -449,7 +460,10 @@ public class MediaControlPanel {
final MediaController controller = getController();
mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller));
- bindOutputSwitcherChip(data);
+ // Show the broadcast dialog button only when the le audio is enabled.
+ mShowBroadcastDialogButton =
+ data.getDevice() != null && data.getDevice().getShowBroadcastButton();
+ bindOutputSwitcherAndBroadcastButton(mShowBroadcastDialogButton, data);
bindGutsMenuForPlayer(data);
bindPlayerContentDescription(data);
bindScrubbingTime(data);
@@ -467,21 +481,40 @@ public class MediaControlPanel {
Trace.endSection();
}
- private void bindOutputSwitcherChip(MediaData data) {
- // Output switcher chip
+ private void bindOutputSwitcherAndBroadcastButton(boolean showBroadcastButton, MediaData data) {
ViewGroup seamlessView = mMediaViewHolder.getSeamless();
seamlessView.setVisibility(View.VISIBLE);
ImageView iconView = mMediaViewHolder.getSeamlessIcon();
TextView deviceName = mMediaViewHolder.getSeamlessText();
final MediaDeviceData device = data.getDevice();
- // Disable clicking on output switcher for invalid devices and resumption controls
- final boolean seamlessDisabled = (device != null && !device.getEnabled())
- || data.getResumption();
- final float seamlessAlpha = seamlessDisabled ? DISABLED_ALPHA : 1.0f;
- mMediaViewHolder.getSeamlessButton().setAlpha(seamlessAlpha);
- seamlessView.setEnabled(!seamlessDisabled);
- CharSequence deviceString = mContext.getString(R.string.media_seamless_other_device);
+ final boolean enabled;
+ final boolean seamlessDisabled;
+ final int iconResource;
+ CharSequence deviceString;
+ if (showBroadcastButton) {
+ // TODO(b/233698402): Use the package name instead of app label to avoid the
+ // unexpected result.
+ mIsCurrentBroadcastedApp = device != null
+ && TextUtils.equals(device.getName(),
+ MediaDataUtils.getAppLabel(mContext, mPackageName, mContext.getString(
+ R.string.bt_le_audio_broadcast_dialog_unknown_name)));
+ seamlessDisabled = !mIsCurrentBroadcastedApp;
+ // Always be enabled if the broadcast button is shown
+ enabled = true;
+ deviceString = mContext.getString(R.string.bt_le_audio_broadcast_dialog_unknown_name);
+ iconResource = R.drawable.settings_input_antenna;
+ } else {
+ // Disable clicking on output switcher for invalid devices and resumption controls
+ seamlessDisabled = (device != null && !device.getEnabled()) || data.getResumption();
+ enabled = !seamlessDisabled;
+ deviceString = mContext.getString(R.string.media_seamless_other_device);
+ iconResource = R.drawable.ic_media_home_devices;
+ }
+
+ mMediaViewHolder.getSeamlessButton().setAlpha(seamlessDisabled ? DISABLED_ALPHA : 1.0f);
+ seamlessView.setEnabled(enabled);
+
if (device != null) {
Drawable icon = device.getIcon();
if (icon instanceof AdaptiveIcon) {
@@ -494,7 +527,7 @@ public class MediaControlPanel {
deviceString = device.getName();
} else {
// Set to default icon
- iconView.setImageResource(R.drawable.ic_media_home_devices);
+ iconView.setImageResource(iconResource);
}
deviceName.setText(deviceString);
seamlessView.setContentDescription(deviceString);
@@ -503,21 +536,39 @@ public class MediaControlPanel {
if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
return;
}
- mLogger.logOpenOutputSwitcher(mUid, mPackageName, mInstanceId);
- if (device.getIntent() != null) {
- if (device.getIntent().isActivity()) {
- mActivityStarter.startActivity(
- device.getIntent().getIntent(), true);
+
+ if (showBroadcastButton) {
+ // If the current media app is not broadcasted and users press the outputer
+ // button, we should pop up the broadcast dialog to check do they want to
+ // switch broadcast to the other media app, otherwise we still pop up the
+ // media output dialog.
+ if (!mIsCurrentBroadcastedApp) {
+ mLogger.logOpenBroadcastDialog(mUid, mPackageName, mInstanceId);
+ mSwitchBroadcastApp = device.getName().toString();
+ mBroadcastDialogController.createBroadcastDialog(mSwitchBroadcastApp,
+ mPackageName, true, mMediaViewHolder.getSeamlessButton());
} else {
- try {
- device.getIntent().send();
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Device pending intent was canceled");
- }
+ mLogger.logOpenOutputSwitcher(mUid, mPackageName, mInstanceId);
+ mMediaOutputDialogFactory.create(mPackageName, true,
+ mMediaViewHolder.getSeamlessButton());
}
} else {
- mMediaOutputDialogFactory.create(mPackageName, true,
- mMediaViewHolder.getSeamlessButton());
+ mLogger.logOpenOutputSwitcher(mUid, mPackageName, mInstanceId);
+ if (device.getIntent() != null) {
+ if (device.getIntent().isActivity()) {
+ mActivityStarter.startActivity(
+ device.getIntent().getIntent(), true);
+ } else {
+ try {
+ device.getIntent().send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Device pending intent was canceled");
+ }
+ }
+ } else {
+ mMediaOutputDialogFactory.create(mPackageName, true,
+ mMediaViewHolder.getSeamlessButton());
+ }
}
});
}
@@ -648,7 +699,18 @@ public class MediaControlPanel {
}
if (wallpaperColors != null) {
mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT);
- artwork = getScaledBackground(artworkIcon, width, height);
+ Drawable albumArt = getScaledBackground(artworkIcon, width, height);
+ GradientDrawable gradient = (GradientDrawable) mContext
+ .getDrawable(R.drawable.qs_media_scrim);
+ gradient.setColors(new int[] {
+ ColorUtilKt.getColorWithAlpha(
+ MediaColorSchemesKt.backgroundStartFromScheme(mutableColorScheme),
+ 0.25f),
+ ColorUtilKt.getColorWithAlpha(
+ MediaColorSchemesKt.backgroundEndFromScheme(mutableColorScheme),
+ 0.9f),
+ });
+ artwork = new LayerDrawable(new Drawable[] { albumArt, gradient });
isArtworkBound = true;
} else {
// If there's no artwork, use colors from the app icon
@@ -700,7 +762,7 @@ public class MediaControlPanel {
}
// Transition Colors to current color scheme
- mColorSchemeTransition.updateColorScheme(colorScheme, mIsArtworkBound);
+ mColorSchemeTransition.updateColorScheme(colorScheme);
// App icon - use notification icon
ImageView appIconView = mMediaViewHolder.getAppIcon();
@@ -956,16 +1018,13 @@ public class MediaControlPanel {
private void bindScrubbingTime(MediaData data) {
ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
- ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
int elapsedTimeId = mMediaViewHolder.getScrubbingElapsedTimeView().getId();
int totalTimeId = mMediaViewHolder.getScrubbingTotalTimeView().getId();
boolean visible = scrubbingTimeViewsEnabled(data.getSemanticActions()) && mIsScrubbing;
setVisibleAndAlpha(expandedSet, elapsedTimeId, visible);
setVisibleAndAlpha(expandedSet, totalTimeId, visible);
- // Never show in collapsed
- setVisibleAndAlpha(collapsedSet, elapsedTimeId, false);
- setVisibleAndAlpha(collapsedSet, totalTimeId, false);
+ // Collapsed view is always GONE as set in XML, so doesn't need to be updated dynamically
}
private boolean scrubbingTimeViewsEnabled(@Nullable MediaButton semanticActions) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index aeeef6b24a1c..5b2cda038bbd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -215,7 +215,10 @@ data class MediaDeviceData
val intent: PendingIntent? = null,
/** Unique id for this device */
- val id: String? = null
+ val id: String? = null,
+
+ /** Whether or not to show the broadcast button */
+ val showBroadcastButton: Boolean
) {
/**
* Check whether [MediaDeviceData] objects are equal in all fields except the icon. The icon
@@ -230,6 +233,7 @@ data class MediaDeviceData
return enabled == other.enabled &&
name == other.name &&
intent == other.intent &&
- id == other.id
+ id == other.id &&
+ showBroadcastButton == other.showBroadcastButton
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 6a69d427929e..30ba476abce2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -686,7 +686,8 @@ class MediaDataManager(
val enabled = deviceIntent != null && deviceIntent.isActivity
val deviceDrawable = Icon.createWithResource(sbn.packageName, deviceIcon)
.loadDrawable(sbn.getPackageContext(context))
- device = MediaDeviceData(enabled, deviceDrawable, deviceName, deviceIntent)
+ device = MediaDeviceData(enabled, deviceDrawable, deviceName, deviceIntent,
+ showBroadcastButton = false)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataUtils.java b/packages/SystemUI/src/com/android/systemui/media/MediaDataUtils.java
new file mode 100644
index 000000000000..b8185b9de7e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataUtils.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.media;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.text.TextUtils;
+
+public class MediaDataUtils {
+
+ public static String getAppLabel(Context context, String packageName, String unknownName) {
+ if (TextUtils.isEmpty(packageName)) {
+ return null;
+ }
+ final PackageManager packageManager = context.getPackageManager();
+ ApplicationInfo applicationInfo;
+ try {
+ applicationInfo = packageManager.getApplicationInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ applicationInfo = null;
+ }
+ final String applicationName =
+ (String) (applicationInfo != null
+ ? packageManager.getApplicationLabel(applicationInfo)
+ : unknownName);
+ return applicationName;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index baed1c4286e9..83050503a18f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -16,15 +16,23 @@
package com.android.systemui.media
+import android.bluetooth.BluetoothLeBroadcast
+import android.bluetooth.BluetoothLeBroadcastMetadata
+import android.content.Context
import android.graphics.drawable.Drawable
import android.media.MediaRouter2Manager
import android.media.session.MediaController
+import android.text.TextUtils
+import android.util.Log
import androidx.annotation.AnyThread
import androidx.annotation.MainThread
import androidx.annotation.WorkerThread
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
+import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.systemui.Dumpable
+import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
@@ -36,16 +44,20 @@ import java.util.concurrent.Executor
import javax.inject.Inject
private const val PLAYBACK_TYPE_UNKNOWN = 0
+private const val TAG = "MediaDeviceManager"
+private const val DEBUG = true
/**
* Provides information about the route (ie. device) where playback is occurring.
*/
class MediaDeviceManager @Inject constructor(
+ private val context: Context,
private val controllerFactory: MediaControllerFactory,
private val localMediaManagerFactory: LocalMediaManagerFactory,
private val mr2manager: MediaRouter2Manager,
private val muteAwaitConnectionManagerFactory: MediaMuteAwaitConnectionManagerFactory,
private val configurationController: ConfigurationController,
+ private val localBluetoothManager: LocalBluetoothManager?,
@Main private val fgExecutor: Executor,
@Background private val bgExecutor: Executor,
dumpManager: DumpManager
@@ -147,7 +159,8 @@ class MediaDeviceManager @Inject constructor(
val controller: MediaController?,
val localMediaManager: LocalMediaManager,
val muteAwaitConnectionManager: MediaMuteAwaitConnectionManager?
- ) : LocalMediaManager.DeviceCallback, MediaController.Callback() {
+ ) : LocalMediaManager.DeviceCallback, MediaController.Callback(),
+ BluetoothLeBroadcast.Callback {
val token
get() = controller?.sessionToken
@@ -166,7 +179,7 @@ class MediaDeviceManager @Inject constructor(
// A device that is not yet connected but is expected to connect imminently. Because it's
// expected to connect imminently, it should be displayed as the current device.
private var aboutToConnectDeviceOverride: AboutToConnectDevice? = null
-
+ private var broadcastDescription: String? = null
private val configListener = object : ConfigurationController.ConfigurationListener {
override fun onLocaleListChanged() {
updateCurrent()
@@ -238,7 +251,11 @@ class MediaDeviceManager @Inject constructor(
) {
aboutToConnectDeviceOverride = AboutToConnectDevice(
fullMediaDevice = localMediaManager.getMediaDeviceById(deviceAddress),
- backupMediaDeviceData = MediaDeviceData(enabled = true, deviceIcon, deviceName)
+ backupMediaDeviceData = MediaDeviceData(
+ /* enabled */ enabled = true,
+ /* icon */ deviceIcon,
+ /* name */ deviceName,
+ /* showBroadcastButton */ showBroadcastButton = false)
)
updateCurrent()
}
@@ -248,23 +265,127 @@ class MediaDeviceManager @Inject constructor(
updateCurrent()
}
+
+ override fun onBroadcastStarted(reason: Int, broadcastId: Int) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastStarted(), reason = $reason , broadcastId = $broadcastId")
+ }
+ updateCurrent()
+ }
+
+ override fun onBroadcastStartFailed(reason: Int) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastStartFailed(), reason = $reason")
+ }
+ }
+
+ override fun onBroadcastMetadataChanged(broadcastId: Int,
+ metadata: BluetoothLeBroadcastMetadata) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = $broadcastId , " +
+ "metadata = $metadata")
+ }
+ updateCurrent()
+ }
+
+ override fun onBroadcastStopped(reason: Int, broadcastId: Int) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastStopped(), reason = $reason , broadcastId = $broadcastId")
+
+ }
+ updateCurrent()
+ }
+
+ override fun onBroadcastStopFailed(reason: Int) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastStopFailed(), reason = $reason")
+ }
+ }
+
+ override fun onBroadcastUpdated(reason: Int, broadcastId: Int) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastUpdated(), reason = $reason , broadcastId = $broadcastId")
+ }
+ updateCurrent()
+ }
+
+ override fun onBroadcastUpdateFailed(reason: Int, broadcastId: Int) {
+ if (DEBUG) {
+ Log.d(TAG, "onBroadcastUpdateFailed(), reason = $reason , " +
+ "broadcastId = $broadcastId")
+ }
+ }
+
+ override fun onPlaybackStarted(reason: Int, broadcastId: Int) {}
+
+ override fun onPlaybackStopped(reason: Int, broadcastId: Int) {}
+
@WorkerThread
private fun updateCurrent() {
- val aboutToConnect = aboutToConnectDeviceOverride
- if (aboutToConnect != null &&
- aboutToConnect.fullMediaDevice == null &&
- aboutToConnect.backupMediaDeviceData != null) {
+ if (isLeAudioBroadcastEnabled()) {
+ current = MediaDeviceData(
+ /* enabled */ true,
+ /* icon */ context.getDrawable(R.drawable.settings_input_antenna),
+ /* name */ broadcastDescription,
+ /* intent */ null,
+ /* showBroadcastButton */ showBroadcastButton = true)
+ } else {
+ val aboutToConnect = aboutToConnectDeviceOverride
+ if (aboutToConnect != null &&
+ aboutToConnect.fullMediaDevice == null &&
+ aboutToConnect.backupMediaDeviceData != null) {
// Only use [backupMediaDeviceData] when we don't have [fullMediaDevice].
current = aboutToConnect.backupMediaDeviceData
return
+ }
+ val device = aboutToConnect?.fullMediaDevice
+ ?: localMediaManager.currentConnectedDevice
+ val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it) }
+
+ // If we have a controller but get a null route, then don't trust the device
+ val enabled = device != null && (controller == null || route != null)
+ val name = route?.name?.toString() ?: device?.name
+ current = MediaDeviceData(enabled, device?.iconWithoutBackground, name,
+ id = device?.id, showBroadcastButton = false)
}
- val device = aboutToConnect?.fullMediaDevice ?: localMediaManager.currentConnectedDevice
- val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it) }
+ }
- // If we have a controller but get a null route, then don't trust the device
- val enabled = device != null && (controller == null || route != null)
- val name = route?.name?.toString() ?: device?.name
- current = MediaDeviceData(enabled, device?.iconWithoutBackground, name, id = device?.id)
+ private fun isLeAudioBroadcastEnabled(): Boolean {
+ if (localBluetoothManager != null) {
+ val profileManager = localBluetoothManager.profileManager
+ if (profileManager != null) {
+ val bluetoothLeBroadcast = profileManager.leAudioBroadcastProfile
+ if (bluetoothLeBroadcast != null && bluetoothLeBroadcast.isEnabled(null)) {
+ getBroadcastingInfo(bluetoothLeBroadcast)
+ return true
+ } else if (DEBUG) {
+ Log.d(TAG, "Can not get LocalBluetoothLeBroadcast")
+ }
+ } else if (DEBUG) {
+ Log.d(TAG, "Can not get LocalBluetoothProfileManager")
+ }
+ } else if (DEBUG) {
+ Log.d(TAG, "Can not get LocalBluetoothManager")
+ }
+ return false
+ }
+
+ private fun getBroadcastingInfo(bluetoothLeBroadcast: LocalBluetoothLeBroadcast) {
+ var currentBroadcastedApp = bluetoothLeBroadcast.appSourceName
+ // TODO(b/233698402): Use the package name instead of app label to avoid the
+ // unexpected result.
+ // Check the current media app's name is the same with current broadcast app's name
+ // or not.
+ var mediaApp = MediaDataUtils.getAppLabel(
+ context, localMediaManager.packageName,
+ context.getString(R.string.bt_le_audio_broadcast_dialog_unknown_name))
+ var isCurrentBroadcastedApp = TextUtils.equals(mediaApp, currentBroadcastedApp)
+ if (isCurrentBroadcastedApp) {
+ broadcastDescription = context.getString(
+ R.string.broadcasting_description_is_broadcasting)
+ } else {
+ broadcastDescription = currentBroadcastedApp
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index ed5c1933af25..458ed4059b3b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -23,6 +23,7 @@ import android.annotation.IntDef
import android.content.Context
import android.content.res.Configuration
import android.graphics.Rect
+import android.util.Log
import android.util.MathUtils
import android.view.View
import android.view.ViewGroup
@@ -48,6 +49,8 @@ import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.traceSection
import javax.inject.Inject
+private val TAG: String = MediaHierarchyManager::class.java.simpleName
+
/**
* Similarly to isShown but also excludes views that have 0 alpha
*/
@@ -141,8 +144,7 @@ class MediaHierarchyManager @Inject constructor(
animatedFraction)
// When crossfading, let's keep the bounds at the right location during fading
boundsProgress = if (animationCrossFadeProgress < 0.5f) 0.0f else 1.0f
- currentAlpha = calculateAlphaFromCrossFade(animationCrossFadeProgress,
- instantlyShowAtEnd = false)
+ currentAlpha = calculateAlphaFromCrossFade(animationCrossFadeProgress)
} else {
// If we're not crossfading, let's interpolate from the start alpha to 1.0f
currentAlpha = MathUtils.lerp(animationStartAlpha, 1.0f, animatedFraction)
@@ -273,7 +275,7 @@ class MediaHierarchyManager @Inject constructor(
if (value >= 0) {
updateTargetState()
// Setting the alpha directly, as the below call will use it to update the alpha
- carouselAlpha = calculateAlphaFromCrossFade(field, instantlyShowAtEnd = true)
+ carouselAlpha = calculateAlphaFromCrossFade(field)
applyTargetStateIfNotAnimating()
}
}
@@ -411,18 +413,10 @@ class MediaHierarchyManager @Inject constructor(
* @param crossFadeProgress The current cross fade progress. 0.5f means it's just switching
* between the start and the end location and the content is fully faded, while 0.75f means
* that we're halfway faded in again in the target state.
- *
- * @param instantlyShowAtEnd should the view be instantly shown at the end. This is needed
- * to avoid fadinging in when the target was hidden anyway.
*/
- private fun calculateAlphaFromCrossFade(
- crossFadeProgress: Float,
- instantlyShowAtEnd: Boolean
- ): Float {
+ private fun calculateAlphaFromCrossFade(crossFadeProgress: Float): Float {
if (crossFadeProgress <= 0.5f) {
return 1.0f - crossFadeProgress / 0.5f
- } else if (instantlyShowAtEnd) {
- return 1.0f
} else {
return (crossFadeProgress - 0.5f) / 0.5f
}
@@ -964,6 +958,14 @@ class MediaHierarchyManager @Inject constructor(
top,
left + currentBounds.width(),
top + currentBounds.height())
+
+ if (mediaFrame.childCount > 0) {
+ val child = mediaFrame.getChildAt(0)
+ if (mediaFrame.height < child.height) {
+ Log.wtf(TAG, "mediaFrame height is too small for child: " +
+ "${mediaFrame.height} vs ${child.height}")
+ }
+ }
}
if (isCrossFadeAnimatorRunning) {
// When cross-fading with an animation, we only notify the media carousel of the
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
new file mode 100644
index 000000000000..6802da347c93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.media
+
+import android.app.ActivityOptions
+import android.content.Intent
+import android.media.projection.IMediaProjection
+import android.media.projection.MediaProjectionManager.EXTRA_MEDIA_PROJECTION
+import android.os.Binder
+import android.os.Bundle
+import android.os.IBinder
+import android.view.View
+import com.android.internal.app.ChooserActivity
+import com.android.internal.app.chooser.NotSelectableTargetInfo
+import com.android.internal.app.chooser.TargetInfo
+import com.android.systemui.util.AsyncActivityLauncher
+import com.android.systemui.R;
+import javax.inject.Inject
+
+class MediaProjectionAppSelectorActivity @Inject constructor(
+ private val activityLauncher: AsyncActivityLauncher
+) : ChooserActivity() {
+
+ public override fun onCreate(bundle: Bundle?) {
+ val queryIntent = Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ intent.putExtra(Intent.EXTRA_INTENT, queryIntent)
+
+ // TODO(b/235465652) Use resource lexeme
+ intent.putExtra(Intent.EXTRA_TITLE, "Record or cast an app")
+
+ super.onCreate(bundle)
+
+ // TODO(b/235465652) we should update VisD of the title and add an icon
+ findViewById<View>(R.id.title)?.visibility = View.VISIBLE
+ }
+
+ override fun appliedThemeResId(): Int =
+ R.style.Theme_SystemUI_MediaProjectionAppSelector
+
+ override fun startSelected(which: Int, always: Boolean, filtered: Boolean) {
+ val currentListAdapter = mChooserMultiProfilePagerAdapter.activeListAdapter
+ val targetInfo = currentListAdapter.targetInfoForPosition(which, filtered) ?: return
+ if (targetInfo is NotSelectableTargetInfo) return
+
+ val intent = createIntent(targetInfo)
+
+ val launchToken: IBinder = Binder("media_projection_launch_token")
+ val activityOptions = ActivityOptions.makeBasic()
+ activityOptions.launchCookie = launchToken
+
+ val userHandle = mMultiProfilePagerAdapter.activeListAdapter.userHandle
+
+ // Launch activity asynchronously and wait for the result, launching of an activity
+ // is typically very fast, so we don't show any loaders.
+ // We wait for the activity to be launched to make sure that the window of the activity
+ // is created and ready to be captured.
+ val activityStarted = activityLauncher
+ .startActivityAsUser(intent, userHandle, activityOptions.toBundle()) {
+ onTargetActivityLaunched(launchToken)
+ }
+
+ // Rely on the ActivityManager to pop up a dialog regarding app suspension
+ // and return false if suspended
+ if (!targetInfo.isSuspended && activityStarted) {
+ // TODO(b/222078415) track activity launch
+ }
+ }
+
+ private fun createIntent(target: TargetInfo): Intent {
+ val intent = Intent(target.resolvedIntent)
+
+ // Launch the app in a new task, so it won't be in the host's app task
+ intent.flags = intent.flags or Intent.FLAG_ACTIVITY_NEW_TASK
+
+ // Remove activity forward result flag as this activity will
+ // return the media projection session
+ intent.flags = intent.flags and Intent.FLAG_ACTIVITY_FORWARD_RESULT.inv()
+
+ return intent
+ }
+
+ override fun onDestroy() {
+ activityLauncher.destroy()
+ super.onDestroy()
+ }
+
+ override fun onActivityStarted(cti: TargetInfo) {
+ // do nothing
+ }
+
+ private fun onTargetActivityLaunched(launchToken: IBinder) {
+ val mediaProjectionBinder = intent.getIBinderExtra(EXTRA_MEDIA_PROJECTION)
+ val projection = IMediaProjection.Stub.asInterface(mediaProjectionBinder)
+
+ projection.launchCookie = launchToken
+
+ val intent = Intent()
+ intent.putExtra(EXTRA_MEDIA_PROJECTION, projection.asBinder())
+ setResult(RESULT_OK, intent)
+ setForceSendResultForMediaProjection()
+ finish()
+ }
+
+ override fun shouldGetOnlyDefaultActivities() = false
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 38604091c409..397bffcaa64c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -40,7 +40,10 @@ import android.text.style.StyleSpan;
import android.util.Log;
import android.view.Window;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.Utils;
@@ -53,6 +56,7 @@ public class MediaProjectionPermissionActivity extends Activity
private String mPackageName;
private int mUid;
private IMediaProjectionManager mService;
+ private FeatureFlags mFeatureFlags;
private AlertDialog mDialog;
@@ -60,6 +64,7 @@ public class MediaProjectionPermissionActivity extends Activity
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ mFeatureFlags = Dependency.get(FeatureFlags.class);
mPackageName = getCallingPackage();
IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE);
mService = IMediaProjectionManager.Stub.asInterface(b);
@@ -141,14 +146,22 @@ public class MediaProjectionPermissionActivity extends Activity
dialogTitle = getString(R.string.media_projection_dialog_title, appName);
}
- mDialog = new AlertDialog.Builder(this, R.style.Theme_SystemUI_Dialog)
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this,
+ R.style.Theme_SystemUI_Dialog)
.setTitle(dialogTitle)
.setIcon(R.drawable.ic_media_projection_permission)
.setMessage(dialogText)
.setPositiveButton(R.string.media_projection_action_text, this)
.setNeutralButton(android.R.string.cancel, this)
- .setOnCancelListener(this)
- .create();
+ .setOnCancelListener(this);
+
+ if (isPartialScreenSharingEnabled()) {
+ // This is a temporary entry point before we have a new permission dialog
+ // TODO(b/233183090): this activity should be redesigned to have a dropdown selector
+ dialogBuilder.setNegativeButton("App", this);
+ }
+
+ mDialog = dialogBuilder.create();
SystemUIDialog.registerDismissListener(mDialog);
SystemUIDialog.applyFlags(mDialog);
@@ -177,6 +190,15 @@ public class MediaProjectionPermissionActivity extends Activity
if (which == AlertDialog.BUTTON_POSITIVE) {
setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName));
}
+
+ if (isPartialScreenSharingEnabled() && which == AlertDialog.BUTTON_NEGATIVE) {
+ IMediaProjection projection = createProjection(mUid, mPackageName);
+ final Intent intent = new Intent(this, MediaProjectionAppSelectorActivity.class);
+ intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
+ projection.asBinder());
+ intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ startActivity(intent);
+ }
} catch (RemoteException e) {
Log.e(TAG, "Error granting projection permission", e);
setResult(RESULT_CANCELED);
@@ -188,10 +210,14 @@ public class MediaProjectionPermissionActivity extends Activity
}
}
+ private IMediaProjection createProjection(int uid, String packageName) throws RemoteException {
+ return mService.createProjection(uid, packageName,
+ MediaProjectionManager.TYPE_SCREEN_CAPTURE, false /* permanentGrant */);
+ }
+
private Intent getMediaProjectionIntent(int uid, String packageName)
throws RemoteException {
- IMediaProjection projection = mService.createProjection(uid, packageName,
- MediaProjectionManager.TYPE_SCREEN_CAPTURE, false /* permanentGrant */);
+ IMediaProjection projection = createProjection(uid, packageName);
Intent intent = new Intent();
intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION, projection.asBinder());
return intent;
@@ -201,4 +227,8 @@ public class MediaProjectionPermissionActivity extends Activity
public void onCancel(DialogInterface dialog) {
finish();
}
+
+ private boolean isPartialScreenSharingEnabled() {
+ return mFeatureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt
index 52f5cc568ba4..0baf01e7476f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt
@@ -176,6 +176,11 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger)
logger.logWithInstanceId(MediaUiEvent.MEDIA_RECOMMENDATION_CARD_TAP, 0, packageName,
instanceId)
}
+
+ fun logOpenBroadcastDialog(uid: Int, packageName: String, instanceId: InstanceId) {
+ logger.logWithInstanceId(MediaUiEvent.MEDIA_OPEN_BROADCAST_DIALOG, uid, packageName,
+ instanceId)
+ }
}
enum class MediaUiEvent(val metricId: Int) : UiEventLogger.UiEventEnum {
@@ -273,7 +278,10 @@ enum class MediaUiEvent(val metricId: Int) : UiEventLogger.UiEventEnum {
MEDIA_RECOMMENDATION_ITEM_TAP(1044),
@UiEvent(doc = "User tapped on a media recommendation card")
- MEDIA_RECOMMENDATION_CARD_TAP(1045);
+ MEDIA_RECOMMENDATION_CARD_TAP(1045),
+
+ @UiEvent(doc = "User opened the broadcast dialog from a media control")
+ MEDIA_OPEN_BROADCAST_DIALOG(1079);
override fun getId() = metricId
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt
new file mode 100644
index 000000000000..e33a1b909d48
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.media.dagger
+
+import android.app.Activity
+import com.android.systemui.media.MediaProjectionAppSelectorActivity
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+@Module
+abstract class MediaProjectionModule {
+
+ @Binds
+ @IntoMap
+ @ClassKey(MediaProjectionAppSelectorActivity::class)
+ abstract fun provideMediaProjectionAppSelectorActivity(
+ activity: MediaProjectionAppSelectorActivity): Activity
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index f2f275323d58..e9b6af44ddf3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -42,14 +42,11 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
private static final String TAG = "MediaOutputAdapter";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private final MediaOutputDialog mMediaOutputDialog;
private ViewGroup mConnectedItem;
private boolean mIncludeDynamicGroup;
- public MediaOutputAdapter(MediaOutputController controller,
- MediaOutputDialog mediaOutputDialog) {
+ public MediaOutputAdapter(MediaOutputController controller) {
super(controller);
- mMediaOutputDialog = mediaOutputDialog;
setHasStableIds(true);
}
@@ -63,7 +60,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
@Override
public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) {
final int size = mController.getMediaDevices().size();
- if (position == size && mController.isZeroMode()) {
+ if (position == size) {
viewHolder.onBind(CUSTOMIZED_ITEM_PAIR_NEW, false /* topMargin */,
true /* bottomMargin */);
} else if (position < size) {
@@ -78,7 +75,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
@Override
public long getItemId(int position) {
final int size = mController.getMediaDevices().size();
- if (position == size && mController.isZeroMode()) {
+ if (position == size) {
return -1;
} else if (position < size) {
return ((List<MediaDevice>) (mController.getMediaDevices()))
@@ -91,11 +88,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
@Override
public int getItemCount() {
- if (mController.isZeroMode()) {
- // Add extra one for "pair new" or dynamic group
- return mController.getMediaDevices().size() + 1;
- }
- return mController.getMediaDevices().size();
+ // Add extra one for "pair new"
+ return mController.getMediaDevices().size() + 1;
}
class MediaDeviceViewHolder extends MediaDeviceBaseViewHolder {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 9b964a5cc09f..0d5cab688d72 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -91,6 +91,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
private TextView mHeaderSubtitle;
private ImageView mHeaderIcon;
private ImageView mAppResourceIcon;
+ private ImageView mBroadcastIcon;
private RecyclerView mDevicesRecyclerView;
private LinearLayout mDeviceListLayout;
private LinearLayout mCastAppLayout;
@@ -239,6 +240,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
mAppButton = mDialogView.requireViewById(R.id.launch_app_button);
mAppResourceIcon = mDialogView.requireViewById(R.id.app_source_icon);
mCastAppLayout = mDialogView.requireViewById(R.id.cast_app_section);
+ mBroadcastIcon = mDialogView.requireViewById(R.id.broadcast_icon);
mDeviceListLayout.getViewTreeObserver().addOnGlobalLayoutListener(
mDeviceListLayoutListener);
@@ -366,6 +368,9 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
mStopButton.setEnabled(true);
mStopButton.setText(getStopButtonText());
mStopButton.setOnClickListener(v -> onStopButtonClick());
+
+ mBroadcastIcon.setVisibility(getBroadcastIconVisibility());
+ mBroadcastIcon.setOnClickListener(v -> onBroadcastIconClick());
}
private void updateButtonBackgroundColorFilter() {
@@ -482,7 +487,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
abstract int getStopButtonVisibility();
public CharSequence getStopButtonText() {
- return mContext.getText(R.string.media_output_dialog_button_stop_casting);
+ return mContext.getText(R.string.keyboard_key_media_stop);
}
public void onStopButtonClick() {
@@ -490,6 +495,14 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
dismiss();
}
+ public int getBroadcastIconVisibility() {
+ return View.GONE;
+ }
+
+ public void onBroadcastIconClick() {
+ // Do nothing.
+ }
+
public boolean isBroadcastSupported() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
index 8f065461c22d..310469dd5415 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
@@ -74,7 +74,7 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog {
MediaOutputBroadcastDialog(Context context, boolean aboveStatusbar,
BroadcastSender broadcastSender, MediaOutputController mediaOutputController) {
super(context, broadcastSender, mediaOutputController);
- mAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
+ mAdapter = new MediaOutputAdapter(mMediaOutputController);
// TODO(b/226710953): Move the part to MediaOutputBaseDialog for every class
// that extends MediaOutputBaseDialog
if (!aboveStatusbar) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index f8b5edcbd11f..7e3275da8c31 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -16,7 +16,7 @@
package com.android.systemui.media.dialog;
-import static android.provider.Settings.ACTION_BLUETOOTH_PAIRING_SETTINGS;
+import static android.provider.Settings.ACTION_BLUETOOTH_SETTINGS;
import android.annotation.CallbackExecutor;
import android.app.AlertDialog;
@@ -300,6 +300,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
return;
}
try {
+ synchronized (mMediaDevicesLock) {
+ mMediaDevices.removeIf(MediaDevice::isMutingExpectedDevice);
+ }
mAudioManager.cancelMuteAwaitConnection(mAudioManager.getMutingExpectedDevice());
} catch (Exception e) {
Log.d(TAG, "Unable to cancel mute await connection");
@@ -711,22 +714,6 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
return false;
}
- boolean isZeroMode() {
- synchronized (mMediaDevicesLock) {
- if (mMediaDevices.size() == 1) {
- final MediaDevice device = mMediaDevices.iterator().next();
- // Add "pair new" only when local output device exists
- final int type = device.getDeviceType();
- if (type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE
- || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE
- || type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE) {
- return true;
- }
- }
- return false;
- }
- }
-
void launchBluetoothPairing(View view) {
ActivityLaunchAnimator.Controller controller =
mDialogLaunchAnimator.createActivityLaunchController(view);
@@ -736,7 +723,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
}
Intent launchIntent =
- new Intent(ACTION_BLUETOOTH_PAIRING_SETTINGS)
+ new Intent(ACTION_BLUETOOTH_SETTINGS)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
final Intent deepLinkIntent =
new Intent(Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY);
@@ -858,6 +845,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|| features.contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK));
}
+ boolean isBluetoothLeDevice(@NonNull MediaDevice device) {
+ return device.isBLEDevice();
+ }
+
boolean isBroadcastSupported() {
LocalBluetoothLeBroadcast broadcast =
mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index 026a3055b01b..fc4773d3c6dd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -42,7 +42,7 @@ public class MediaOutputDialog extends MediaOutputBaseDialog {
MediaOutputController mediaOutputController, UiEventLogger uiEventLogger) {
super(context, broadcastSender, mediaOutputController);
mUiEventLogger = uiEventLogger;
- mAdapter = new MediaOutputAdapter(mMediaOutputController, this);
+ mAdapter = new MediaOutputAdapter(mMediaOutputController);
if (!aboveStatusbar) {
getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
}
@@ -99,12 +99,17 @@ public class MediaOutputDialog extends MediaOutputBaseDialog {
@Override
public boolean isBroadcastSupported() {
- return mMediaOutputController.isBroadcastSupported();
+ boolean isBluetoothLeDevice = false;
+ if (mMediaOutputController.getCurrentConnectedMediaDevice() != null) {
+ isBluetoothLeDevice = mMediaOutputController.isBluetoothLeDevice(
+ mMediaOutputController.getCurrentConnectedMediaDevice());
+ }
+ return mMediaOutputController.isBroadcastSupported() && isBluetoothLeDevice;
}
@Override
public CharSequence getStopButtonText() {
- int resId = R.string.media_output_dialog_button_stop_casting;
+ int resId = R.string.keyboard_key_media_stop;
if (isBroadcastSupported() && mMediaOutputController.isPlaying()
&& !mMediaOutputController.isBluetoothLeBroadcastEnabled()) {
resId = R.string.media_output_broadcast;
@@ -129,6 +134,17 @@ public class MediaOutputDialog extends MediaOutputBaseDialog {
}
}
+ @Override
+ public int getBroadcastIconVisibility() {
+ return (isBroadcastSupported() && mMediaOutputController.isBluetoothLeBroadcastEnabled())
+ ? View.VISIBLE : View.GONE;
+ }
+
+ @Override
+ public void onBroadcastIconClick() {
+ startLeBroadcastDialog();
+ }
+
@VisibleForTesting
public enum MediaOutputEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "The MediaOutput dialog became visible on the screen.")
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index 5581fb861e6a..8249a7c0779c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -21,8 +21,10 @@ import android.media.AudioManager
import android.media.session.MediaSessionManager
import android.os.PowerExemptionManager
import android.view.View
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.media.nearby.NearbyMediaDevicesManager
@@ -48,6 +50,7 @@ class MediaOutputDialogFactory @Inject constructor(
private val powerExemptionManager: PowerExemptionManager
) {
companion object {
+ private const val INTERACTION_JANK_TAG = "media_output"
var mediaOutputDialog: MediaOutputDialog? = null
}
@@ -67,7 +70,13 @@ class MediaOutputDialogFactory @Inject constructor(
// Show the dialog.
if (view != null) {
- dialogLaunchAnimator.showFromView(dialog, view)
+ dialogLaunchAnimator.showFromView(
+ dialog, view,
+ cuj = DialogCuj(
+ InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+ INTERACTION_JANK_TAG
+ )
+ )
} else {
dialog.show()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
deleted file mode 100644
index ba2f006fc58e..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.media.dialog;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-
-import com.android.settingslib.media.MediaDevice;
-import com.android.systemui.R;
-
-import java.util.List;
-
-/**
- * Adapter for media output dynamic group dialog.
- */
-//TODO: clear this class after new UI updated
-public class MediaOutputGroupAdapter extends MediaOutputBaseAdapter {
-
- private static final String TAG = "MediaOutputGroupAdapter";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- private final List<MediaDevice> mGroupMediaDevices;
-
- public MediaOutputGroupAdapter(MediaOutputController controller) {
- super(controller);
- mGroupMediaDevices = controller.getGroupMediaDevices();
- }
-
- @Override
- public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
- int viewType) {
- super.onCreateViewHolder(viewGroup, viewType);
-
- return new GroupViewHolder(mHolderView);
- }
-
- @Override
- public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) {
- // Add "Group"
- if (position == 0) {
- viewHolder.onBind(CUSTOMIZED_ITEM_GROUP, true /* topMargin */,
- false /* bottomMargin */);
- return;
- }
- // Add available devices
- final int newPosition = position - 1;
- final int size = mGroupMediaDevices.size();
- if (newPosition < size) {
- viewHolder.onBind(mGroupMediaDevices.get(newPosition), false /* topMargin */,
- newPosition == (size - 1) /* bottomMargin */, position);
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "Incorrect position: " + position);
- }
- }
-
- @Override
- public int getItemCount() {
- // Require extra item for group volume operation
- return mGroupMediaDevices.size() + 1;
- }
-
- @Override
- CharSequence getItemTitle(MediaDevice device) {
- return super.getItemTitle(device);
- }
-
- class GroupViewHolder extends MediaDeviceBaseViewHolder {
-
- GroupViewHolder(View view) {
- super(view);
- }
-
- @Override
- void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
- super.onBind(device, topMargin, bottomMargin, position);
- mCheckBox.setVisibility(View.VISIBLE);
- mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
- onCheckBoxClicked(isChecked, device);
- });
- boolean isCurrentSeekbarInvisible = mSeekBar.getVisibility() == View.GONE;
- setTwoLineLayout(device, false /* bFocused */, true /* showSeekBar */,
- false /* showProgressBar */, false /* showSubtitle*/);
- initSeekbar(device, isCurrentSeekbarInvisible);
- final List<MediaDevice> selectedDevices = mController.getSelectedMediaDevice();
- if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
- mCheckBox.setButtonDrawable(R.drawable.ic_check_box);
- mCheckBox.setChecked(false);
- mCheckBox.setEnabled(true);
- } else if (isDeviceIncluded(selectedDevices, device)) {
- if (selectedDevices.size() == 1 || !isDeviceIncluded(
- mController.getDeselectableMediaDevice(), device)) {
- mCheckBox.setButtonDrawable(getDisabledCheckboxDrawable());
- mCheckBox.setChecked(true);
- mCheckBox.setEnabled(false);
- } else {
- mCheckBox.setButtonDrawable(R.drawable.ic_check_box);
- mCheckBox.setChecked(true);
- mCheckBox.setEnabled(true);
- }
- }
- }
-
- @Override
- void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
- if (customizedItem == CUSTOMIZED_ITEM_GROUP) {
- setTwoLineLayout(mContext.getText(R.string.media_output_dialog_group),
- true /* bFocused */, true /* showSeekBar */, false /* showProgressBar */,
- false /* showSubtitle*/);
- mTitleIcon.setImageDrawable(getSpeakerDrawable());
- mCheckBox.setVisibility(View.GONE);
- initSessionSeekbar();
- }
- }
-
- private void onCheckBoxClicked(boolean isChecked, MediaDevice device) {
- if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
- mController.addDeviceToPlayMedia(device);
- } else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(),
- device)) {
- mController.removeDeviceFromPlayMedia(device);
- }
- }
-
- private Drawable getDisabledCheckboxDrawable() {
- final Drawable drawable = mContext.getDrawable(R.drawable.ic_check_box_blue_24dp)
- .mutate();
- final Bitmap checkbox = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
- drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
- final Canvas canvas = new Canvas(checkbox);
- TypedValue value = new TypedValue();
- mContext.getTheme().resolveAttribute(android.R.attr.disabledAlpha, value, true);
- drawable.setAlpha((int) (value.getFloat() * 255));
- drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- drawable.draw(canvas);
-
- return drawable;
- }
-
- private boolean isDeviceIncluded(List<MediaDevice> deviceList, MediaDevice targetDevice) {
- for (MediaDevice device : deviceList) {
- if (TextUtils.equals(device.getId(), targetDevice.getId())) {
- return true;
- }
- }
- return false;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
deleted file mode 100644
index bb3f969c86df..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.media.dialog;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.view.View;
-import android.view.WindowManager;
-
-import androidx.core.graphics.drawable.IconCompat;
-
-import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastSender;
-
-/**
- * Dialog for media output group.
- */
-// TODO(b/203073091): Remove this class once group logic been implemented.
-public class MediaOutputGroupDialog extends MediaOutputBaseDialog {
-
- MediaOutputGroupDialog(Context context, boolean aboveStatusbar, BroadcastSender broadcastSender,
- MediaOutputController mediaOutputController) {
- super(context, broadcastSender, mediaOutputController);
- mMediaOutputController.resetGroupMediaDevices();
- mAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
- if (!aboveStatusbar) {
- getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
- }
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Override
- int getHeaderIconRes() {
- return R.drawable.ic_arrow_back;
- }
-
- @Override
- IconCompat getHeaderIcon() {
- return null;
- }
-
- @Override
- int getHeaderIconSize() {
- return mContext.getResources().getDimensionPixelSize(
- R.dimen.media_output_dialog_header_back_icon_size);
- }
-
- @Override
- CharSequence getHeaderText() {
- return mContext.getString(R.string.media_output_dialog_add_output);
- }
-
- @Override
- CharSequence getHeaderSubtitle() {
- final int size = mMediaOutputController.getSelectedMediaDevice().size();
- if (size == 1) {
- return mContext.getText(R.string.media_output_dialog_single_device);
- }
- return mContext.getString(R.string.media_output_dialog_multiple_devices, size);
- }
-
- @Override
- Drawable getAppSourceIcon() {
- return null;
- }
-
- @Override
- int getStopButtonVisibility() {
- return View.VISIBLE;
- }
-
- @Override
- void onHeaderIconClick() {
- // Given that we launched the media output group dialog from the media output dialog,
- // dismissing this dialog will show the media output dialog again.
- dismiss();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java
index 7c0481049098..2c35db337cda 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java
@@ -37,11 +37,6 @@ public class MediaDreamComplication implements Complication {
}
@Override
- public int getRequiredTypeAvailability() {
- return COMPLICATION_TYPE_CAST_INFO;
- }
-
- @Override
public ViewHolder createView(ComplicationViewModel model) {
return mComponentFactory.create().getViewHolder();
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt
index 3cc99a8ef77e..e95976f555f8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt
@@ -27,4 +27,4 @@ interface ChipInfoCommon {
fun getTimeoutMs(): Long
}
-const val DEFAULT_TIMEOUT_MILLIS = 3000L
+const val DEFAULT_TIMEOUT_MILLIS = 4000L
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 7cc52e428218..c9fce794f57f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -25,13 +25,14 @@ import android.graphics.drawable.Drawable
import android.os.PowerManager
import android.os.SystemClock
import android.util.Log
-import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
-import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
-import android.widget.LinearLayout
+import android.view.accessibility.AccessibilityManager
+import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS
+import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS
+import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
import com.android.internal.widget.CachingIconView
import com.android.settingslib.Utils
import com.android.systemui.R
@@ -53,19 +54,23 @@ import com.android.systemui.util.view.ViewUtil
abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
internal val context: Context,
internal val logger: MediaTttLogger,
- private val windowManager: WindowManager,
+ internal val windowManager: WindowManager,
private val viewUtil: ViewUtil,
@Main private val mainExecutor: DelayableExecutor,
+ private val accessibilityManager: AccessibilityManager,
private val tapGestureDetector: TapGestureDetector,
private val powerManager: PowerManager,
@LayoutRes private val chipLayoutRes: Int
) {
- /** The window layout parameters we'll use when attaching the view to a window. */
+
+ /**
+ * Window layout params that will be used as a starting point for the [windowLayoutParams] of
+ * all subclasses.
+ */
@SuppressLint("WrongConstant") // We're allowed to use TYPE_VOLUME_OVERLAY
- private val windowLayoutParams = WindowManager.LayoutParams().apply {
+ internal val commonWindowLayoutParams = WindowManager.LayoutParams().apply {
width = WindowManager.LayoutParams.WRAP_CONTENT
height = WindowManager.LayoutParams.WRAP_CONTENT
- gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL)
type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY
flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
title = WINDOW_TITLE
@@ -73,6 +78,14 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
setTrustedOverlay()
}
+ /**
+ * The window layout parameters we'll use when attaching the view to a window.
+ *
+ * Subclasses must override this to provide their specific layout params, and they should use
+ * [commonWindowLayoutParams] as part of their layout params.
+ */
+ internal abstract val windowLayoutParams: WindowManager.LayoutParams
+
/** The chip view currently being displayed. Null if the chip is not being displayed. */
private var chipView: ViewGroup? = null
@@ -110,10 +123,16 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
}
// Cancel and re-set the chip timeout each time we get a new state.
+ val timeout = accessibilityManager.getRecommendedTimeoutMillis(
+ chipInfo.getTimeoutMs().toInt(),
+ // Not all chips have controls so FLAG_CONTENT_CONTROLS might be superfluous, but
+ // include it just to be safe.
+ FLAG_CONTENT_ICONS or FLAG_CONTENT_TEXT or FLAG_CONTENT_CONTROLS
+ )
cancelChipViewTimeout?.run()
cancelChipViewTimeout = mainExecutor.executeDelayed(
{ removeChip(MediaTttRemovalReason.REASON_TIMEOUT) },
- chipInfo.getTimeoutMs()
+ timeout.toLong()
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 072263fcf38c..99a5b8b2d450 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -16,16 +16,23 @@
package com.android.systemui.media.taptotransfer.receiver
+import android.annotation.SuppressLint
import android.app.StatusBarManager
import android.content.Context
+import android.graphics.PointF
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.media.MediaRoute2Info
import android.os.Handler
import android.os.PowerManager
import android.util.Log
+import android.view.Gravity
+import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
+import android.view.accessibility.AccessibilityManager
+import androidx.core.graphics.ColorUtils
+import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
@@ -35,6 +42,7 @@ import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCom
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.gesture.TapGestureDetector
+import com.android.systemui.util.animation.AnimationUtil.Companion.frames
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.view.ViewUtil
import javax.inject.Inject
@@ -52,6 +60,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
windowManager: WindowManager,
viewUtil: ViewUtil,
mainExecutor: DelayableExecutor,
+ accessibilityManager: AccessibilityManager,
tapGestureDetector: TapGestureDetector,
powerManager: PowerManager,
@Main private val mainHandler: Handler,
@@ -62,10 +71,22 @@ class MediaTttChipControllerReceiver @Inject constructor(
windowManager,
viewUtil,
mainExecutor,
+ accessibilityManager,
tapGestureDetector,
powerManager,
R.layout.media_ttt_chip_receiver
) {
+ @SuppressLint("WrongConstant") // We're allowed to use LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ override val windowLayoutParams = commonWindowLayoutParams.apply {
+ gravity = Gravity.BOTTOM.or(Gravity.CENTER_HORIZONTAL)
+ // Params below are needed for the ripple to work correctly
+ width = WindowManager.LayoutParams.MATCH_PARENT
+ height = WindowManager.LayoutParams.MATCH_PARENT
+ layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ fitInsetsTypes = 0 // Ignore insets from all system bars
+ flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ }
+
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
override fun updateMediaTapToTransferReceiverDisplay(
@StatusBarManager.MediaTransferReceiverState displayState: Int,
@@ -128,6 +149,19 @@ class MediaTttChipControllerReceiver @Inject constructor(
)
}
+ override fun animateChipIn(chipView: ViewGroup) {
+ val appIconView = chipView.requireViewById<View>(R.id.app_icon)
+ appIconView.animate()
+ .translationYBy(-1 * getTranslationAmount().toFloat())
+ .setDuration(30.frames)
+ .start()
+ appIconView.animate()
+ .alpha(1f)
+ .setDuration(5.frames)
+ .start()
+ startRipple(chipView.requireViewById(R.id.ripple))
+ }
+
override fun getIconSize(isAppIcon: Boolean): Int? =
context.resources.getDimensionPixelSize(
if (isAppIcon) {
@@ -136,6 +170,45 @@ class MediaTttChipControllerReceiver @Inject constructor(
R.dimen.media_ttt_generic_icon_size_receiver
}
)
+
+ /** Returns the amount that the chip will be translated by in its intro animation. */
+ private fun getTranslationAmount(): Int {
+ return context.resources.getDimensionPixelSize(R.dimen.media_ttt_receiver_vert_translation)
+ }
+
+ private fun startRipple(rippleView: ReceiverChipRippleView) {
+ if (rippleView.rippleInProgress) {
+ // Skip if ripple is still playing
+ return
+ }
+ rippleView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
+ override fun onViewDetachedFromWindow(view: View?) {}
+
+ override fun onViewAttachedToWindow(view: View?) {
+ if (view == null) {
+ return
+ }
+ val attachedRippleView = view as ReceiverChipRippleView
+ layoutRipple(attachedRippleView)
+ attachedRippleView.startRipple()
+ attachedRippleView.removeOnAttachStateChangeListener(this)
+ }
+ })
+ }
+
+ private fun layoutRipple(rippleView: ReceiverChipRippleView) {
+ val windowBounds = windowManager.currentWindowMetrics.bounds
+ val height = windowBounds.height()
+ val width = windowBounds.width()
+
+ rippleView.radius = height / 5f
+ // Center the ripple on the bottom of the screen in the middle.
+ rippleView.origin = PointF(/* x= */ width / 2f, /* y= */ height.toFloat())
+
+ val color = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
+ val colorWithAlpha = ColorUtils.setAlphaComponent(color, 70)
+ rippleView.setColor(colorWithAlpha)
+ }
}
data class ChipReceiverInfo(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreenListener.aidl b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
index 46e4299f99fa..fed546bf52c2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreenListener.aidl
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-package com.android.wm.shell.stagesplit;
+package com.android.systemui.media.taptotransfer.receiver
+
+import android.content.Context
+import android.util.AttributeSet
+import com.android.systemui.ripple.RippleView
/**
- * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks.
+ * An expanding ripple effect for the media tap-to-transfer receiver chip.
*/
-oneway interface ISplitScreenListener {
-
- /**
- * Called when the stage position changes.
- */
- void onStagePositionChanged(int stage, int position);
-
- /**
- * Called when a task changes stages.
- */
- void onTaskStageChanged(int taskId, int stage, boolean visible);
-} \ No newline at end of file
+class ReceiverChipRippleView(
+ context: Context?, attrs: AttributeSet?
+) : RippleView(context, attrs) {
+ init {
+ setRippleFill(true)
+ duration = 3000L
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 54b4380e2443..797a7701413b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -21,9 +21,11 @@ import android.content.Context
import android.media.MediaRoute2Info
import android.os.PowerManager
import android.util.Log
+import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
+import android.view.accessibility.AccessibilityManager
import android.widget.TextView
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
@@ -53,6 +55,7 @@ class MediaTttChipControllerSender @Inject constructor(
windowManager: WindowManager,
viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
+ accessibilityManager: AccessibilityManager,
tapGestureDetector: TapGestureDetector,
powerManager: PowerManager,
private val uiEventLogger: MediaTttSenderUiEventLogger
@@ -62,10 +65,15 @@ class MediaTttChipControllerSender @Inject constructor(
windowManager,
viewUtil,
mainExecutor,
+ accessibilityManager,
tapGestureDetector,
powerManager,
R.layout.media_ttt_chip
) {
+ override val windowLayoutParams = commonWindowLayoutParams.apply {
+ gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL)
+ }
+
private var currentlyDisplayedChipState: ChipStateSender? = null
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 779292c43340..6bc50a6b2406 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -17,11 +17,19 @@
package com.android.systemui.navigationbar;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
import static com.android.systemui.accessibility.SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON;
import static com.android.systemui.accessibility.SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
import android.content.ContentResolver;
import android.content.Context;
@@ -50,6 +58,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.io.PrintWriter;
@@ -325,6 +334,23 @@ public final class NavBarHelper implements
void updateAssistantAvailable(boolean available);
}
+ static @TransitionMode int transitionMode(boolean isTransient, int appearance) {
+ final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_NAVIGATION_BARS;
+ if (isTransient) {
+ return MODE_SEMI_TRANSPARENT;
+ } else if ((appearance & lightsOutOpaque) == lightsOutOpaque) {
+ return MODE_LIGHTS_OUT;
+ } else if ((appearance & APPEARANCE_LOW_PROFILE_BARS) != 0) {
+ return MODE_LIGHTS_OUT_TRANSPARENT;
+ } else if ((appearance & APPEARANCE_OPAQUE_NAVIGATION_BARS) != 0) {
+ return MODE_OPAQUE;
+ } else if ((appearance & APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS) != 0) {
+ return MODE_SEMI_TRANSPARENT;
+ } else {
+ return MODE_TRANSPARENT;
+ }
+ }
+
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("NavbarTaskbarFriendster");
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index aa38b781229c..281ef9495d8e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -27,9 +27,6 @@ import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.containsType;
-import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
-import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
-import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
@@ -39,6 +36,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE;
+import static com.android.systemui.navigationbar.NavBarHelper.transitionMode;
import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
@@ -48,11 +46,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_I
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG_WINDOW_STATE;
import static com.android.systemui.statusbar.phone.CentralSurfaces.dumpBarTransitions;
@@ -86,7 +80,7 @@ import android.util.Log;
import android.view.Display;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
-import android.view.InsetsState;
+import android.view.InsetsFrameProvider;
import android.view.InsetsState.InternalInsetsType;
import android.view.InsetsVisibilities;
import android.view.KeyEvent;
@@ -111,6 +105,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.util.LatencyTracker;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.Gefingerpoken;
@@ -215,8 +210,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private final UiEventLogger mUiEventLogger;
private final NavBarHelper mNavBarHelper;
private final NotificationShadeDepthController mNotificationShadeDepthController;
- private final UserContextProvider mUserContextProvider;
private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener;
+ private final UserContextProvider mUserContextProvider;
private final RegionSamplingHelper mRegionSamplingHelper;
private final int mNavColorSampleMargin;
private NavigationBarFrame mFrame;
@@ -631,6 +626,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
}, mainExecutor, bgExecutor);
+ mView.setBackgroundExecutor(bgExecutor);
mView.setEdgeBackGestureHandler(mEdgeBackGestureHandler);
mNavBarMode = mNavigationModeController.addListener(mModeChangedListener);
}
@@ -1075,7 +1071,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName) {
+ @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName,
+ LetterboxDetails[] letterboxDetails) {
if (displayId != mDisplayId) {
return;
}
@@ -1152,23 +1149,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
return false;
}
- private static @TransitionMode int transitionMode(boolean isTransient, int appearance) {
- final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_NAVIGATION_BARS;
- if (isTransient) {
- return MODE_SEMI_TRANSPARENT;
- } else if ((appearance & lightsOutOpaque) == lightsOutOpaque) {
- return MODE_LIGHTS_OUT;
- } else if ((appearance & APPEARANCE_LOW_PROFILE_BARS) != 0) {
- return MODE_LIGHTS_OUT_TRANSPARENT;
- } else if ((appearance & APPEARANCE_OPAQUE_NAVIGATION_BARS) != 0) {
- return MODE_OPAQUE;
- } else if ((appearance & APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS) != 0) {
- return MODE_SEMI_TRANSPARENT;
- } else {
- return MODE_TRANSPARENT;
- }
- }
-
@Override
public void disable(int displayId, int state1, int state2, boolean animate) {
if (displayId != mDisplayId) {
@@ -1654,12 +1634,14 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
| WindowManager.LayoutParams.FLAG_SLIPPERY,
PixelFormat.TRANSLUCENT);
lp.gravity = gravity;
- lp.providedInternalInsets = new Insets[InsetsState.SIZE];
if (insetsHeight != -1) {
- lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] =
- Insets.of(0, height - insetsHeight, 0, 0);
+ lp.providedInsets = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(ITYPE_NAVIGATION_BAR, Insets.of(0, 0, 0, insetsHeight))
+ };
} else {
- lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] = null;
+ lp.providedInsets = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(ITYPE_NAVIGATION_BAR)
+ };
}
lp.token = new Binder();
lp.accessibilityTitle = userContext.getString(R.string.nav_bar);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index d756af77d2a9..2a7c8716f036 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -69,7 +69,6 @@ import java.util.Optional;
import javax.inject.Inject;
-
/** A controller to handle navigation bars. */
@SysUISingleton
public class NavigationBarController implements
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 3fc9afe6ea94..9e1ba5f723a3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -72,6 +72,7 @@ import com.android.systemui.navigationbar.buttons.NearestTouchFrame;
import com.android.systemui.navigationbar.buttons.RotationContextButton;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.recents.Recents;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shared.rotation.FloatingRotationButton;
import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
import com.android.systemui.shared.rotation.RotationButtonController;
@@ -81,13 +82,13 @@ import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/** */
@@ -97,6 +98,8 @@ public class NavigationBarView extends FrameLayout {
final static boolean ALTERNATE_CAR_MODE_UI = false;
+ private Executor mBgExecutor;
+
// The current view is one of mHorizontal or mVertical depending on the current configuration
View mCurrentView = null;
private View mVertical;
@@ -349,6 +352,10 @@ public class NavigationBarView extends FrameLayout {
notifyVerticalChangedListener(mIsVertical);
}
+ public void setBackgroundExecutor(Executor bgExecutor) {
+ mBgExecutor = bgExecutor;
+ }
+
public void setTouchHandler(Gefingerpoken touchHandler) {
mTouchHandler = touchHandler;
}
@@ -768,8 +775,8 @@ public class NavigationBarView extends FrameLayout {
updateSlippery();
reloadNavIcons();
updateNavButtonIcons();
- WindowManagerWrapper.getInstance().setNavBarVirtualKeyHapticFeedbackEnabled(
- !mShowSwipeUpUi);
+ mBgExecutor.execute(() -> WindowManagerWrapper.getInstance()
+ .setNavBarVirtualKeyHapticFeedbackEnabled(!mShowSwipeUpUi));
getHomeButton().setAccessibilityDelegate(
mShowSwipeUpUi ? mQuickStepAccessibilityDelegate : null);
}
@@ -812,6 +819,7 @@ public class NavigationBarView extends FrameLayout {
mImeDrawsImeNavBar = imeDrawsImeNavBar;
mBarTransitions.onNavigationModeChanged(mNavBarMode);
mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode);
+ mRotationButtonController.onNavigationModeChanged(mNavBarMode);
updateRotationButton();
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 8ee16a92a9bd..9e0c49641e72 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -24,6 +24,7 @@ import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static com.android.systemui.navigationbar.NavBarHelper.transitionMode;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
@@ -35,6 +36,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_I
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import android.app.StatusBarManager;
import android.app.StatusBarManager.WindowVisibleState;
@@ -50,10 +52,12 @@ import android.util.Log;
import android.view.Display;
import android.view.InsetsVisibilities;
import android.view.View;
+import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
import androidx.annotation.NonNull;
+import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
@@ -114,6 +118,9 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
};
private int mDisabledFlags;
private @WindowVisibleState int mTaskBarWindowState = WINDOW_STATE_SHOWING;
+
+ private @TransitionMode int mTransitionMode;
+ private @Appearance int mAppearance;
private @Behavior int mBehavior;
private final Context mContext;
private final DisplayManager mDisplayManager;
@@ -146,19 +153,12 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
}
};
- private final Consumer<Boolean> mNavbarOverlayVisibilityChangeCallback = (visible) -> {
- if (visible) {
- mAutoHideController.touchAutoHide();
- }
- };
private BackAnimation mBackAnimation;
@Inject
- public TaskbarDelegate(
- Context context,
+ public TaskbarDelegate(Context context,
EdgeBackGestureHandler.Factory edgeBackGestureHandlerFactory,
- LightBarTransitionsController.Factory lightBarTransitionsControllerFactory
- ) {
+ LightBarTransitionsController.Factory lightBarTransitionsControllerFactory) {
mLightBarTransitionsControllerFactory = lightBarTransitionsControllerFactory;
mEdgeBackGestureHandler = edgeBackGestureHandlerFactory.create(context);
@@ -358,11 +358,18 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void onSystemBarAttributesChanged(int displayId, int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, int behavior,
- InsetsVisibilities requestedVisibilities, String packageName) {
+ InsetsVisibilities requestedVisibilities, String packageName,
+ LetterboxDetails[] letterboxDetails) {
mOverviewProxyService.onSystemBarAttributesChanged(displayId, behavior);
- if (mLightBarController != null && displayId == mDisplayId) {
- mLightBarController.onNavigationBarAppearanceChanged(appearance, false/*nbModeChanged*/,
- BarTransitions.MODE_TRANSPARENT /*navigationBarMode*/, navbarColorManagedByIme);
+ boolean nbModeChanged = false;
+ if (mAppearance != appearance) {
+ mAppearance = appearance;
+ nbModeChanged = updateTransitionMode(
+ transitionMode(mTaskbarTransientShowing, appearance));
+ }
+ if (displayId == mDisplayId) {
+ mLightBarController.onNavigationBarAppearanceChanged(appearance, nbModeChanged,
+ BarTransitions.MODE_TRANSPARENT, navbarColorManagedByIme);
}
if (mBehavior != behavior) {
mBehavior = behavior;
@@ -413,6 +420,22 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private void onTransientStateChanged() {
mEdgeBackGestureHandler.onNavBarTransientStateChanged(mTaskbarTransientShowing);
+
+ final int transitionMode = transitionMode(mTaskbarTransientShowing, mAppearance);
+ if (updateTransitionMode(transitionMode)) {
+ mLightBarController.onNavigationBarModeChanged(transitionMode);
+ }
+ }
+
+ private boolean updateTransitionMode(int barMode) {
+ if (mTransitionMode != barMode) {
+ mTransitionMode = barMode;
+ if (mAutoHideController != null) {
+ mAutoHideController.touchAutoHide();
+ }
+ return true;
+ }
+ return false;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
new file mode 100644
index 000000000000..282243563952
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
@@ -0,0 +1,397 @@
+package com.android.systemui.navigationbar.gestural
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.RectF
+import android.view.View
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.dynamicanimation.animation.SpringAnimation
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.internal.util.LatencyTracker
+import com.android.settingslib.Utils
+import com.android.systemui.navigationbar.gestural.BackPanelController.DelayedOnAnimationEndListener
+
+private const val TAG = "BackPanel"
+private const val DEBUG = false
+
+class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : View(context) {
+
+ var arrowsPointLeft = false
+ set(value) {
+ if (field != value) {
+ invalidate()
+ field = value
+ }
+ }
+
+ // Arrow color and shape
+ private val arrowPath = Path()
+ private val arrowPaint = Paint()
+
+ // Arrow background color and shape
+ private var arrowBackgroundRect = RectF()
+ private var arrowBackgroundPaint = Paint()
+
+ // True if the panel is currently on the left of the screen
+ var isLeftPanel = false
+
+ /**
+ * Used to track back arrow latency from [android.view.MotionEvent.ACTION_DOWN] to [onDraw]
+ */
+ private var trackingBackArrowLatency = false
+
+ /**
+ * The length of the arrow measured horizontally. Used for animating [arrowPath]
+ */
+ private var arrowLength = AnimatedFloat("arrowLength", SpringForce())
+
+ /**
+ * The height of the arrow measured vertically from its center to its top (i.e. half the total
+ * height). Used for animating [arrowPath]
+ */
+ private var arrowHeight = AnimatedFloat("arrowHeight", SpringForce())
+
+ private val backgroundWidth = AnimatedFloat(
+ name = "backgroundWidth",
+ SpringForce().apply {
+ stiffness = 600f
+ dampingRatio = 0.65f
+ }
+ )
+
+ private val backgroundHeight = AnimatedFloat(
+ name = "backgroundHeight",
+ SpringForce().apply {
+ stiffness = 600f
+ dampingRatio = 0.65f
+ }
+ )
+
+ /**
+ * Corners of the background closer to the edge of the screen (where the arrow appeared from).
+ * Used for animating [arrowBackgroundRect]
+ */
+ private val backgroundEdgeCornerRadius = AnimatedFloat(
+ name = "backgroundEdgeCornerRadius",
+ SpringForce().apply {
+ stiffness = 400f
+ dampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY
+ }
+ )
+
+ /**
+ * Corners of the background further from the edge of the screens (toward the direction the
+ * arrow is being dragged). Used for animating [arrowBackgroundRect]
+ */
+ private val backgroundFarCornerRadius = AnimatedFloat(
+ name = "backgroundDragCornerRadius",
+ SpringForce().apply {
+ stiffness = 2200f
+ dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
+ }
+ )
+
+ /**
+ * Left/right position of the background relative to the canvas. Also corresponds with the
+ * background's margin relative to the screen edge. The arrow will be centered within the
+ * background.
+ */
+ private var horizontalTranslation = AnimatedFloat("horizontalTranslation", SpringForce())
+
+ private val currentAlpha: FloatPropertyCompat<BackPanel> =
+ object : FloatPropertyCompat<BackPanel>("currentAlpha") {
+ override fun setValue(panel: BackPanel, value: Float) {
+ panel.alpha = value
+ }
+
+ override fun getValue(panel: BackPanel): Float = panel.alpha
+ }
+
+ private val alphaAnimation = SpringAnimation(this, currentAlpha)
+ .setSpring(
+ SpringForce()
+ .setStiffness(60f)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
+ )
+
+ /**
+ * Canvas vertical translation. How far up/down the arrow and background appear relative to the
+ * canvas.
+ */
+ private var verticalTranslation: AnimatedFloat = AnimatedFloat(
+ name = "verticalTranslation",
+ SpringForce().apply {
+ stiffness = SpringForce.STIFFNESS_MEDIUM
+ }
+ )
+
+ /**
+ * Use for drawing debug info. Can only be set if [DEBUG]=true
+ */
+ var drawDebugInfo: ((canvas: Canvas) -> Unit)? = null
+ set(value) {
+ if (DEBUG) field = value
+ }
+
+ internal fun updateArrowPaint(arrowThickness: Float) {
+ // Arrow constants
+ arrowPaint.strokeWidth = arrowThickness
+
+ arrowPaint.color =
+ Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
+ arrowBackgroundPaint.color = Utils.getColorAccentDefaultColor(context)
+ }
+
+ private inner class AnimatedFloat(name: String, springForce: SpringForce) {
+ // The resting position when not stretched by a touch drag
+ private var restingPosition = 0f
+
+ // The current position as updated by the SpringAnimation
+ var pos = 0f
+ set(v) {
+ if (field != v) {
+ field = v
+ invalidate()
+ }
+ }
+
+ val animation: SpringAnimation
+
+ init {
+ val floatProp = object : FloatPropertyCompat<AnimatedFloat>(name) {
+ override fun setValue(animatedFloat: AnimatedFloat, value: Float) {
+ animatedFloat.pos = value
+ }
+
+ override fun getValue(animatedFloat: AnimatedFloat): Float = animatedFloat.pos
+ }
+ animation = SpringAnimation(this, floatProp)
+ animation.spring = springForce
+ }
+
+ fun snapTo(newPosition: Float) {
+ animation.cancel()
+ restingPosition = newPosition
+ animation.spring.finalPosition = newPosition
+ pos = newPosition
+ }
+
+ fun stretchTo(stretchAmount: Float) {
+ animation.animateToFinalPosition(restingPosition + stretchAmount)
+ }
+
+ /**
+ * Animates to a new position ([finalPosition]) that is the given fraction ([amount])
+ * between the existing [restingPosition] and the new [finalPosition].
+ *
+ * The [restingPosition] will remain unchanged. Only the animation is updated.
+ */
+ fun stretchBy(finalPosition: Float, amount: Float) {
+ val stretchedAmount = amount * (finalPosition - restingPosition)
+ animation.animateToFinalPosition(restingPosition + stretchedAmount)
+ }
+
+ fun updateRestingPosition(pos: Float, animated: Boolean) {
+ restingPosition = pos
+ if (animated)
+ animation.animateToFinalPosition(restingPosition)
+ else
+ snapTo(restingPosition)
+ }
+ }
+
+ init {
+ visibility = GONE
+ arrowPaint.apply {
+ style = Paint.Style.STROKE
+ strokeCap = Paint.Cap.SQUARE
+ }
+ arrowBackgroundPaint.apply {
+ style = Paint.Style.FILL
+ strokeJoin = Paint.Join.ROUND
+ strokeCap = Paint.Cap.ROUND
+ }
+ }
+
+ private fun calculateArrowPath(dx: Float, dy: Float): Path {
+ arrowPath.reset()
+ arrowPath.moveTo(dx, -dy)
+ arrowPath.lineTo(0f, 0f)
+ arrowPath.lineTo(dx, dy)
+ arrowPath.moveTo(dx, -dy)
+ return arrowPath
+ }
+
+ fun addEndListener(endListener: DelayedOnAnimationEndListener): Boolean {
+ return if (alphaAnimation.isRunning) {
+ alphaAnimation.addEndListener(endListener)
+ true
+ } else if (horizontalTranslation.animation.isRunning) {
+ horizontalTranslation.animation.addEndListener(endListener)
+ true
+ } else {
+ endListener.runNow()
+ false
+ }
+ }
+
+ fun setStretch(
+ horizontalTranslationStretchAmount: Float,
+ arrowStretchAmount: Float,
+ backgroundWidthStretchAmount: Float,
+ fullyStretchedDimens: EdgePanelParams.BackIndicatorDimens
+ ) {
+ horizontalTranslation.stretchBy(
+ finalPosition = fullyStretchedDimens.horizontalTranslation,
+ amount = horizontalTranslationStretchAmount
+ )
+ arrowLength.stretchBy(
+ finalPosition = fullyStretchedDimens.arrowDimens.length,
+ amount = arrowStretchAmount
+ )
+ arrowHeight.stretchBy(
+ finalPosition = fullyStretchedDimens.arrowDimens.height,
+ amount = arrowStretchAmount
+ )
+ backgroundWidth.stretchBy(
+ finalPosition = fullyStretchedDimens.backgroundDimens.width,
+ amount = backgroundWidthStretchAmount
+ )
+ }
+
+ fun resetStretch() {
+ horizontalTranslation.stretchTo(0f)
+ arrowLength.stretchTo(0f)
+ arrowHeight.stretchTo(0f)
+ backgroundWidth.stretchTo(0f)
+ backgroundHeight.stretchTo(0f)
+ backgroundEdgeCornerRadius.stretchTo(0f)
+ backgroundFarCornerRadius.stretchTo(0f)
+ }
+
+ /**
+ * Updates resting arrow and background size not accounting for stretch
+ */
+ internal fun setRestingDimens(
+ restingParams: EdgePanelParams.BackIndicatorDimens,
+ animate: Boolean
+ ) {
+ horizontalTranslation.updateRestingPosition(restingParams.horizontalTranslation, animate)
+ arrowLength.updateRestingPosition(restingParams.arrowDimens.length, animate)
+ arrowHeight.updateRestingPosition(restingParams.arrowDimens.height, animate)
+ backgroundWidth.updateRestingPosition(restingParams.backgroundDimens.width, animate)
+ backgroundHeight.updateRestingPosition(restingParams.backgroundDimens.height, animate)
+ backgroundEdgeCornerRadius.updateRestingPosition(
+ restingParams.backgroundDimens.edgeCornerRadius,
+ animate
+ )
+ backgroundFarCornerRadius.updateRestingPosition(
+ restingParams.backgroundDimens.farCornerRadius,
+ animate
+ )
+ }
+
+ fun animateVertically(yPos: Float) = verticalTranslation.stretchTo(yPos)
+
+ fun setArrowStiffness(arrowStiffness: Float, arrowDampingRatio: Float) {
+ arrowLength.animation.spring.apply {
+ stiffness = arrowStiffness
+ dampingRatio = arrowDampingRatio
+ }
+ arrowHeight.animation.spring.apply {
+ stiffness = arrowStiffness
+ dampingRatio = arrowDampingRatio
+ }
+ }
+
+ override fun hasOverlappingRendering() = false
+
+ override fun onDraw(canvas: Canvas) {
+ var edgeCorner = backgroundEdgeCornerRadius.pos
+ val farCorner = backgroundFarCornerRadius.pos
+ val halfHeight = backgroundHeight.pos / 2
+
+ canvas.save()
+
+ if (!isLeftPanel) canvas.scale(-1f, 1f, width / 2.0f, 0f)
+
+ canvas.translate(
+ horizontalTranslation.pos,
+ height * 0.5f + verticalTranslation.pos
+ )
+
+ val arrowBackground = arrowBackgroundRect.apply {
+ left = 0f
+ top = -halfHeight
+ right = backgroundWidth.pos
+ bottom = halfHeight
+ }.toPathWithRoundCorners(
+ topLeft = edgeCorner,
+ bottomLeft = edgeCorner,
+ topRight = farCorner,
+ bottomRight = farCorner
+ )
+ canvas.drawPath(arrowBackground, arrowBackgroundPaint)
+
+ val dx = arrowLength.pos
+ val dy = arrowHeight.pos
+
+ // How far the arrow bounding box should be from the edge of the screen. Measured from
+ // either the tip or the back of the arrow, whichever is closer
+ var arrowOffset = (backgroundWidth.pos - dx) / 2
+ canvas.translate(
+ /* dx= */ arrowOffset,
+ /* dy= */ 0f /* pass 0 for the y position since the canvas was already translated */
+ )
+
+ val arrowPointsAwayFromEdge = !arrowsPointLeft.xor(isLeftPanel)
+ if (arrowPointsAwayFromEdge) {
+ canvas.apply {
+ scale(-1f, 1f, 0f, 0f)
+ translate(-dx, 0f)
+ }
+ }
+
+ val arrowPath = calculateArrowPath(dx = dx, dy = dy)
+ canvas.drawPath(arrowPath, arrowPaint)
+ canvas.restore()
+
+ if (trackingBackArrowLatency) {
+ latencyTracker.onActionEnd(LatencyTracker.ACTION_SHOW_BACK_ARROW)
+ trackingBackArrowLatency = false
+ }
+
+ if (DEBUG) drawDebugInfo?.invoke(canvas)
+ }
+
+ fun startTrackingShowBackArrowLatency() {
+ latencyTracker.onActionStart(LatencyTracker.ACTION_SHOW_BACK_ARROW)
+ trackingBackArrowLatency = true
+ }
+
+ private fun RectF.toPathWithRoundCorners(
+ topLeft: Float = 0f,
+ topRight: Float = 0f,
+ bottomRight: Float = 0f,
+ bottomLeft: Float = 0f
+ ): Path = Path().apply {
+ val corners = floatArrayOf(
+ topLeft, topLeft,
+ topRight, topRight,
+ bottomRight, bottomRight,
+ bottomLeft, bottomLeft
+ )
+ addRoundRect(this@toPathWithRoundCorners, corners, Path.Direction.CW)
+ }
+
+ fun cancelAlphaAnimations() {
+ alphaAnimation.cancel()
+ alpha = 1f
+ }
+
+ fun fadeOut() {
+ alphaAnimation.animateToFinalPosition(0f)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
new file mode 100644
index 000000000000..28ab83c83a42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -0,0 +1,760 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.navigationbar.gestural
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Point
+import android.os.Handler
+import android.os.SystemClock
+import android.os.VibrationEffect
+import android.util.Log
+import android.util.MathUtils.constrain
+import android.util.MathUtils.saturate
+import android.view.Gravity
+import android.view.MotionEvent
+import android.view.VelocityTracker
+import android.view.View
+import android.view.ViewConfiguration
+import android.view.WindowManager
+import android.view.animation.DecelerateInterpolator
+import android.view.animation.PathInterpolator
+import android.window.BackEvent
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.internal.util.LatencyTracker
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.NavigationEdgeBackPlugin
+import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.ViewController
+import com.android.wm.shell.back.BackAnimation
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlin.math.abs
+import kotlin.math.max
+import kotlin.math.min
+import kotlin.math.sign
+
+private const val TAG = "BackPanelController"
+private const val DEBUG = false
+
+private const val ENABLE_FAILSAFE = true
+
+private const val FAILSAFE_DELAY_MS: Long = 350
+
+/**
+ * The time required between the arrow-appears vibration effect and the back-committed vibration
+ * effect. If the arrow is flung quickly, the phone only vibrates once. However, if the arrow is
+ * held on the screen for a long time, it will vibrate a second time when the back gesture is
+ * committed.
+ */
+private const val GESTURE_DURATION_FOR_CLICK_MS = 400
+
+/**
+ * The min duration arrow remains on screen during a fling event.
+ */
+private const val FLING_MIN_APPEARANCE_DURATION = 235L
+
+/**
+ * The min duration arrow remains on screen during a fling event.
+ */
+private const val MIN_FLING_VELOCITY = 3000
+
+/**
+ * The amount of rubber banding we do for the vertical translation
+ */
+private const val RUBBER_BAND_AMOUNT = 15
+
+private const val ARROW_APPEAR_STIFFNESS = 600f
+private const val ARROW_APPEAR_DAMPING_RATIO = 0.4f
+private const val ARROW_DISAPPEAR_STIFFNESS = 1200f
+private const val ARROW_DISAPPEAR_DAMPING_RATIO = SpringForce.DAMPING_RATIO_NO_BOUNCY
+
+/**
+ * The interpolator used to rubber band
+ */
+private val RUBBER_BAND_INTERPOLATOR = PathInterpolator(1.0f / 5.0f, 1.0f, 1.0f, 1.0f)
+
+private val DECELERATE_INTERPOLATOR = DecelerateInterpolator()
+
+private val DECELERATE_INTERPOLATOR_SLOW = DecelerateInterpolator(0.7f)
+
+class BackPanelController private constructor(
+ context: Context,
+ private var backAnimation: BackAnimation?,
+ private val windowManager: WindowManager,
+ private val viewConfiguration: ViewConfiguration,
+ @Main private val mainHandler: Handler,
+ private val vibratorHelper: VibratorHelper,
+ private val configurationController: ConfigurationController,
+ latencyTracker: LatencyTracker
+) : ViewController<BackPanel>(BackPanel(context, latencyTracker)), NavigationEdgeBackPlugin {
+
+ /**
+ * Injectable instance to create a new BackPanelController.
+ *
+ * Necessary because EdgeBackGestureHandler sometimes needs to create new instances of
+ * BackPanelController, and we need to match EdgeBackGestureHandler's context.
+ */
+ class Factory @Inject constructor(
+ private val windowManager: WindowManager,
+ private val viewConfiguration: ViewConfiguration,
+ @Main private val mainHandler: Handler,
+ private val vibratorHelper: VibratorHelper,
+ private val configurationController: ConfigurationController,
+ private val latencyTracker: LatencyTracker
+ ) {
+ /** Construct a [BackPanelController]. */
+ fun create(context: Context, backAnimation: BackAnimation?): BackPanelController {
+ val backPanelController = BackPanelController(
+ context,
+ backAnimation,
+ windowManager,
+ viewConfiguration,
+ mainHandler,
+ vibratorHelper,
+ configurationController,
+ latencyTracker
+ )
+ backPanelController.init()
+ return backPanelController
+ }
+ }
+
+ private var params: EdgePanelParams = EdgePanelParams(resources)
+ private var currentState: GestureState = GestureState.GONE
+ private var previousState: GestureState = GestureState.GONE
+
+ // Phone should only vibrate the first time the arrow is activated
+ private var hasHapticPlayed = false
+
+ // Screen attributes
+ private lateinit var layoutParams: WindowManager.LayoutParams
+ private val displaySize = Point()
+
+ private lateinit var backCallback: NavigationEdgeBackPlugin.BackCallback
+
+ private var previousXTranslation = 0f
+ private var totalTouchDelta = 0f
+ private var velocityTracker: VelocityTracker? = null
+ set(value) {
+ if (field != value) field?.recycle()
+ field = value
+ }
+ get() {
+ if (field == null) field = VelocityTracker.obtain()
+ return field
+ }
+
+ // The x,y position of the first touch event
+ private var startX = 0f
+ private var startY = 0f
+
+ private var gestureStartTime = 0L
+
+ // Whether the current gesture has moved a sufficiently large amount,
+ // so that we can unambiguously start showing the ENTRY animation
+ private var hasPassedDragSlop = false
+
+ private val failsafeRunnable = Runnable { onFailsafe() }
+
+ private enum class GestureState {
+ /* Arrow is off the screen and invisible */
+ GONE,
+
+ /* Arrow is animating in */
+ ENTRY,
+
+ /* could be entry, neutral, or stretched, releasing will commit back */
+ ACTIVE,
+
+ /* releasing will cancel back */
+ INACTIVE,
+
+ /* like committed, but animation takes longer */
+ FLUNG,
+
+ /* back action currently occurring, arrow soon to be GONE */
+ COMMITTED,
+
+ /* back action currently cancelling, arrow soon to be GONE */
+ CANCELLED;
+
+ /**
+ * @return true if the current state responds to touch move events in some way (e.g. by
+ * stretching the back indicator)
+ */
+ fun isInteractive(): Boolean {
+ return when (this) {
+ ENTRY, ACTIVE, INACTIVE -> true
+ GONE, FLUNG, COMMITTED, CANCELLED -> false
+ }
+ }
+ }
+
+ /**
+ * Wrapper around OnAnimationEndListener which runs the given runnable after a delay. The
+ * runnable is not called if the animation is cancelled
+ */
+ inner class DelayedOnAnimationEndListener internal constructor(
+ private val handler: Handler,
+ private val runnable: Runnable,
+ private val minDuration: Long
+ ) : DynamicAnimation.OnAnimationEndListener {
+ override fun onAnimationEnd(
+ animation: DynamicAnimation<*>,
+ canceled: Boolean,
+ value: Float,
+ velocity: Float
+ ) {
+ animation.removeEndListener(this)
+ if (!canceled) {
+ // Total elapsed time of the gesture and the animation
+ val totalElapsedTime = SystemClock.uptimeMillis() - gestureStartTime
+ // The delay between finishing this animation and starting the runnable
+ val delay = max(0, minDuration - totalElapsedTime)
+ handler.postDelayed(runnable, delay)
+ }
+ }
+
+ internal fun runNow() {
+ runnable.run()
+ }
+ }
+
+ private val setCommittedEndListener =
+ DelayedOnAnimationEndListener(
+ mainHandler,
+ { updateArrowState(GestureState.COMMITTED) },
+ minDuration = FLING_MIN_APPEARANCE_DURATION
+ )
+
+ private val setGoneEndListener =
+ DelayedOnAnimationEndListener(
+ mainHandler,
+ {
+ cancelFailsafe()
+ updateArrowState(GestureState.GONE)
+ },
+ minDuration = 0
+ )
+
+ // Vibration
+ private var vibrationTime: Long = 0
+
+ // Minimum of the screen's width or the predefined threshold
+ private var fullyStretchedThreshold = 0f
+
+ /**
+ * Used for initialization and configuration changes
+ */
+ private fun updateConfiguration() {
+ params.update(resources)
+ updateBackAnimationSwipeThresholds()
+ mView.updateArrowPaint(params.arrowThickness)
+ }
+
+ private val configurationListener = object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ updateConfiguration()
+ }
+
+ override fun onLayoutDirectionChanged(isLayoutRtl: Boolean) {
+ updateArrowDirection(isLayoutRtl)
+ }
+ }
+
+ override fun onViewAttached() {
+ updateConfiguration()
+ updateArrowDirection(configurationController.isLayoutRtl)
+ updateArrowState(GestureState.GONE, force = true)
+ updateRestingArrowDimens(animated = false, currentState)
+ configurationController.addCallback(configurationListener)
+ }
+
+ /** Update the arrow direction. The arrow should point the same way for both panels. */
+ private fun updateArrowDirection(isLayoutRtl: Boolean) {
+ mView.arrowsPointLeft = isLayoutRtl
+ }
+
+ override fun onViewDetached() {
+ configurationController.removeCallback(configurationListener)
+ }
+
+ override fun onMotionEvent(event: MotionEvent) {
+ backAnimation?.onBackMotion(
+ event.x,
+ event.y,
+ event.actionMasked,
+ if (mView.isLeftPanel) BackEvent.EDGE_LEFT else BackEvent.EDGE_RIGHT
+ )
+
+ velocityTracker!!.addMovement(event)
+ when (event.actionMasked) {
+ MotionEvent.ACTION_DOWN -> {
+ resetOnDown()
+ startX = event.x
+ startY = event.y
+ gestureStartTime = SystemClock.uptimeMillis()
+ }
+ MotionEvent.ACTION_MOVE -> {
+ // only go to the ENTRY state after some minimum motion has occurred
+ if (dragSlopExceeded(event.x, startX)) {
+ handleMoveEvent(event)
+ }
+ }
+ MotionEvent.ACTION_UP -> {
+ if (currentState == GestureState.ACTIVE) {
+ updateArrowState(if (isFlung()) GestureState.FLUNG else GestureState.COMMITTED)
+ } else if (currentState != GestureState.GONE) { // if invisible, skip animation
+ updateArrowState(GestureState.CANCELLED)
+ }
+ velocityTracker = null
+ }
+ MotionEvent.ACTION_CANCEL -> {
+ // Receiving a CANCEL implies that something else intercepted
+ // the gesture, i.e., the user did not cancel their gesture.
+ // Therefore, disappear immediately, with minimum fanfare.
+ updateArrowState(GestureState.GONE)
+ velocityTracker = null
+ }
+ }
+ }
+
+ /**
+ * Returns false until the current gesture exceeds the touch slop threshold,
+ * and returns true thereafter (we reset on the subsequent back gesture).
+ * The moment it switches from false -> true is important,
+ * because that's when we switch state, from GONE -> ENTRY.
+ * @return whether the current gesture has moved past a minimum threshold.
+ */
+ private fun dragSlopExceeded(curX: Float, startX: Float): Boolean {
+ if (hasPassedDragSlop) return true
+
+ if (abs(curX - startX) > viewConfiguration.scaledTouchSlop) {
+ // Reset the arrow to the side
+ updateArrowState(GestureState.ENTRY)
+
+ windowManager.updateViewLayout(mView, layoutParams)
+ mView.startTrackingShowBackArrowLatency()
+
+ hasPassedDragSlop = true
+ }
+ return hasPassedDragSlop
+ }
+
+ private fun updateArrowStateOnMove(yTranslation: Float, xTranslation: Float) {
+ if (!currentState.isInteractive())
+ return
+
+ when (currentState) {
+ // Check if we should transition from ENTRY to ACTIVE
+ GestureState.ENTRY ->
+ if (xTranslation > params.swipeTriggerThreshold) {
+ updateArrowState(GestureState.ACTIVE)
+ }
+
+ // Abort if we had continuous motion toward the edge for a while, OR the direction
+ // in Y is bigger than X * 2
+ GestureState.ACTIVE ->
+ if ((totalTouchDelta < 0 && -totalTouchDelta > params.minDeltaForSwitch) ||
+ (yTranslation > xTranslation * 2)
+ ) {
+ updateArrowState(GestureState.INACTIVE)
+ }
+
+ // Re-activate if we had continuous motion away from the edge for a while
+ GestureState.INACTIVE ->
+ if (totalTouchDelta > 0 && totalTouchDelta > params.minDeltaForSwitch) {
+ updateArrowState(GestureState.ACTIVE)
+ }
+
+ // By default assume the current direction is kept
+ else -> {}
+ }
+ }
+
+ private fun handleMoveEvent(event: MotionEvent) {
+ if (!currentState.isInteractive())
+ return
+
+ val x = event.x
+ val y = event.y
+
+ val yOffset = y - startY
+
+ // How far in the y direction we are from the original touch
+ val yTranslation = abs(yOffset)
+
+ // How far in the x direction we are from the original touch ignoring motion that
+ // occurs between the screen edge and the touch start.
+ val xTranslation = max(0f, if (mView.isLeftPanel) x - startX else startX - x)
+
+ // Compared to last time, how far we moved in the x direction. If <0, we are moving closer
+ // to the edge. If >0, we are moving further from the edge
+ val xDelta = xTranslation - previousXTranslation
+ previousXTranslation = xTranslation
+
+ if (abs(xDelta) > 0) {
+ if (sign(xDelta) == sign(totalTouchDelta)) {
+ // Direction has NOT changed, so keep counting the delta
+ totalTouchDelta += xDelta
+ } else {
+ // Direction has changed, so reset the delta
+ totalTouchDelta = xDelta
+ }
+ }
+
+ updateArrowStateOnMove(yTranslation, xTranslation)
+ when (currentState) {
+ GestureState.ACTIVE ->
+ stretchActiveBackIndicator(fullScreenStretchProgress(xTranslation))
+ GestureState.ENTRY ->
+ stretchEntryBackIndicator(preThresholdStretchProgress(xTranslation))
+ GestureState.INACTIVE ->
+ mView.resetStretch()
+ }
+
+ // set y translation
+ setVerticalTranslation(yOffset)
+ }
+
+ private fun setVerticalTranslation(yOffset: Float) {
+ val yTranslation = abs(yOffset)
+ val maxYOffset = (mView.height - params.entryIndicator.backgroundDimens.height) / 2f
+ val yProgress = saturate(yTranslation / (maxYOffset * RUBBER_BAND_AMOUNT))
+ mView.animateVertically(
+ RUBBER_BAND_INTERPOLATOR.getInterpolation(yProgress) * maxYOffset *
+ sign(yOffset)
+ )
+ }
+
+ /**
+ * @return the relative position of the drag from the time after the arrow is activated until
+ * the arrow is fully stretched (between 0.0 - 1.0f)
+ */
+ private fun fullScreenStretchProgress(xTranslation: Float): Float {
+ return saturate(
+ (xTranslation - params.swipeTriggerThreshold) /
+ (fullyStretchedThreshold - params.swipeTriggerThreshold)
+ )
+ }
+
+ /**
+ * Tracks the relative position of the drag from the entry until the threshold where the arrow
+ * activates (between 0.0 - 1.0f)
+ */
+ private fun preThresholdStretchProgress(xTranslation: Float): Float {
+ return saturate(xTranslation / params.swipeTriggerThreshold)
+ }
+
+ private fun stretchActiveBackIndicator(progress: Float) {
+ val rubberBandIterpolation = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
+ mView.setStretch(
+ horizontalTranslationStretchAmount = rubberBandIterpolation,
+ arrowStretchAmount = rubberBandIterpolation,
+ backgroundWidthStretchAmount = DECELERATE_INTERPOLATOR_SLOW.getInterpolation(progress),
+ params.fullyStretchedIndicator
+ )
+ }
+
+ private fun stretchEntryBackIndicator(progress: Float) {
+ mView.setStretch(
+ horizontalTranslationStretchAmount = 0f,
+ arrowStretchAmount = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress),
+ backgroundWidthStretchAmount = DECELERATE_INTERPOLATOR.getInterpolation(progress),
+ params.preThresholdIndicator
+ )
+ }
+
+ fun setBackAnimation(backAnimation: BackAnimation?) {
+ this.backAnimation = backAnimation
+ updateBackAnimationSwipeThresholds()
+ }
+
+ private fun updateBackAnimationSwipeThresholds() {
+ backAnimation?.setSwipeThresholds(
+ params.swipeTriggerThreshold,
+ fullyStretchedThreshold
+ )
+ }
+
+ override fun onDestroy() {
+ cancelFailsafe()
+ windowManager.removeView(mView)
+ }
+
+ override fun setIsLeftPanel(isLeftPanel: Boolean) {
+ mView.isLeftPanel = isLeftPanel
+ layoutParams.gravity = if (isLeftPanel) {
+ Gravity.LEFT or Gravity.TOP
+ } else {
+ Gravity.RIGHT or Gravity.TOP
+ }
+ }
+
+ override fun setInsets(insetLeft: Int, insetRight: Int) {
+ }
+
+ override fun setBackCallback(callback: NavigationEdgeBackPlugin.BackCallback) {
+ backCallback = callback
+ }
+
+ override fun setLayoutParams(layoutParams: WindowManager.LayoutParams) {
+ this.layoutParams = layoutParams
+ windowManager.addView(mView, layoutParams)
+ }
+
+ private fun isFlung() = velocityTracker!!.run {
+ computeCurrentVelocity(1000)
+ abs(xVelocity) > MIN_FLING_VELOCITY
+ }
+
+ private fun playFlingBackAnimation() {
+ playAnimation(setCommittedEndListener)
+ }
+
+ private fun playCommitBackAnimation() {
+ // Check if we should vibrate again
+ if (previousState != GestureState.FLUNG) {
+ backCallback.triggerBack()
+ velocityTracker!!.computeCurrentVelocity(1000)
+ val isSlow = abs(velocityTracker!!.xVelocity) < 500
+ val hasNotVibratedRecently =
+ SystemClock.uptimeMillis() - vibrationTime >= GESTURE_DURATION_FOR_CLICK_MS
+ if (isSlow || hasNotVibratedRecently) {
+ vibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK)
+ }
+ }
+ playAnimation(setGoneEndListener)
+ }
+
+ private fun playCancelBackAnimation() {
+ backCallback.cancelBack()
+ playAnimation(setGoneEndListener)
+ }
+
+ /**
+ * @return true if the animation is running, false otherwise. Some transitions don't animate
+ */
+ private fun playAnimation(endListener: DelayedOnAnimationEndListener) {
+ updateRestingArrowDimens(animated = true, currentState)
+
+ if (!mView.addEndListener(endListener)) {
+ scheduleFailsafe()
+ }
+ }
+
+ private fun resetOnDown() {
+ hasPassedDragSlop = false
+ hasHapticPlayed = false
+ totalTouchDelta = 0f
+ vibrationTime = 0
+ cancelFailsafe()
+ backAnimation?.setTriggerBack(false)
+ }
+
+ private fun updateYPosition(touchY: Float) {
+ var yPosition = touchY - params.fingerOffset
+ yPosition = max(yPosition, params.minArrowYPosition.toFloat())
+ yPosition -= layoutParams.height / 2.0f
+ layoutParams.y = constrain(yPosition.toInt(), 0, displaySize.y)
+ }
+
+ override fun setDisplaySize(displaySize: Point) {
+ this.displaySize.set(displaySize.x, displaySize.y)
+ fullyStretchedThreshold = min(displaySize.x.toFloat(), params.swipeProgressThreshold)
+ updateBackAnimationSwipeThresholds()
+ }
+
+ /**
+ * Updates resting arrow and background size not accounting for stretch
+ */
+ private fun updateRestingArrowDimens(animated: Boolean, currentState: GestureState) {
+ if (animated) {
+ when (currentState) {
+ GestureState.ENTRY, GestureState.ACTIVE, GestureState.FLUNG ->
+ mView.setArrowStiffness(ARROW_APPEAR_STIFFNESS, ARROW_APPEAR_DAMPING_RATIO)
+ GestureState.CANCELLED -> mView.fadeOut()
+ else ->
+ mView.setArrowStiffness(
+ ARROW_DISAPPEAR_STIFFNESS,
+ ARROW_DISAPPEAR_DAMPING_RATIO
+ )
+ }
+ }
+ mView.setRestingDimens(
+ restingParams = EdgePanelParams.BackIndicatorDimens(
+ horizontalTranslation = when (currentState) {
+ GestureState.GONE -> -params.activeIndicator.backgroundDimens.width
+ // Position the committed arrow slightly further off the screen so we do not
+ // see part of it bouncing
+ GestureState.COMMITTED ->
+ -params.activeIndicator.backgroundDimens.width * 1.5f
+ GestureState.FLUNG -> params.fullyStretchedIndicator.horizontalTranslation
+ GestureState.ACTIVE -> params.activeIndicator.horizontalTranslation
+ GestureState.ENTRY, GestureState.INACTIVE, GestureState.CANCELLED ->
+ params.entryIndicator.horizontalTranslation
+ },
+ arrowDimens = when (currentState) {
+ GestureState.ACTIVE, GestureState.INACTIVE,
+ GestureState.COMMITTED, GestureState.FLUNG -> params.activeIndicator.arrowDimens
+ GestureState.CANCELLED -> params.cancelledArrowDimens
+ GestureState.GONE, GestureState.ENTRY -> params.entryIndicator.arrowDimens
+ },
+ backgroundDimens = when (currentState) {
+ GestureState.GONE, GestureState.ENTRY -> params.entryIndicator.backgroundDimens
+ else ->
+ params.activeIndicator.backgroundDimens.copy(
+ edgeCornerRadius =
+ if (currentState == GestureState.INACTIVE ||
+ currentState == GestureState.CANCELLED
+ )
+ params.entryIndicator.backgroundDimens.edgeCornerRadius
+ else
+ params.activeIndicator.backgroundDimens.edgeCornerRadius
+ )
+ }
+ ),
+ animate = animated
+ )
+ }
+
+ /**
+ * Update arrow state. If state has not changed, this is a no-op.
+ *
+ * Transitioning to active/inactive will indicate whether or not releasing touch will trigger
+ * the back action.
+ */
+ private fun updateArrowState(newState: GestureState, force: Boolean = false) {
+ if (!force && currentState == newState) return
+
+ if (DEBUG) Log.d(TAG, "updateArrowState $currentState -> $newState")
+ previousState = currentState
+ currentState = newState
+ if (currentState == GestureState.GONE) {
+ mView.cancelAlphaAnimations()
+ mView.visibility = View.GONE
+ } else {
+ mView.visibility = View.VISIBLE
+ }
+
+ when (currentState) {
+ // Transitioning to GONE never animates since the arrow is (presumably) already off the
+ // screen
+ GestureState.GONE -> updateRestingArrowDimens(animated = false, currentState)
+ GestureState.ENTRY -> {
+ updateYPosition(startY)
+ updateRestingArrowDimens(animated = true, currentState)
+ }
+ GestureState.ACTIVE -> {
+ backAnimation?.setTriggerBack(true)
+ updateRestingArrowDimens(animated = true, currentState)
+ // Vibrate the first time we transition to ACTIVE
+ if (!hasHapticPlayed) {
+ hasHapticPlayed = true
+ vibrationTime = SystemClock.uptimeMillis()
+ vibratorHelper.vibrate(VibrationEffect.EFFECT_TICK)
+ }
+ }
+ GestureState.INACTIVE -> {
+ backAnimation?.setTriggerBack(false)
+ updateRestingArrowDimens(animated = true, currentState)
+ }
+ GestureState.FLUNG -> playFlingBackAnimation()
+ GestureState.COMMITTED -> playCommitBackAnimation()
+ GestureState.CANCELLED -> playCancelBackAnimation()
+ }
+ }
+
+ private fun scheduleFailsafe() {
+ if (!ENABLE_FAILSAFE) return
+ cancelFailsafe()
+ if (DEBUG) Log.d(TAG, "scheduleFailsafe")
+ mainHandler.postDelayed(failsafeRunnable, FAILSAFE_DELAY_MS)
+ }
+
+ private fun cancelFailsafe() {
+ if (DEBUG) Log.d(TAG, "cancelFailsafe")
+ mainHandler.removeCallbacks(failsafeRunnable)
+ }
+
+ private fun onFailsafe() {
+ if (DEBUG) Log.d(TAG, "onFailsafe")
+ updateArrowState(GestureState.GONE, force = true)
+ }
+
+ override fun dump(pw: PrintWriter) {
+ pw.println("$TAG:")
+ pw.println(" currentState=$currentState")
+ pw.println(" isLeftPanel=$mView.isLeftPanel")
+ }
+
+ init {
+ if (DEBUG) mView.drawDebugInfo = { canvas ->
+ val debugStrings = listOf(
+ "$currentState",
+ "startX=$startX",
+ "startY=$startY",
+ "xDelta=${"%.1f".format(totalTouchDelta)}",
+ "xTranslation=${"%.1f".format(previousXTranslation)}",
+ "pre=${"%.0f".format(preThresholdStretchProgress(previousXTranslation) * 100)}%",
+ "post=${"%.0f".format(fullScreenStretchProgress(previousXTranslation) * 100)}%"
+ )
+ val debugPaint = Paint().apply {
+ color = Color.WHITE
+ }
+ val debugInfoBottom = debugStrings.size * 32f + 4f
+ canvas.drawRect(
+ 4f,
+ 4f,
+ canvas.width.toFloat(),
+ debugStrings.size * 32f + 4f,
+ debugPaint
+ )
+ debugPaint.apply {
+ color = Color.BLACK
+ textSize = 32f
+ }
+ var offset = 32f
+ for (debugText in debugStrings) {
+ canvas.drawText(debugText, 10f, offset, debugPaint)
+ offset += 32f
+ }
+ debugPaint.apply {
+ color = Color.RED
+ style = Paint.Style.STROKE
+ strokeWidth = 4f
+ }
+ val canvasWidth = canvas.width.toFloat()
+ val canvasHeight = canvas.height.toFloat()
+ canvas.drawRect(0f, 0f, canvasWidth, canvasHeight, debugPaint)
+
+ fun drawVerticalLine(x: Float, color: Int) {
+ debugPaint.color = color
+ val x = if (mView.isLeftPanel) x else canvasWidth - x
+ canvas.drawLine(x, debugInfoBottom, x, canvas.height.toFloat(), debugPaint)
+ }
+
+ drawVerticalLine(x = params.swipeTriggerThreshold, color = Color.BLUE)
+ drawVerticalLine(x = startX, color = Color.GREEN)
+ drawVerticalLine(x = previousXTranslation, color = Color.DKGRAY)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index ea41fe74f798..067f4cbb975e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -53,15 +53,15 @@ import android.view.MotionEvent;
import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowManager;
-import android.view.WindowMetrics;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.internal.util.LatencyTracker;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -82,15 +82,19 @@ import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
import com.android.wm.shell.back.BackAnimation;
+import com.android.wm.shell.pip.Pip;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import javax.inject.Inject;
+import javax.inject.Provider;
/**
* Utility class to handle edge swipes for back gesture
@@ -128,7 +132,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
@Override
public void onPrioritizedRotation(@Surface.Rotation int rotation) {
mStartingQuickstepRotation = rotation;
- updateDisabledForQuickstep(mContext.getResources().getConfiguration());
+ updateDisabledForQuickstep(mLastReportedConfig);
}
};
@@ -145,16 +149,6 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mPackageName = "_UNKNOWN";
}
}
-
- @Override
- public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
- mIsInPipMode = true;
- }
-
- @Override
- public void onActivityUnpinned() {
- mIsInPipMode = false;
- }
};
private DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
@@ -182,10 +176,13 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private final PluginManager mPluginManager;
private final ProtoTracer mProtoTracer;
private final NavigationModeController mNavigationModeController;
+ private final BackPanelController.Factory mBackPanelControllerFactory;
private final ViewConfiguration mViewConfiguration;
private final WindowManager mWindowManager;
private final IWindowManager mWindowManagerService;
+ private final Optional<Pip> mPipOptional;
private final FalsingManager mFalsingManager;
+ private final Configuration mLastReportedConfig = new Configuration();
// Activities which should not trigger Back gesture.
private final List<ComponentName> mGestureBlockingActivities = new ArrayList<>();
@@ -199,6 +196,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private final Region mExcludeRegion = new Region();
private final Region mUnrestrictedExcludeRegion = new Region();
private final LatencyTracker mLatencyTracker;
+ private final Provider<BackGestureTfClassifierProvider>
+ mBackGestureTfClassifierProviderProvider;
+ private final FeatureFlags mFeatureFlags;
// The left side edge width where touch down is allowed
private int mEdgeWidthLeft;
@@ -214,6 +214,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
// We temporarily disable back gesture when user is quickswitching
// between apps of different orientations
private boolean mDisabledForQuickstep;
+ // This gets updated when the value of PipTransitionState#isInPip changes.
+ private boolean mIsInPip;
private final PointF mDownPoint = new PointF();
private final PointF mEndPoint = new PointF();
@@ -229,7 +231,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private boolean mIsNavBarShownTransiently;
private boolean mIsBackGestureAllowed;
private boolean mGestureBlockingActivityRunning;
- private boolean mIsInPipMode;
+ private boolean mIsNewBackAffordanceEnabled;
private InputMonitor mInputMonitor;
private InputChannelCompat.InputEventReceiver mInputEventReceiver;
@@ -297,13 +299,27 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
}
};
-
- EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
- SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor,
- BroadcastDispatcher broadcastDispatcher, ProtoTracer protoTracer,
- NavigationModeController navigationModeController, ViewConfiguration viewConfiguration,
- WindowManager windowManager, IWindowManager windowManagerService,
- FalsingManager falsingManager, LatencyTracker latencyTracker) {
+ private final Consumer<Boolean> mOnIsInPipStateChangedListener =
+ (isInPip) -> mIsInPip = isInPip;
+
+ EdgeBackGestureHandler(
+ Context context,
+ OverviewProxyService overviewProxyService,
+ SysUiState sysUiState,
+ PluginManager pluginManager,
+ @Main Executor executor,
+ BroadcastDispatcher broadcastDispatcher,
+ ProtoTracer protoTracer,
+ NavigationModeController navigationModeController,
+ BackPanelController.Factory backPanelControllerFactory,
+ ViewConfiguration viewConfiguration,
+ WindowManager windowManager,
+ IWindowManager windowManagerService,
+ Optional<Pip> pipOptional,
+ FalsingManager falsingManager,
+ LatencyTracker latencyTracker,
+ Provider<BackGestureTfClassifierProvider> backGestureTfClassifierProviderProvider,
+ FeatureFlags featureFlags) {
super(broadcastDispatcher);
mContext = context;
mDisplayId = context.getDisplayId();
@@ -313,11 +329,16 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mPluginManager = pluginManager;
mProtoTracer = protoTracer;
mNavigationModeController = navigationModeController;
+ mBackPanelControllerFactory = backPanelControllerFactory;
mViewConfiguration = viewConfiguration;
mWindowManager = windowManager;
mWindowManagerService = windowManagerService;
+ mPipOptional = pipOptional;
mFalsingManager = falsingManager;
mLatencyTracker = latencyTracker;
+ mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider;
+ mFeatureFlags = featureFlags;
+ mLastReportedConfig.setTo(mContext.getResources().getConfiguration());
ComponentName recentsComponentName = ComponentName.unflattenFromString(
context.getString(com.android.internal.R.string.config_recentsComponentName));
if (recentsComponentName != null) {
@@ -474,6 +495,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mPluginManager.removePluginListener(this);
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
+ mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(null));
try {
mWindowManagerService.unregisterSystemGestureExclusionListener(
@@ -491,6 +513,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
mMainExecutor::execute, mOnPropertiesChangedListener);
+ mPipOptional.ifPresent(
+ pip -> pip.setOnIsInPipStateChangedListener(mOnIsInPipStateChangedListener));
try {
mWindowManagerService.registerSystemGestureExclusionListener(
@@ -507,8 +531,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
Choreographer.getInstance(), this::onInputEvent);
// Add a nav bar panel window
- setEdgeBackPlugin(
- new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
+ mIsNewBackAffordanceEnabled = mFeatureFlags.isEnabled(Flags.NEW_BACK_AFFORDANCE);
+ resetEdgeBackPlugin();
mPluginManager.addPluginListener(
this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
}
@@ -523,7 +547,17 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
@Override
public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) {
- setEdgeBackPlugin(new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
+ resetEdgeBackPlugin();
+ }
+
+ private void resetEdgeBackPlugin() {
+ if (mIsNewBackAffordanceEnabled) {
+ setEdgeBackPlugin(
+ mBackPanelControllerFactory.create(mContext, mBackAnimation));
+ } else {
+ setEdgeBackPlugin(
+ new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
+ }
}
private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
@@ -583,10 +617,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
}
if (newState) {
- String mlModelName = DeviceConfig.getString(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_NAME, "backgesture");
- mBackGestureTfClassifierProvider = SystemUIFactory.getInstance()
- .createBackGestureTfClassifierProvider(mContext.getAssets(), mlModelName);
+ mBackGestureTfClassifierProvider = mBackGestureTfClassifierProviderProvider.get();
mMLModelThreshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
if (mBackGestureTfClassifierProvider.isActive()) {
@@ -653,7 +684,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private boolean isWithinTouchRegion(int x, int y) {
// If the point is inside the PiP or Nav bar overlay excluded bounds, then ignore the back
// gesture
- final boolean isInsidePip = mIsInPipMode && mPipExcludedBounds.contains(x, y);
+ final boolean isInsidePip = mIsInPip && mPipExcludedBounds.contains(x, y);
if (isInsidePip || mNavBarOverlayExcludedBounds.contains(x, y)) {
return false;
}
@@ -856,12 +887,12 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
if (DEBUG_MISSING_GESTURE) {
Log.d(DEBUG_MISSING_GESTURE_TAG, "Config changed: config=" + newConfig);
}
+ mLastReportedConfig.updateFrom(newConfig);
updateDisplaySize();
}
private void updateDisplaySize() {
- WindowMetrics metrics = mWindowManager.getMaximumWindowMetrics();
- Rect bounds = metrics.getBounds();
+ Rect bounds = mLastReportedConfig.windowConfiguration.getMaxBounds();
mDisplaySize.set(bounds.width(), bounds.height());
if (DEBUG_MISSING_GESTURE) {
Log.d(DEBUG_MISSING_GESTURE_TAG, "Update display size: mDisplaySize=" + mDisplaySize);
@@ -906,7 +937,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
pw.println(" mInRejectedExclusion=" + mInRejectedExclusion);
pw.println(" mExcludeRegion=" + mExcludeRegion);
pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion);
- pw.println(" mIsInPipMode=" + mIsInPipMode);
+ pw.println(" mIsInPip=" + mIsInPip);
pw.println(" mPipExcludedBounds=" + mPipExcludedBounds);
pw.println(" mNavBarOverlayExcludedBounds=" + mNavBarOverlayExcludedBounds);
pw.println(" mEdgeWidthLeft=" + mEdgeWidthLeft);
@@ -948,8 +979,12 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
public void setBackAnimation(BackAnimation backAnimation) {
mBackAnimation = backAnimation;
- if (mEdgeBackPlugin != null && mEdgeBackPlugin instanceof NavigationBarEdgePanel) {
- ((NavigationBarEdgePanel) mEdgeBackPlugin).setBackAnimation(backAnimation);
+ if (mEdgeBackPlugin != null) {
+ if (mEdgeBackPlugin instanceof NavigationBarEdgePanel) {
+ ((NavigationBarEdgePanel) mEdgeBackPlugin).setBackAnimation(backAnimation);
+ } else if (mEdgeBackPlugin instanceof BackPanelController) {
+ ((BackPanelController) mEdgeBackPlugin).setBackAnimation(backAnimation);
+ }
}
}
@@ -967,20 +1002,35 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
private final BroadcastDispatcher mBroadcastDispatcher;
private final ProtoTracer mProtoTracer;
private final NavigationModeController mNavigationModeController;
+ private final BackPanelController.Factory mBackPanelControllerFactory;
private final ViewConfiguration mViewConfiguration;
private final WindowManager mWindowManager;
private final IWindowManager mWindowManagerService;
+ private final Optional<Pip> mPipOptional;
private final FalsingManager mFalsingManager;
private final LatencyTracker mLatencyTracker;
+ private final Provider<BackGestureTfClassifierProvider>
+ mBackGestureTfClassifierProviderProvider;
+ private final FeatureFlags mFeatureFlags;
@Inject
public Factory(OverviewProxyService overviewProxyService,
- SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor,
- BroadcastDispatcher broadcastDispatcher, ProtoTracer protoTracer,
- NavigationModeController navigationModeController,
- ViewConfiguration viewConfiguration, WindowManager windowManager,
- IWindowManager windowManagerService, FalsingManager falsingManager,
- LatencyTracker latencyTracker) {
+ SysUiState sysUiState,
+ PluginManager pluginManager,
+ @Main Executor executor,
+ BroadcastDispatcher broadcastDispatcher,
+ ProtoTracer protoTracer,
+ NavigationModeController navigationModeController,
+ BackPanelController.Factory backPanelControllerFactory,
+ ViewConfiguration viewConfiguration,
+ WindowManager windowManager,
+ IWindowManager windowManagerService,
+ Optional<Pip> pipOptional,
+ FalsingManager falsingManager,
+ LatencyTracker latencyTracker,
+ Provider<BackGestureTfClassifierProvider>
+ backGestureTfClassifierProviderProvider,
+ FeatureFlags featureFlags) {
mOverviewProxyService = overviewProxyService;
mSysUiState = sysUiState;
mPluginManager = pluginManager;
@@ -988,19 +1038,37 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mBroadcastDispatcher = broadcastDispatcher;
mProtoTracer = protoTracer;
mNavigationModeController = navigationModeController;
+ mBackPanelControllerFactory = backPanelControllerFactory;
mViewConfiguration = viewConfiguration;
mWindowManager = windowManager;
mWindowManagerService = windowManagerService;
+ mPipOptional = pipOptional;
mFalsingManager = falsingManager;
mLatencyTracker = latencyTracker;
+ mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider;
+ mFeatureFlags = featureFlags;
}
/** Construct a {@link EdgeBackGestureHandler}. */
public EdgeBackGestureHandler create(Context context) {
- return new EdgeBackGestureHandler(context, mOverviewProxyService, mSysUiState,
- mPluginManager, mExecutor, mBroadcastDispatcher, mProtoTracer,
- mNavigationModeController, mViewConfiguration, mWindowManager,
- mWindowManagerService, mFalsingManager, mLatencyTracker);
+ return new EdgeBackGestureHandler(
+ context,
+ mOverviewProxyService,
+ mSysUiState,
+ mPluginManager,
+ mExecutor,
+ mBroadcastDispatcher,
+ mProtoTracer,
+ mNavigationModeController,
+ mBackPanelControllerFactory,
+ mViewConfiguration,
+ mWindowManager,
+ mWindowManagerService,
+ mPipOptional,
+ mFalsingManager,
+ mLatencyTracker,
+ mBackGestureTfClassifierProviderProvider,
+ mFeatureFlags);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
new file mode 100644
index 000000000000..a3fb58d5b015
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -0,0 +1,140 @@
+package com.android.systemui.navigationbar.gestural
+
+import android.content.res.Resources
+import com.android.systemui.R
+
+data class EdgePanelParams(private var resources: Resources) {
+
+ data class ArrowDimens(
+ val length: Float = 0f,
+ val height: Float = 0f
+ )
+
+ data class BackgroundDimens(
+ val width: Float = 0f,
+ val height: Float = 0f,
+ val edgeCornerRadius: Float = 0f,
+ val farCornerRadius: Float = 0f
+ )
+
+ data class BackIndicatorDimens(
+ val horizontalTranslation: Float = 0f,
+ val arrowDimens: ArrowDimens = ArrowDimens(),
+ val backgroundDimens: BackgroundDimens = BackgroundDimens()
+ )
+
+ var arrowThickness: Float = 0f
+ private set
+ var entryIndicator = BackIndicatorDimens()
+ private set
+ var activeIndicator = BackIndicatorDimens()
+ private set
+ var preThresholdIndicator = BackIndicatorDimens()
+ private set
+ var fullyStretchedIndicator = BackIndicatorDimens()
+ private set
+ var cancelledArrowDimens = ArrowDimens()
+
+ // navigation bar edge constants
+ var arrowPaddingEnd: Int = 0
+ private set
+
+ // The closest to y
+ var minArrowYPosition: Int = 0
+ private set
+ var fingerOffset: Int = 0
+ private set
+ var swipeTriggerThreshold: Float = 0f
+ private set
+ var swipeProgressThreshold: Float = 0f
+ private set
+
+ // The minimum delta needed to change direction / stop triggering back
+ var minDeltaForSwitch: Int = 0
+ private set
+
+ init {
+ update(resources)
+ }
+
+ private fun getDimen(id: Int): Float {
+ return resources.getDimension(id)
+ }
+
+ private fun getPx(id: Int): Int {
+ return resources.getDimensionPixelSize(id)
+ }
+
+ fun update(resources: Resources) {
+ this.resources = resources
+ arrowThickness = getDimen(R.dimen.navigation_edge_arrow_thickness)
+ arrowPaddingEnd = getPx(R.dimen.navigation_edge_panel_padding)
+ minArrowYPosition = getPx(R.dimen.navigation_edge_arrow_min_y)
+ fingerOffset = getPx(R.dimen.navigation_edge_finger_offset)
+ swipeTriggerThreshold = getDimen(R.dimen.navigation_edge_action_drag_threshold)
+ swipeProgressThreshold = getDimen(R.dimen.navigation_edge_action_progress_threshold)
+ minDeltaForSwitch = getPx(R.dimen.navigation_edge_minimum_x_delta_for_switch)
+
+ entryIndicator = BackIndicatorDimens(
+ horizontalTranslation = getDimen(R.dimen.navigation_edge_entry_margin),
+ arrowDimens = ArrowDimens(
+ length = getDimen(R.dimen.navigation_edge_entry_arrow_length),
+ height = getDimen(R.dimen.navigation_edge_entry_arrow_height),
+ ),
+ backgroundDimens = BackgroundDimens(
+ width = getDimen(R.dimen.navigation_edge_entry_background_width),
+ height = getDimen(R.dimen.navigation_edge_entry_background_height),
+ edgeCornerRadius = getDimen(R.dimen.navigation_edge_entry_edge_corners),
+ farCornerRadius = getDimen(R.dimen.navigation_edge_entry_far_corners)
+ )
+ )
+
+ activeIndicator = BackIndicatorDimens(
+ horizontalTranslation = getDimen(R.dimen.navigation_edge_active_margin),
+ arrowDimens = ArrowDimens(
+ length = getDimen(R.dimen.navigation_edge_active_arrow_length),
+ height = getDimen(R.dimen.navigation_edge_active_arrow_height),
+ ),
+ backgroundDimens = BackgroundDimens(
+ width = getDimen(R.dimen.navigation_edge_active_background_width),
+ height = getDimen(R.dimen.navigation_edge_active_background_height),
+ edgeCornerRadius = getDimen(R.dimen.navigation_edge_active_edge_corners),
+ farCornerRadius = getDimen(R.dimen.navigation_edge_active_far_corners)
+
+ )
+ )
+
+ preThresholdIndicator = BackIndicatorDimens(
+ horizontalTranslation = getDimen(R.dimen.navigation_edge_pre_threshold_margin),
+ arrowDimens = ArrowDimens(
+ length = entryIndicator.arrowDimens.length,
+ height = entryIndicator.arrowDimens.height,
+ ),
+ backgroundDimens = BackgroundDimens(
+ width = getDimen(R.dimen.navigation_edge_pre_threshold_background_width),
+ height = getDimen(R.dimen.navigation_edge_pre_threshold_background_height),
+ edgeCornerRadius = getDimen(R.dimen.navigation_edge_pre_threshold_edge_corners),
+ farCornerRadius = getDimen(R.dimen.navigation_edge_pre_threshold_far_corners)
+ )
+ )
+
+ fullyStretchedIndicator = BackIndicatorDimens(
+ horizontalTranslation = getDimen(R.dimen.navigation_edge_stretch_margin),
+ arrowDimens = ArrowDimens(
+ length = getDimen(R.dimen.navigation_edge_stretched_arrow_length),
+ height = getDimen(R.dimen.navigation_edge_stretched_arrow_height),
+ ),
+ backgroundDimens = BackgroundDimens(
+ width = getDimen(R.dimen.navigation_edge_stretch_background_width),
+ height = getDimen(R.dimen.navigation_edge_stretch_background_height),
+ edgeCornerRadius = getDimen(R.dimen.navigation_edge_stretch_edge_corners),
+ farCornerRadius = getDimen(R.dimen.navigation_edge_stretch_far_corners)
+ )
+ )
+
+ cancelledArrowDimens = ArrowDimens(
+ length = getDimen(R.dimen.navigation_edge_cancelled_arrow_length),
+ height = getDimen(R.dimen.navigation_edge_cancelled_arrow_height)
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/GestureModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/GestureModule.java
new file mode 100644
index 000000000000..f98496d43730
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/GestureModule.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.navigationbar.gestural;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ *
+ */
+@Module
+public interface GestureModule {
+ /** */
+ @Provides
+ static BackGestureTfClassifierProvider providsBackGestureTfClassifierProvider() {
+ return new BackGestureTfClassifierProvider();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleModule.kt b/packages/SystemUI/src/com/android/systemui/people/PeopleModule.kt
new file mode 100644
index 000000000000..dd35445f9b7b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleModule.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.people
+
+import com.android.systemui.people.data.repository.PeopleTileRepository
+import com.android.systemui.people.data.repository.PeopleTileRepositoryImpl
+import com.android.systemui.people.data.repository.PeopleWidgetRepository
+import com.android.systemui.people.data.repository.PeopleWidgetRepositoryImpl
+import dagger.Binds
+import dagger.Module
+
+/** Dagger module to provide/bind people space dependencies. */
+@Module
+interface PeopleModule {
+ @Binds fun bindTileRepository(impl: PeopleTileRepositoryImpl): PeopleTileRepository
+
+ @Binds fun bindWidgetRepository(impl: PeopleWidgetRepositoryImpl): PeopleWidgetRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
index b55d86e8fb1c..0ba077eb0912 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
@@ -29,7 +29,8 @@ import android.os.UserHandle;
import android.util.Log;
import android.widget.RemoteViews;
-import com.android.systemui.SystemUIAppComponentFactory;
+import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback;
+import com.android.systemui.SystemUIAppComponentFactoryBase.ContextInitializer;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.shared.system.PeopleProviderUtils;
@@ -37,11 +38,11 @@ import javax.inject.Inject;
/** API that returns a People Tile preview. */
public class PeopleProvider extends ContentProvider implements
- SystemUIAppComponentFactory.ContextInitializer {
+ ContextInitializer {
private static final String TAG = "PeopleProvider";
private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
private static final String EMPTY_STRING = "";
- private SystemUIAppComponentFactory.ContextAvailableCallback mCallback;
+ private ContextAvailableCallback mCallback;
@Inject
PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
@@ -144,7 +145,7 @@ public class PeopleProvider extends ContentProvider implements
@Override
public void setContextAvailableCallback(
- SystemUIAppComponentFactory.ContextAvailableCallback callback) {
+ ContextAvailableCallback callback) {
mCallback = callback;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index 93a3f81fdd6b..e845aa85a121 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -19,144 +19,52 @@ package com.android.systemui.people;
import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID;
-import static com.android.systemui.people.PeopleTileViewHelper.getPersonIconBitmap;
-import static com.android.systemui.people.PeopleTileViewHelper.getSizeInDp;
-
-import android.app.Activity;
-import android.app.people.PeopleSpaceTile;
-import android.content.Context;
import android.content.Intent;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.Outline;
-import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.widget.LinearLayout;
-import com.android.systemui.R;
-import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
-import com.android.systemui.people.widget.PeopleTileKey;
+import androidx.activity.ComponentActivity;
+import androidx.lifecycle.ViewModelProvider;
-import java.util.ArrayList;
-import java.util.List;
+import com.android.systemui.people.ui.view.PeopleViewBinder;
+import com.android.systemui.people.ui.viewmodel.PeopleViewModel;
import javax.inject.Inject;
/** People Tile Widget configuration activity that shows the user their conversation tiles. */
-public class PeopleSpaceActivity extends Activity {
+public class PeopleSpaceActivity extends ComponentActivity {
private static final String TAG = "PeopleSpaceActivity";
private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
- private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
- private Context mContext;
- private int mAppWidgetId;
+ private final PeopleViewModel.Factory mViewModelFactory;
+ private PeopleViewModel mViewModel;
@Inject
- public PeopleSpaceActivity(PeopleSpaceWidgetManager peopleSpaceWidgetManager) {
+ public PeopleSpaceActivity(PeopleViewModel.Factory viewModelFactory) {
super();
- mPeopleSpaceWidgetManager = peopleSpaceWidgetManager;
-
+ mViewModelFactory = viewModelFactory;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mContext = getApplicationContext();
- mAppWidgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID,
- INVALID_APPWIDGET_ID);
setResult(RESULT_CANCELED);
- }
-
- /** Builds the conversation selection activity. */
- private void buildActivity() {
- List<PeopleSpaceTile> priorityTiles = new ArrayList<>();
- List<PeopleSpaceTile> recentTiles = new ArrayList<>();
- try {
- priorityTiles = mPeopleSpaceWidgetManager.getPriorityTiles();
- recentTiles = mPeopleSpaceWidgetManager.getRecentTiles();
- } catch (Exception e) {
- Log.e(TAG, "Couldn't retrieve conversations", e);
- }
-
- // If no conversations, render activity without conversations
- if (recentTiles.isEmpty() && priorityTiles.isEmpty()) {
- setContentView(R.layout.people_space_activity_no_conversations);
-
- // The Tile preview has colorBackground as its background. Change it so it's different
- // than the activity's background.
- LinearLayout item = findViewById(android.R.id.background);
- GradientDrawable shape = (GradientDrawable) item.getBackground();
- final TypedArray ta = mContext.getTheme().obtainStyledAttributes(
- new int[]{com.android.internal.R.attr.colorSurface});
- shape.setColor(ta.getColor(0, Color.WHITE));
- return;
- }
-
- setContentView(R.layout.people_space_activity);
- setTileViews(R.id.priority, R.id.priority_tiles, priorityTiles);
- setTileViews(R.id.recent, R.id.recent_tiles, recentTiles);
- }
-
- private ViewOutlineProvider mViewOutlineProvider = new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(),
- mContext.getResources().getDimension(R.dimen.people_space_widget_radius));
- }
- };
-
- /** Sets a {@link PeopleSpaceTileView}s for each conversation. */
- private void setTileViews(int viewId, int tilesId, List<PeopleSpaceTile> tiles) {
- if (tiles.isEmpty()) {
- LinearLayout view = findViewById(viewId);
- view.setVisibility(View.GONE);
- return;
- }
-
- ViewGroup layout = findViewById(tilesId);
- layout.setClipToOutline(true);
- layout.setOutlineProvider(mViewOutlineProvider);
- for (int i = 0; i < tiles.size(); ++i) {
- PeopleSpaceTile tile = tiles.get(i);
- PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext,
- layout, tile.getId(), i == (tiles.size() - 1));
- setTileView(tileView, tile);
- }
- }
-
- /** Sets {@code tileView} with the data in {@code conversation}. */
- private void setTileView(PeopleSpaceTileView tileView, PeopleSpaceTile tile) {
- try {
- if (tile.getUserName() != null) {
- tileView.setName(tile.getUserName().toString());
- }
- tileView.setPersonIcon(getPersonIconBitmap(mContext, tile,
- getSizeInDp(mContext, R.dimen.avatar_size_for_medium,
- mContext.getResources().getDisplayMetrics().density)));
-
- PeopleTileKey key = new PeopleTileKey(tile);
- tileView.setOnClickListener(v -> storeWidgetConfiguration(tile, key));
- } catch (Exception e) {
- Log.e(TAG, "Couldn't retrieve shortcut information", e);
- }
- }
-
- /** Stores the user selected configuration for {@code mAppWidgetId}. */
- private void storeWidgetConfiguration(PeopleSpaceTile tile, PeopleTileKey key) {
- if (PeopleSpaceUtils.DEBUG) {
- if (DEBUG) {
- Log.d(TAG, "Put " + tile.getUserName() + "'s shortcut ID: "
- + tile.getId() + " for widget ID: "
- + mAppWidgetId);
- }
- }
- mPeopleSpaceWidgetManager.addNewWidget(mAppWidgetId, key);
- finishActivity();
+ mViewModel = new ViewModelProvider(this, mViewModelFactory).get(PeopleViewModel.class);
+
+ // Update the widget ID coming from the intent.
+ int widgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID);
+ mViewModel.onWidgetIdChanged(widgetId);
+
+ ViewGroup view = PeopleViewBinder.create(this);
+ PeopleViewBinder.bind(view, mViewModel, /* lifecycleOwner= */ this,
+ () -> {
+ finishActivity();
+ return null;
+ });
+ setContentView(view);
}
/** Finish activity with a successful widget configuration result. */
@@ -169,19 +77,13 @@ public class PeopleSpaceActivity extends Activity {
/** Finish activity without choosing a widget. */
public void dismissActivity(View v) {
if (DEBUG) Log.d(TAG, "Activity dismissed with no widgets added!");
+ setResult(RESULT_CANCELED);
finish();
}
private void setActivityResult(int result) {
Intent resultValue = new Intent();
- resultValue.putExtra(EXTRA_APPWIDGET_ID, mAppWidgetId);
+ resultValue.putExtra(EXTRA_APPWIDGET_ID, mViewModel.getAppWidgetId().getValue());
setResult(result, resultValue);
}
-
- @Override
- protected void onResume() {
- super.onResume();
- // Refresh tile views to sync new conversations.
- buildActivity();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java
index 4ee951f3cdb1..58e700f81388 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java
@@ -28,6 +28,7 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.IconDrawableFactory;
import android.util.Log;
+import android.view.ContextThemeWrapper;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
@@ -52,16 +53,15 @@ class PeopleStoryIconFactory implements AutoCloseable {
PeopleStoryIconFactory(Context context, PackageManager pm,
IconDrawableFactory iconDrawableFactory, int iconSizeDp) {
- context.setTheme(android.R.style.Theme_DeviceDefault_DayNight);
- mIconBitmapSize = (int) (iconSizeDp * context.getResources().getDisplayMetrics().density);
- mDensity = context.getResources().getDisplayMetrics().density;
+ mContext = new ContextThemeWrapper(context, android.R.style.Theme_DeviceDefault_DayNight);
+ mIconBitmapSize = (int) (iconSizeDp * mContext.getResources().getDisplayMetrics().density);
+ mDensity = mContext.getResources().getDisplayMetrics().density;
mIconSize = mDensity * iconSizeDp;
mPackageManager = pm;
mIconDrawableFactory = iconDrawableFactory;
- mImportantConversationColor = context.getColor(R.color.important_conversation);
- mAccentColor = Utils.getColorAttr(context,
+ mImportantConversationColor = mContext.getColor(R.color.important_conversation);
+ mAccentColor = Utils.getColorAttr(mContext,
com.android.internal.R.attr.colorAccentPrimaryVariant).getDefaultColor();
- mContext = context;
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 00aa1381ace1..be82b1faac8e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -75,6 +75,7 @@ import androidx.core.math.MathUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
+import com.android.systemui.people.data.model.PeopleTileModel;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import com.android.systemui.people.widget.PeopleTileKey;
@@ -299,7 +300,8 @@ public class PeopleTileViewHelper {
return createLastInteractionRemoteViews();
}
- private static boolean isDndBlockingTileData(@Nullable PeopleSpaceTile tile) {
+ /** Whether the conversation associated with {@code tile} can bypass DND. */
+ public static boolean isDndBlockingTileData(@Nullable PeopleSpaceTile tile) {
if (tile == null) return false;
int notificationPolicyState = tile.getNotificationPolicyState();
@@ -536,7 +538,8 @@ public class PeopleTileViewHelper {
return views;
}
- private static boolean getHasNewStory(PeopleSpaceTile tile) {
+ /** Whether {@code tile} has a new story. */
+ public static boolean getHasNewStory(PeopleSpaceTile tile) {
return tile.getStatuses() != null && tile.getStatuses().stream().anyMatch(
c -> c.getActivity() == ACTIVITY_NEW_STORY);
}
@@ -1250,16 +1253,24 @@ public class PeopleTileViewHelper {
}
/** Returns a bitmap with the user icon and package icon. */
- public static Bitmap getPersonIconBitmap(Context context, PeopleSpaceTile tile,
+ public static Bitmap getPersonIconBitmap(Context context, PeopleTileModel tile,
int maxAvatarSize) {
- boolean hasNewStory = getHasNewStory(tile);
- return getPersonIconBitmap(context, tile, maxAvatarSize, hasNewStory);
+ return getPersonIconBitmap(context, maxAvatarSize, tile.getHasNewStory(),
+ tile.getUserIcon(), tile.getKey().getPackageName(), tile.getKey().getUserId(),
+ tile.isImportant(), tile.isDndBlocking());
}
/** Returns a bitmap with the user icon and package icon. */
private static Bitmap getPersonIconBitmap(
Context context, PeopleSpaceTile tile, int maxAvatarSize, boolean hasNewStory) {
- Icon icon = tile.getUserIcon();
+ return getPersonIconBitmap(context, maxAvatarSize, hasNewStory, tile.getUserIcon(),
+ tile.getPackageName(), getUserId(tile),
+ tile.isImportantConversation(), isDndBlockingTileData(tile));
+ }
+
+ private static Bitmap getPersonIconBitmap(
+ Context context, int maxAvatarSize, boolean hasNewStory, Icon icon, String packageName,
+ int userId, boolean importantConversation, boolean dndBlockingTileData) {
if (icon == null) {
Drawable placeholder = context.getDrawable(R.drawable.ic_avatar_with_badge).mutate();
placeholder.setColorFilter(getDisabledColorFilter());
@@ -1272,10 +1283,10 @@ public class PeopleTileViewHelper {
RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
context.getResources(), icon.getBitmap());
Drawable personDrawable = storyIcon.getPeopleTileDrawable(roundedDrawable,
- tile.getPackageName(), getUserId(tile), tile.isImportantConversation(),
+ packageName, userId, importantConversation,
hasNewStory);
- if (isDndBlockingTileData(tile)) {
+ if (dndBlockingTileData) {
personDrawable.setColorFilter(getDisabledColorFilter());
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/data/model/PeopleTileModel.kt b/packages/SystemUI/src/com/android/systemui/people/data/model/PeopleTileModel.kt
new file mode 100644
index 000000000000..5d8539fabc6b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/data/model/PeopleTileModel.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.people.data.model
+
+import android.graphics.drawable.Icon
+import com.android.systemui.people.widget.PeopleTileKey
+
+/** Models a tile/conversation. */
+data class PeopleTileModel(
+ val key: PeopleTileKey,
+ val username: String,
+ val userIcon: Icon,
+ val hasNewStory: Boolean,
+ val isImportant: Boolean,
+ val isDndBlocking: Boolean,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/people/data/repository/PeopleTileRepository.kt b/packages/SystemUI/src/com/android/systemui/people/data/repository/PeopleTileRepository.kt
new file mode 100644
index 000000000000..01b43d52130b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/data/repository/PeopleTileRepository.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.people.data.repository
+
+import android.app.people.PeopleSpaceTile
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.people.PeopleTileViewHelper
+import com.android.systemui.people.data.model.PeopleTileModel
+import com.android.systemui.people.widget.PeopleSpaceWidgetManager
+import com.android.systemui.people.widget.PeopleTileKey
+import javax.inject.Inject
+
+/** A Repository to fetch the current tiles/conversations. */
+// TODO(b/238993727): Make the tiles API reactive.
+interface PeopleTileRepository {
+ /* The current priority tiles. */
+ fun priorityTiles(): List<PeopleTileModel>
+
+ /* The current recent tiles. */
+ fun recentTiles(): List<PeopleTileModel>
+}
+
+@SysUISingleton
+class PeopleTileRepositoryImpl
+@Inject
+constructor(
+ private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager,
+) : PeopleTileRepository {
+ override fun priorityTiles(): List<PeopleTileModel> {
+ return peopleSpaceWidgetManager.priorityTiles.map { it.toModel() }
+ }
+
+ override fun recentTiles(): List<PeopleTileModel> {
+ return peopleSpaceWidgetManager.recentTiles.map { it.toModel() }
+ }
+
+ private fun PeopleSpaceTile.toModel(): PeopleTileModel {
+ return PeopleTileModel(
+ PeopleTileKey(this),
+ userName.toString(),
+ userIcon,
+ PeopleTileViewHelper.getHasNewStory(this),
+ isImportantConversation,
+ PeopleTileViewHelper.isDndBlockingTileData(this),
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/data/repository/PeopleWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/people/data/repository/PeopleWidgetRepository.kt
new file mode 100644
index 000000000000..f2b6cb1fc3f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/data/repository/PeopleWidgetRepository.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.people.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.people.widget.PeopleSpaceWidgetManager
+import com.android.systemui.people.widget.PeopleTileKey
+import javax.inject.Inject
+
+interface PeopleWidgetRepository {
+ /**
+ * Bind the widget with ID [widgetId] to the tile keyed by [tileKey].
+ *
+ * If there is already a widget with [widgetId], this existing widget will be reconfigured and
+ * associated to this tile. If there is no widget with [widgetId], a new one will be created.
+ */
+ fun setWidgetTile(widgetId: Int, tileKey: PeopleTileKey)
+}
+
+@SysUISingleton
+class PeopleWidgetRepositoryImpl
+@Inject
+constructor(
+ private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager,
+) : PeopleWidgetRepository {
+ override fun setWidgetTile(widgetId: Int, tileKey: PeopleTileKey) {
+ peopleSpaceWidgetManager.addNewWidget(widgetId, tileKey)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt b/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt
new file mode 100644
index 000000000000..bc982cccaacd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.people.ui.view
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Outline
+import android.graphics.drawable.GradientDrawable
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewOutlineProvider
+import android.widget.LinearLayout
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.Lifecycle.State.CREATED
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
+import com.android.systemui.people.PeopleSpaceTileView
+import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel
+import com.android.systemui.people.ui.viewmodel.PeopleViewModel
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
+
+/** A ViewBinder for [PeopleViewModel]. */
+object PeopleViewBinder {
+ private const val TAG = "PeopleSpaceViewBinder"
+
+ /**
+ * The [ViewOutlineProvider] used to clip the corner radius of the recent and priority lists.
+ */
+ private val ViewOutlineProvider =
+ object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ outline.setRoundRect(
+ 0,
+ 0,
+ view.width,
+ view.height,
+ view.context.resources.getDimension(R.dimen.people_space_widget_radius),
+ )
+ }
+ }
+
+ /** Create a [View] that can later be [bound][bind] to a [PeopleViewModel]. */
+ @JvmStatic
+ fun create(context: Context): ViewGroup {
+ return LayoutInflater.from(context)
+ .inflate(R.layout.people_space_activity, /* root= */ null) as ViewGroup
+ }
+
+ /** Bind [view] to [viewModel]. */
+ @JvmStatic
+ fun bind(
+ view: ViewGroup,
+ viewModel: PeopleViewModel,
+ lifecycleOwner: LifecycleOwner,
+ onFinish: () -> Unit,
+ ) {
+ // Call [onFinish] this activity when the ViewModel tells us so.
+ lifecycleOwner.lifecycleScope.launch {
+ lifecycleOwner.repeatOnLifecycle(CREATED) {
+ viewModel.isFinished.collect { isFinished ->
+ if (isFinished) {
+ viewModel.clearIsFinished()
+ onFinish()
+ }
+ }
+ }
+ }
+
+ // Start collecting the UI data once the Activity is STARTED.
+ lifecycleOwner.lifecycleScope.launch {
+ lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ combine(
+ viewModel.priorityTiles,
+ viewModel.recentTiles,
+ ) { priority, recent ->
+ priority to recent
+ }
+ .collect { (priorityTiles, recentTiles) ->
+ if (priorityTiles.isNotEmpty() || recentTiles.isNotEmpty()) {
+ setConversationsContent(
+ view,
+ priorityTiles,
+ recentTiles,
+ viewModel::onTileClicked,
+ )
+ } else {
+ setNoConversationsContent(view)
+ }
+ }
+ }
+ }
+
+ // Make sure to refresh the tiles/conversations when the Activity is resumed, so that it
+ // updates them when going back to the Activity after leaving it.
+ lifecycleOwner.lifecycleScope.launch {
+ lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ viewModel.onTileRefreshRequested()
+ }
+ }
+ }
+
+ private fun setNoConversationsContent(view: ViewGroup) {
+ // This should never happen.
+ if (view.childCount > 1) {
+ error("view has ${view.childCount} children, it should have maximum 1")
+ }
+
+ // The static content for no conversations is already shown.
+ if (view.findViewById<View>(R.id.top_level_no_conversations) != null) {
+ return
+ }
+
+ // If we were showing the content with conversations earlier, remove it.
+ if (view.childCount == 1) {
+ view.removeViewAt(0)
+ }
+
+ val context = view.context
+ val noConversationsView =
+ LayoutInflater.from(context)
+ .inflate(R.layout.people_space_activity_no_conversations, /* root= */ view)
+
+ // The Tile preview has colorBackground as its background. Change it so it's different than
+ // the activity's background.
+ val item = noConversationsView.findViewById<LinearLayout>(android.R.id.background)
+ val shape = item.background as GradientDrawable
+ val ta =
+ context.theme.obtainStyledAttributes(
+ intArrayOf(com.android.internal.R.attr.colorSurface)
+ )
+ shape.setColor(ta.getColor(0, Color.WHITE))
+ ta.recycle()
+ }
+
+ private fun setConversationsContent(
+ view: ViewGroup,
+ priorityTiles: List<PeopleTileViewModel>,
+ recentTiles: List<PeopleTileViewModel>,
+ onTileClicked: (PeopleTileViewModel) -> Unit,
+ ) {
+ // This should never happen.
+ if (view.childCount > 1) {
+ error("view has ${view.childCount} children, it should have maximum 1")
+ }
+
+ // Inflate the content with conversations, if it's not already.
+ if (view.findViewById<View>(R.id.top_level_with_conversations) == null) {
+ // If we were showing the content without conversations earlier, remove it.
+ if (view.childCount == 1) {
+ view.removeViewAt(0)
+ }
+
+ LayoutInflater.from(view.context)
+ .inflate(R.layout.people_space_activity_with_conversations, /* root= */ view)
+ }
+
+ // TODO(b/193782241): Replace the NestedScrollView + 2x LinearLayout from this layout into a
+ // single RecyclerView once this screen is tested by screenshot tests. Introduce a
+ // PeopleSpaceTileViewBinder that will properly create and bind the View associated to a
+ // PeopleSpaceTileViewModel (and remove the PeopleSpaceTileView class).
+ val conversationsView = view.requireViewById<View>(R.id.top_level_with_conversations)
+ setTileViews(
+ conversationsView,
+ R.id.priority,
+ R.id.priority_tiles,
+ priorityTiles,
+ onTileClicked,
+ )
+
+ setTileViews(
+ conversationsView,
+ R.id.recent,
+ R.id.recent_tiles,
+ recentTiles,
+ onTileClicked,
+ )
+ }
+
+ /** Sets a [PeopleSpaceTileView]s for each conversation. */
+ private fun setTileViews(
+ root: View,
+ tilesListId: Int,
+ tilesId: Int,
+ tiles: List<PeopleTileViewModel>,
+ onTileClicked: (PeopleTileViewModel) -> Unit,
+ ) {
+ // Remove any previously added tile.
+ // TODO(b/193782241): Once this list is a big RecyclerView, set the current list and use
+ // DiffUtil to do as less addView/removeView as possible.
+ val layout = root.requireViewById<ViewGroup>(tilesId)
+ layout.removeAllViews()
+ layout.outlineProvider = ViewOutlineProvider
+
+ val tilesListView = root.requireViewById<LinearLayout>(tilesListId)
+ if (tiles.isEmpty()) {
+ tilesListView.visibility = View.GONE
+ return
+ }
+ tilesListView.visibility = View.VISIBLE
+
+ // Add each tile.
+ tiles.forEachIndexed { i, tile ->
+ val tileView =
+ PeopleSpaceTileView(root.context, layout, tile.key.shortcutId, i == tiles.size - 1)
+ bindTileView(tileView, tile, onTileClicked)
+ }
+ }
+
+ /** Sets [tileView] with the data in [conversation]. */
+ private fun bindTileView(
+ tileView: PeopleSpaceTileView,
+ tile: PeopleTileViewModel,
+ onTileClicked: (PeopleTileViewModel) -> Unit,
+ ) {
+ try {
+ tileView.setName(tile.username)
+ tileView.setPersonIcon(tile.icon)
+ tileView.setOnClickListener { onTileClicked(tile) }
+ } catch (e: Exception) {
+ Log.e(TAG, "Couldn't retrieve shortcut information", e)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleTileViewModel.kt
new file mode 100644
index 000000000000..40205ce9424a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleTileViewModel.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.people.ui.viewmodel
+
+import android.graphics.Bitmap
+import com.android.systemui.people.widget.PeopleTileKey
+
+/** Models UI state for a single tile/conversation. */
+data class PeopleTileViewModel(
+ val key: PeopleTileKey,
+ val icon: Bitmap,
+ val username: String?,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt b/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt
new file mode 100644
index 000000000000..17de991588b8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.people.ui.viewmodel
+
+import android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID
+import android.content.Context
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.people.PeopleSpaceUtils
+import com.android.systemui.people.PeopleTileViewHelper
+import com.android.systemui.people.data.model.PeopleTileModel
+import com.android.systemui.people.data.repository.PeopleTileRepository
+import com.android.systemui.people.data.repository.PeopleWidgetRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Models UI state for the people space, allowing the user to select which conversation should be
+ * associated to a new or existing Conversation widget.
+ */
+class PeopleViewModel(
+ @Application private val context: Context,
+ private val tileRepository: PeopleTileRepository,
+ private val widgetRepository: PeopleWidgetRepository,
+) : ViewModel() {
+ /**
+ * The list of the priority tiles/conversations.
+ *
+ * Important: Even though this is a Flow, the underlying API used to populate this Flow is not
+ * reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles.
+ */
+ private val _priorityTiles = MutableStateFlow(priorityTiles())
+ val priorityTiles: Flow<List<PeopleTileViewModel>> = _priorityTiles
+
+ /**
+ * The list of the priority tiles/conversations.
+ *
+ * Important: Even though this is a Flow, the underlying API used to populate this Flow is not
+ * reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles.
+ */
+ private val _recentTiles = MutableStateFlow(recentTiles())
+ val recentTiles: Flow<List<PeopleTileViewModel>> = _recentTiles
+
+ /** The ID of the widget currently being edited/added. */
+ private val _appWidgetId = MutableStateFlow(INVALID_APPWIDGET_ID)
+ val appWidgetId: StateFlow<Int> = _appWidgetId
+
+ /** Whether the user journey is complete. */
+ private val _isFinished = MutableStateFlow(false)
+ val isFinished: StateFlow<Boolean> = _isFinished
+
+ /** Refresh the [priorityTiles] and [recentTiles]. */
+ fun onTileRefreshRequested() {
+ _priorityTiles.value = priorityTiles()
+ _recentTiles.value = recentTiles()
+ }
+
+ /** Called when the [appWidgetId] should be changed to [widgetId]. */
+ fun onWidgetIdChanged(widgetId: Int) {
+ _appWidgetId.value = widgetId
+ }
+
+ /** Clear [isFinished], setting it to false. */
+ fun clearIsFinished() {
+ _isFinished.value = false
+ }
+
+ /** Called when a tile is clicked. */
+ fun onTileClicked(tile: PeopleTileViewModel) {
+ if (PeopleSpaceUtils.DEBUG) {
+ Log.d(
+ TAG,
+ "Put ${tile.username}'s shortcut ID: ${tile.key.shortcutId} for widget ID: " +
+ _appWidgetId.value
+ )
+ }
+ widgetRepository.setWidgetTile(_appWidgetId.value, tile.key)
+ _isFinished.value = true
+ }
+
+ private fun priorityTiles(): List<PeopleTileViewModel> {
+ return try {
+ tileRepository.priorityTiles().map { it.toViewModel() }
+ } catch (e: Exception) {
+ Log.e(TAG, "Couldn't retrieve priority conversations", e)
+ emptyList()
+ }
+ }
+
+ private fun recentTiles(): List<PeopleTileViewModel> {
+ return try {
+ tileRepository.recentTiles().map { it.toViewModel() }
+ } catch (e: Exception) {
+ Log.e(TAG, "Couldn't retrieve recent conversations", e)
+ emptyList()
+ }
+ }
+
+ private fun PeopleTileModel.toViewModel(): PeopleTileViewModel {
+ val icon =
+ PeopleTileViewHelper.getPersonIconBitmap(
+ context,
+ this,
+ PeopleTileViewHelper.getSizeInDp(
+ context,
+ R.dimen.avatar_size_for_medium,
+ context.resources.displayMetrics.density,
+ )
+ )
+ return PeopleTileViewModel(key, icon, username)
+ }
+
+ /** The Factory that should be used to create a [PeopleViewModel]. */
+ class Factory
+ @Inject
+ constructor(
+ @Application private val context: Context,
+ private val tileRepository: PeopleTileRepository,
+ private val widgetRepository: PeopleWidgetRepository,
+ ) : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ check(modelClass == PeopleViewModel::class.java)
+ return PeopleViewModel(context, tileRepository, widgetRepository) as T
+ }
+ }
+
+ companion object {
+ private const val TAG = "PeopleSpaceViewModel"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
index e1b97a454c5d..20c6c556706e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.os.Bundle;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
@@ -34,6 +35,7 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.people.PeopleSpaceUtils;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -43,6 +45,7 @@ import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.bubbles.Bubble;
import java.util.Optional;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -58,6 +61,7 @@ public class LaunchConversationActivity extends Activity {
private boolean mIsForTesting;
private IStatusBarService mIStatusBarService;
private CommandQueue mCommandQueue;
+ private Executor mBgExecutor;
private Bubble mBubble;
private NotificationEntry mEntryToBubble;
@@ -67,7 +71,8 @@ public class LaunchConversationActivity extends Activity {
CommonNotifCollection commonNotifCollection,
Optional<BubblesManager> bubblesManagerOptional,
UserManager userManager,
- CommandQueue commandQueue
+ CommandQueue commandQueue,
+ @Background Executor bgExecutor
) {
super();
mVisibilityProvider = visibilityProvider;
@@ -91,6 +96,7 @@ public class LaunchConversationActivity extends Activity {
mCommandQueue.removeCallback(this);
}
});
+ mBgExecutor = bgExecutor;
}
@Override
@@ -172,34 +178,36 @@ public class LaunchConversationActivity extends Activity {
return;
}
- try {
- if (mIStatusBarService == null || mCommonNotifCollection == null) {
- if (DEBUG) {
- Log.d(TAG, "Skipping clear notification: null services, key: " + notifKey);
- }
- return;
+ if (mIStatusBarService == null || mCommonNotifCollection == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Skipping clear notification: null services, key: " + notifKey);
}
+ return;
+ }
- NotificationEntry entry = mCommonNotifCollection.getEntry(notifKey);
- if (entry == null || entry.getRanking() == null) {
- if (DEBUG) {
- Log.d(TAG, "Skipping clear notification: NotificationEntry or its Ranking"
- + " is null, key: " + notifKey);
- }
- return;
+ NotificationEntry entry = mCommonNotifCollection.getEntry(notifKey);
+ if (entry == null || entry.getRanking() == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Skipping clear notification: NotificationEntry or its Ranking"
+ + " is null, key: " + notifKey);
}
+ return;
+ }
- NotificationVisibility notifVisibility = mVisibilityProvider.obtain(entry, true);
- int rank = notifVisibility.rank;
+ NotificationVisibility notifVisibility = mVisibilityProvider.obtain(entry, true);
+ int rank = notifVisibility.rank;
- if (DEBUG) Log.d(TAG, "Clearing notification, key: " + notifKey + ", rank: " + rank);
- mIStatusBarService.onNotificationClear(
- packageName, userHandle.getIdentifier(), notifKey,
- NotificationStats.DISMISSAL_OTHER,
- NotificationStats.DISMISS_SENTIMENT_POSITIVE, notifVisibility);
- } catch (Exception e) {
- Log.e(TAG, "Exception cancelling notification:" + e);
- }
+ if (DEBUG) Log.d(TAG, "Clearing notification, key: " + notifKey + ", rank: " + rank);
+ mBgExecutor.execute(() -> {
+ try {
+ mIStatusBarService.onNotificationClear(
+ packageName, userHandle.getIdentifier(), notifKey,
+ NotificationStats.DISMISSAL_OTHER,
+ NotificationStats.DISMISS_SENTIMENT_POSITIVE, notifVisibility);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception cancelling notification:" + e);
+ }
+ });
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index 1a7bd8cb6cf9..18be0aa6580d 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -289,7 +289,7 @@ public class PeopleSpaceWidgetManager {
if (DEBUG) Log.d(TAG, "Updating widget: " + appWidgetId);
PeopleSpaceTile tile = getTileForExistingWidget(appWidgetId);
if (tile == null) {
- Log.e(TAG, "Matching conversation not found for shortcut ID");
+ Log.e(TAG, "Matching conversation not found for widget " + appWidgetId);
}
updateAppWidgetOptionsAndView(appWidgetId, tile);
widgetIdToTile.put(appWidgetId, tile);
@@ -308,7 +308,7 @@ public class PeopleSpaceWidgetManager {
if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + " for: " + key.toString());
if (!PeopleTileKey.isValid(key)) {
- Log.e(TAG, "Cannot update invalid widget");
+ Log.e(TAG, "Invalid tile key updating widget " + appWidgetId);
return;
}
RemoteViews views = PeopleTileViewHelper.createRemoteViews(mContext, tile, appWidgetId,
@@ -330,7 +330,7 @@ public class PeopleSpaceWidgetManager {
/** Updates tile in app widget options and the current view. */
public void updateAppWidgetOptionsAndView(int appWidgetId, PeopleSpaceTile tile) {
if (tile == null) {
- if (DEBUG) Log.w(TAG, "Storing null tile");
+ Log.w(TAG, "Storing null tile for widget " + appWidgetId);
}
synchronized (mTiles) {
mTiles.put(appWidgetId, tile);
@@ -348,7 +348,7 @@ public class PeopleSpaceWidgetManager {
try {
return getTileForExistingWidgetThrowing(appWidgetId);
} catch (Exception e) {
- Log.e(TAG, "failed to retrieve tile for widget ID " + appWidgetId, e);
+ Log.e(TAG, "failed to retrieve tile for existing widget " + appWidgetId, e);
return null;
}
}
@@ -388,7 +388,7 @@ public class PeopleSpaceWidgetManager {
boolean supplementFromStorage) throws
PackageManager.NameNotFoundException {
if (!PeopleTileKey.isValid(key)) {
- Log.e(TAG, "PeopleTileKey invalid: " + key.toString());
+ Log.e(TAG, "Invalid tile key finding tile for existing widget " + appWidgetId);
return null;
}
@@ -423,7 +423,7 @@ public class PeopleSpaceWidgetManager {
// Add current state.
return getTileWithCurrentState(storedTile.build(), ACTION_BOOT_COMPLETED);
} catch (RemoteException e) {
- Log.e(TAG, "getTileFromPersistentStorage failing", e);
+ Log.e(TAG, "getTileFromPersistentStorage failing for widget " + appWidgetId, e);
return null;
}
}
@@ -591,10 +591,7 @@ public class PeopleSpaceWidgetManager {
if (DEBUG) Log.d(TAG, "Augmenting tile for existing widget: " + widgetId);
PeopleSpaceTile tile = getTileForExistingWidget(widgetId);
if (tile == null) {
- if (DEBUG) {
- Log.w(TAG, "Widget: " + widgetId
- + ". Null tile for existing widget, skipping update.");
- }
+ Log.w(TAG, "Null tile for existing widget " + widgetId + ", skipping update.");
return Optional.empty();
}
String contactUriString = mSharedPrefs.getString(String.valueOf(widgetId), null);
@@ -816,7 +813,7 @@ public class PeopleSpaceWidgetManager {
tile = getTileFromPersistentStorage(key, appWidgetId, /* supplementFromStorage= */
false);
} catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Cannot add widget since app was uninstalled");
+ Log.e(TAG, "Cannot add widget " + appWidgetId + " since app was uninstalled");
return;
}
if (tile == null) {
@@ -851,7 +848,7 @@ public class PeopleSpaceWidgetManager {
Collections.singletonList(tile.getId()),
tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
} catch (Exception e) {
- Log.w(TAG, "failed to cache shortcut", e);
+ Log.w(TAG, "failed to cache shortcut for widget " + appWidgetId, e);
}
PeopleSpaceTile finalTile = tile;
mBgExecutor.execute(
@@ -862,7 +859,7 @@ public class PeopleSpaceWidgetManager {
public void registerConversationListenerIfNeeded(int widgetId, PeopleTileKey key) {
// Retrieve storage needed for registration.
if (!PeopleTileKey.isValid(key)) {
- if (DEBUG) Log.w(TAG, "Could not register listener for widget: " + widgetId);
+ Log.w(TAG, "Invalid tile key registering listener for widget " + widgetId);
return;
}
TileConversationListener newListener = new TileConversationListener();
@@ -911,7 +908,7 @@ public class PeopleSpaceWidgetManager {
widgetSp.getInt(USER_ID, INVALID_USER_ID),
widgetSp.getString(PACKAGE_NAME, null));
if (!PeopleTileKey.isValid(key)) {
- if (DEBUG) Log.e(TAG, "Could not delete " + widgetId);
+ Log.e(TAG, "Invalid tile key trying to remove widget " + widgetId);
return;
}
storedWidgetIdsForKey = new HashSet<>(
@@ -1083,7 +1080,8 @@ public class PeopleSpaceWidgetManager {
synchronized (mLock) {
existingTile = getTileForExistingWidgetThrowing(appWidgetId);
if (existingTile == null) {
- Log.e(TAG, "Matching conversation not found for shortcut ID");
+ Log.e(TAG, "Matching conversation not found for widget "
+ + appWidgetId);
continue;
}
updatedTile = getTileWithCurrentState(existingTile, entryPoint);
@@ -1091,7 +1089,7 @@ public class PeopleSpaceWidgetManager {
}
} catch (PackageManager.NameNotFoundException e) {
// Delete data for uninstalled widgets.
- Log.e(TAG, "package no longer found for tile", e);
+ Log.e(TAG, "Package no longer found for widget " + appWidgetId, e);
JobScheduler jobScheduler = mContext.getSystemService(JobScheduler.class);
if (jobScheduler != null
&& jobScheduler.getPendingJob(PeopleBackupFollowUpJob.JOB_ID) != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 05e8c021e3f5..90fc1d7ed49e 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -55,6 +55,7 @@ import android.view.WindowManager;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.settingslib.Utils;
@@ -62,6 +63,7 @@ import com.android.settingslib.fuelgauge.BatterySaverUtils;
import com.android.settingslib.utils.PowerUtil;
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
+import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.SysUISingleton;
@@ -93,6 +95,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private static final String TAG_TEMPERATURE = "high_temp";
private static final String TAG_AUTO_SAVER = "auto_saver";
+ private static final String INTERACTION_JANK_TAG = "start_power_saver";
+
private static final int SHOWING_NOTHING = 0;
private static final int SHOWING_WARNING = 1;
private static final int SHOWING_INVALID_CHARGER = 3;
@@ -707,7 +711,9 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
});
WeakReference<View> ref = mBatteryControllerLazy.get().getLastPowerSaverStartView();
if (ref != null && ref.get() != null && ref.get().isAggregatedVisible()) {
- mDialogLaunchAnimator.showFromView(d, ref.get());
+ mDialogLaunchAnimator.showFromView(d, ref.get(),
+ new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+ INTERACTION_JANK_TAG));
} else {
d.show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 6f05852f3e03..0288c9fce64a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -45,16 +45,18 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_FOOTER_DOT
+import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.Dumpable
import com.android.systemui.R
+import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.DeviceConfigProxy
import com.android.systemui.util.indentIfPossible
@@ -81,6 +83,7 @@ class FgsManagerController @Inject constructor(
) : IForegroundServiceObserver.Stub(), Dumpable {
companion object {
+ private const val INTERACTION_JANK_TAG = "active_background_apps"
private val LOG_TAG = FgsManagerController::class.java.simpleName
private const val DEFAULT_TASK_MANAGER_ENABLED = true
private const val DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT = false
@@ -153,31 +156,38 @@ class FgsManagerController @Inject constructor(
currentProfileIds.addAll(userTracker.userProfiles.map { it.id })
- deviceConfigProxy.addOnPropertiesChangedListener(NAMESPACE_SYSTEMUI,
- backgroundExecutor) {
+ deviceConfigProxy.addOnPropertiesChangedListener(
+ NAMESPACE_SYSTEMUI,
+ backgroundExecutor
+ ) {
isAvailable = it.getBoolean(TASK_MANAGER_ENABLED, isAvailable)
showFooterDot =
- it.getBoolean(TASK_MANAGER_SHOW_FOOTER_DOT, showFooterDot)
+ it.getBoolean(TASK_MANAGER_SHOW_FOOTER_DOT, showFooterDot)
}
- isAvailable = deviceConfigProxy.getBoolean(NAMESPACE_SYSTEMUI,
- TASK_MANAGER_ENABLED, DEFAULT_TASK_MANAGER_ENABLED)
- showFooterDot = deviceConfigProxy.getBoolean(NAMESPACE_SYSTEMUI,
- TASK_MANAGER_SHOW_FOOTER_DOT, DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT)
+ isAvailable = deviceConfigProxy.getBoolean(
+ NAMESPACE_SYSTEMUI,
+ TASK_MANAGER_ENABLED, DEFAULT_TASK_MANAGER_ENABLED
+ )
+ showFooterDot = deviceConfigProxy.getBoolean(
+ NAMESPACE_SYSTEMUI,
+ TASK_MANAGER_SHOW_FOOTER_DOT, DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT
+ )
dumpManager.registerDumpable(this)
broadcastDispatcher.registerReceiver(
- object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- if (intent.action == Intent.ACTION_SHOW_FOREGROUND_SERVICE_MANAGER) {
- showDialog(null)
- }
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == Intent.ACTION_SHOW_FOREGROUND_SERVICE_MANAGER) {
+ showDialog(null)
}
- },
- IntentFilter(Intent.ACTION_SHOW_FOREGROUND_SERVICE_MANAGER),
- executor = mainExecutor,
- flags = Context.RECEIVER_NOT_EXPORTED)
+ }
+ },
+ IntentFilter(Intent.ACTION_SHOW_FOREGROUND_SERVICE_MANAGER),
+ executor = mainExecutor,
+ flags = Context.RECEIVER_NOT_EXPORTED
+ )
initialized = true
}
@@ -193,10 +203,12 @@ class FgsManagerController @Inject constructor(
val userPackageKey = UserPackage(userId, packageName)
if (isForeground) {
runningServiceTokens.getOrPut(userPackageKey) { StartTimeAndTokens(systemClock) }
- .addToken(token)
+ .addToken(token)
} else {
if (runningServiceTokens[userPackageKey]?.also {
- it.removeToken(token) }?.isEmpty() == true) {
+ it.removeToken(token)
+ }?.isEmpty() == true
+ ) {
runningServiceTokens.remove(userPackageKey)
}
}
@@ -209,7 +221,7 @@ class FgsManagerController @Inject constructor(
@GuardedBy("lock")
val onNumberOfPackagesChangedListeners: MutableSet<OnNumberOfPackagesChangedListener> =
- mutableSetOf()
+ mutableSetOf()
@GuardedBy("lock")
val onDialogDismissedListeners: MutableSet<OnDialogDismissedListener> = mutableSetOf()
@@ -284,7 +296,7 @@ class FgsManagerController @Inject constructor(
recyclerView.adapter = appListAdapter
val topSpacing = dialogContext.resources
- .getDimensionPixelSize(R.dimen.fgs_manager_list_top_spacing)
+ .getDimensionPixelSize(R.dimen.fgs_manager_list_top_spacing)
dialog.setView(recyclerView, 0, topSpacing, 0, 0)
this.dialog = dialog
@@ -302,7 +314,15 @@ class FgsManagerController @Inject constructor(
mainExecutor.execute {
viewLaunchedFrom
- ?.let { dialogLaunchAnimator.showFromView(dialog, it) } ?: dialog.show()
+ ?.let {
+ dialogLaunchAnimator.showFromView(
+ dialog, it,
+ cuj = DialogCuj(
+ InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+ INTERACTION_JANK_TAG
+ )
+ )
+ } ?: dialog.show()
}
backgroundExecutor.execute {
@@ -329,11 +349,14 @@ class FgsManagerController @Inject constructor(
addedPackages.forEach {
val ai = packageManager.getApplicationInfoAsUser(it.packageName, 0, it.userId)
- runningApps[it] = RunningApp(it.userId, it.packageName,
- runningServiceTokens[it]!!.startTime, it.uiControl,
- packageManager.getApplicationLabel(ai),
- packageManager.getUserBadgedIcon(
- packageManager.getApplicationIcon(ai), UserHandle.of(it.userId)))
+ runningApps[it] = RunningApp(
+ it.userId, it.packageName,
+ runningServiceTokens[it]!!.startTime, it.uiControl,
+ packageManager.getApplicationLabel(ai),
+ packageManager.getUserBadgedIcon(
+ packageManager.getApplicationIcon(ai), UserHandle.of(it.userId)
+ )
+ )
logEvent(stopped = false, it.packageName, it.userId, runningApps[it]!!.timeStarted)
}
@@ -349,7 +372,7 @@ class FgsManagerController @Inject constructor(
mainExecutor.execute {
appListAdapter
- .setData(runningApps.values.toList().sortedByDescending { it.timeStarted })
+ .setData(runningApps.values.toList().sortedByDescending { it.timeStarted })
}
}
@@ -367,8 +390,10 @@ class FgsManagerController @Inject constructor(
}
backgroundExecutor.execute {
val uid = packageManager.getPackageUidAsUser(packageName, userId)
- SysUiStatsLog.write(SysUiStatsLog.TASK_MANAGER_EVENT_REPORTED, uid, event,
- timeLogged - timeStarted)
+ SysUiStatsLog.write(
+ SysUiStatsLog.TASK_MANAGER_EVENT_REPORTED, uid, event,
+ timeLogged - timeStarted
+ )
}
}
@@ -379,8 +404,10 @@ class FgsManagerController @Inject constructor(
private var data: List<RunningApp> = listOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppItemViewHolder {
- return AppItemViewHolder(LayoutInflater.from(parent.context)
- .inflate(R.layout.fgs_manager_app_item, parent, false))
+ return AppItemViewHolder(
+ LayoutInflater.from(parent.context)
+ .inflate(R.layout.fgs_manager_app_item, parent, false)
+ )
}
override fun onBindViewHolder(holder: AppItemViewHolder, position: Int) {
@@ -392,8 +419,9 @@ class FgsManagerController @Inject constructor(
iconView.setImageDrawable(runningApp.icon)
appLabelView.text = runningApp.appLabel
durationView.text = DateUtils.formatDuration(
- max(systemClock.elapsedRealtime() - runningApp.timeStarted, 60000),
- DateUtils.LENGTH_MEDIUM)
+ max(systemClock.elapsedRealtime() - runningApp.timeStarted, 60000),
+ DateUtils.LENGTH_MEDIUM
+ )
stopButton.setOnClickListener {
stopButton.setText(R.string.fgs_manager_app_item_stop_button_stopped_label)
stopPackage(runningApp.userId, runningApp.packageName, runningApp.timeStarted)
@@ -431,12 +459,12 @@ class FgsManagerController @Inject constructor(
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int):
- Boolean {
+ Boolean {
return oldData[oldItemPosition] == newData[newItemPosition]
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int):
- Boolean {
+ Boolean {
return oldData[oldItemPosition].stopped == newData[newItemPosition].stopped
}
}).dispatchUpdatesTo(this)
@@ -462,7 +490,7 @@ class FgsManagerController @Inject constructor(
fun updateUiControl() {
backgroundRestrictionExemptionReason =
- activityManager.getBackgroundRestrictionExemptionReason(uid)
+ activityManager.getBackgroundRestrictionExemptionReason(uid)
uiControl = when (backgroundRestrictionExemptionReason) {
PowerExemptionManager.REASON_SYSTEM_UID,
PowerExemptionManager.REASON_DEVICE_DEMO_MODE -> UIControl.HIDE_ENTRY
@@ -523,8 +551,10 @@ class FgsManagerController @Inject constructor(
fun dump(pw: PrintWriter) {
pw.println("StartTimeAndTokens: [")
pw.indentIfPossible {
- pw.println("startTime=$startTime (time running =" +
- " ${systemClock.elapsedRealtime() - startTime}ms)")
+ pw.println(
+ "startTime=$startTime (time running =" +
+ " ${systemClock.elapsedRealtime() - startTime}ms)"
+ )
pw.println("tokens: [")
pw.indentIfPossible {
for (token in tokens) {
@@ -572,8 +602,10 @@ class FgsManagerController @Inject constructor(
pw.indentIfPossible {
pw.println("userId=$userId")
pw.println("packageName=$packageName")
- pw.println("timeStarted=$timeStarted (time since start =" +
- " ${systemClock.elapsedRealtime() - timeStarted}ms)")
+ pw.println(
+ "timeStarted=$timeStarted (time since start =" +
+ " ${systemClock.elapsedRealtime() - timeStarted}ms)"
+ )
pw.println("uiControl=$uiControl")
pw.println("appLabel=$appLabel")
pw.println("icon=$icon")
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index ce50ddff7b0f..0697133a02f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -15,6 +15,7 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
@@ -283,6 +284,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
.inflate(R.layout.qs_paged_page, this, false);
page.setMinRows(mMinRows);
page.setMaxColumns(mMaxColumns);
+ page.setSelected(false);
return page;
}
@@ -625,6 +627,16 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
}
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ if (mAdapter != null && mAdapter.getCount() > 0) {
+ event.setItemCount(mAdapter.getCount());
+ event.setFromIndex(getCurrentPageNumber());
+ event.setToIndex(getCurrentPageNumber());
+ }
+ }
+
private static Animator setupBounceAnimator(View view, int ordinal) {
view.setAlpha(0f);
view.setScaleX(0f);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
index ba6b1dd28c97..875493d73f9e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
@@ -17,6 +17,7 @@
package com.android.systemui.qs;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FGS_MANAGER_FOOTER_VIEW;
+import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
import android.content.Context;
import android.view.View;
@@ -141,8 +142,8 @@ public class QSFgsManagerFooter implements View.OnClickListener,
public void handleRefreshState() {
mMainExecutor.execute(() -> {
- CharSequence text = mContext.getResources().getQuantityString(
- R.plurals.fgs_manager_footer_label, mNumPackages, mNumPackages);
+ CharSequence text = icuMessageFormat(mContext.getResources(),
+ R.string.fgs_manager_footer_label, mNumPackages);
mFooterText.setText(text);
mNumberView.setText(Integer.toString(mNumPackages));
mNumberView.setContentDescription(text);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index d03a2e55d628..8c5e6cc63147 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -57,7 +57,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.util.LifecycleFragment;
-import com.android.systemui.util.Utils;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -624,7 +623,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
if (mQSAnimator != null) {
mQSAnimator.setPosition(expansion);
}
- updateMediaPositions();
}
private void setAlphaAnimationProgress(float progress) {
@@ -644,10 +642,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
if (mLastQSExpansion == 1.0f) {
// Fully expanded, let's set the layout bounds as clip bounds. This is necessary because
// it's a scrollview and otherwise wouldn't be clipped. However, we set the horizontal
- // bounds so the pages go to the ends of QSContainerImpl
- ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) mQSPanelScrollView.getLayoutParams();
- mQsBounds.set(-lp.leftMargin, 0, mQSPanelScrollView.getWidth() + lp.rightMargin,
+ // bounds so the pages go to the ends of QSContainerImpl (most cases) or its parent
+ // (large screen portrait)
+ int sideMargin = getResources().getDimensionPixelSize(
+ R.dimen.qs_tiles_page_horizontal_margin) * 2;
+ mQsBounds.set(-sideMargin, 0, mQSPanelScrollView.getWidth() + sideMargin,
mQSPanelScrollView.getHeight());
}
mQSPanelScrollView.setClipBounds(mQsBounds);
@@ -660,54 +659,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
- mQSPanelScrollView.getPaddingBottom());
}
- private void updateMediaPositions() {
- if (Utils.useQsMediaPlayer(getContext())) {
- mContainer.getLocationOnScreen(mTmpLocation);
- float absoluteBottomPosition = mTmpLocation[1] + mContainer.getHeight();
- // The Media can be scrolled off screen by default, let's offset it
- float expandedMediaPosition = absoluteBottomPosition - mQSPanelScrollView.getScrollY()
- + mQSPanelScrollView.getScrollRange();
- pinToBottom(expandedMediaPosition, mQsMediaHost, true /* expanded */);
- // The expanded media host should never move above the laid out position
- pinToBottom(absoluteBottomPosition, mQqsMediaHost, false /* expanded */);
- }
- }
-
- private void pinToBottom(float absoluteBottomPosition, MediaHost mediaHost, boolean expanded) {
- View hostView = mediaHost.getHostView();
- // On keyguard we cross-fade to expanded, so no need to pin it.
- // If the collapsed qs isn't visible, we also just keep it at the laid out position.
- if (mLastQSExpansion > 0 && !isKeyguardState() && mQqsMediaHost.getVisible()) {
- float targetPosition = absoluteBottomPosition - getTotalBottomMargin(hostView)
- - hostView.getHeight();
- float currentPosition = mediaHost.getCurrentBounds().top
- - hostView.getTranslationY();
- float translationY = targetPosition - currentPosition;
- if (expanded) {
- // Never go below the laid out position. This is necessary since the qs panel can
- // change in height and we don't want to ever go below it's position
- translationY = Math.min(translationY, 0);
- } else {
- translationY = Math.max(translationY, 0);
- }
- hostView.setTranslationY(translationY);
- } else {
- hostView.setTranslationY(0);
- }
- }
-
- private float getTotalBottomMargin(View startView) {
- int result = 0;
- View child = startView;
- View parent = (View) startView.getParent();
- while (!(parent instanceof QSContainerImpl) && parent != null) {
- result += parent.getHeight() - child.getBottom();
- child = parent;
- parent = (View) parent.getParent();
- }
- return result;
- }
-
private boolean headerWillBeAnimating() {
return mState == StatusBarState.KEYGUARD && mShowCollapsedOnKeyguard
&& !isKeyguardState();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 584de6e8c28f..87fcce455ea6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -80,9 +80,11 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.util.FrameworkStatsLog;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
+import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
@@ -108,6 +110,8 @@ class QSSecurityFooter extends ViewController<View>
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final boolean DEBUG_FORCE_VISIBLE = false;
+ private static final String INTERACTION_JANK_TAG = "managed_device_info";
+
private final TextView mFooterText;
private final ImageView mPrimaryFooterIcon;
private Context mContext;
@@ -557,7 +561,8 @@ class QSSecurityFooter extends ViewController<View>
mDialog.setView(view);
if (mView.isAggregatedVisible()) {
- mDialogLaunchAnimator.showFromView(mDialog, mView);
+ mDialogLaunchAnimator.showFromView(mDialog, mView, new DialogCuj(
+ InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG));
} else {
mDialog.show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index fbdabc74aba4..a1c66b35bacc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -396,7 +396,6 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
}
void saveTilesToSettings(List<String> tileSpecs) {
- if (tileSpecs.contains("work")) Log.wtfStack(TAG, "Saving work tile");
mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs),
null /* tag */, false /* default */, mCurrentUser,
true /* overrideable by restore */);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 1488231275c3..a92c7e3c8554 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -39,6 +39,7 @@ import com.android.systemui.qs.tiles.ColorInversionTile;
import com.android.systemui.qs.tiles.DataSaverTile;
import com.android.systemui.qs.tiles.DeviceControlsTile;
import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.qs.tiles.DreamTile;
import com.android.systemui.qs.tiles.FlashlightTile;
import com.android.systemui.qs.tiles.HotspotTile;
import com.android.systemui.qs.tiles.InternetTile;
@@ -96,6 +97,7 @@ public class QSFactoryImpl implements QSFactory {
private final Provider<QuickAccessWalletTile> mQuickAccessWalletTileProvider;
private final Provider<QRCodeScannerTile> mQRCodeScannerTileProvider;
private final Provider<OneHandedModeTile> mOneHandedModeTileProvider;
+ private final Provider<DreamTile> mDreamTileProvider;
private final Lazy<QSHost> mQsHostLazy;
private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;
@@ -132,7 +134,8 @@ public class QSFactoryImpl implements QSFactory {
Provider<QuickAccessWalletTile> quickAccessWalletTileProvider,
Provider<QRCodeScannerTile> qrCodeScannerTileProvider,
Provider<OneHandedModeTile> oneHandedModeTileProvider,
- Provider<ColorCorrectionTile> colorCorrectionTileProvider) {
+ Provider<ColorCorrectionTile> colorCorrectionTileProvider,
+ Provider<DreamTile> dreamTileProvider) {
mQsHostLazy = qsHostLazy;
mCustomTileBuilderProvider = customTileBuilderProvider;
@@ -165,6 +168,7 @@ public class QSFactoryImpl implements QSFactory {
mQRCodeScannerTileProvider = qrCodeScannerTileProvider;
mOneHandedModeTileProvider = oneHandedModeTileProvider;
mColorCorrectionTileProvider = colorCorrectionTileProvider;
+ mDreamTileProvider = dreamTileProvider;
}
/** Creates a tile with a type based on {@code tileSpec} */
@@ -238,6 +242,8 @@ public class QSFactoryImpl implements QSFactory {
return mOneHandedModeTileProvider.get();
case "color_correction":
return mColorCorrectionTileProvider.get();
+ case "dream":
+ return mDreamTileProvider.get();
}
// Custom tiles
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 59164dea9c45..5147d5934039 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -670,7 +670,8 @@ internal object SubtitleArrayMapping {
"qr_code_scanner" to R.array.tile_states_qr_code_scanner,
"alarm" to R.array.tile_states_alarm,
"onehanded" to R.array.tile_states_onehanded,
- "color_correction" to R.array.tile_states_color_correction
+ "color_correction" to R.array.tile_states_color_correction,
+ "dream" to R.array.tile_states_dream
)
fun getSubtitleId(spec: String?): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index ccec80db3ea5..86d4fa3002fc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -56,7 +56,6 @@ import dagger.Lazy;
/** Quick settings tile: Airplane mode **/
public class AirplaneModeTile extends QSTileImpl<BooleanState> {
- private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_airplane);
private final SettingObserver mSetting;
private final BroadcastDispatcher mBroadcastDispatcher;
private final Lazy<ConnectivityManager> mLazyConnectivityManager;
@@ -129,11 +128,8 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> {
final boolean airplaneMode = value != 0;
state.value = airplaneMode;
state.label = mContext.getString(R.string.airplane_mode);
- state.icon = mIcon;
- if (state.slash == null) {
- state.slash = new SlashState();
- }
- state.slash.isSlashed = !airplaneMode;
+ state.icon = ResourceIcon.get(state.value
+ ? R.drawable.qs_airplane_icon_on : R.drawable.qs_airplane_icon_off);
state.state = airplaneMode ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.contentDescription = state.label;
state.expandedAccessibilityClassName = Switch.class.getName();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index 1004fcae3827..ee49b294dfcd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -57,8 +57,6 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements
private boolean mCharging;
private boolean mPluggedIn;
- private Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_battery_saver);
-
@Inject
public BatterySaverTile(
QSHost host,
@@ -145,7 +143,9 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements
protected void handleUpdateState(BooleanState state, Object arg) {
state.state = mPluggedIn ? Tile.STATE_UNAVAILABLE
: mPowerSave ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
- state.icon = mIcon;
+ state.icon = ResourceIcon.get(mPowerSave
+ ? R.drawable.qs_battery_saver_icon_on
+ : R.drawable.qs_battery_saver_icon_off);
state.label = mContext.getString(R.string.battery_detail_switch_title);
state.secondaryLabel = "";
state.contentDescription = state.label;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index f736231bc22b..9a0d0d9656ca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -16,12 +16,12 @@
package com.android.systemui.qs.tiles;
+import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
-import android.content.Context;
import android.content.Intent;
-import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.os.UserManager;
@@ -134,9 +134,10 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_bluetooth);
state.stateDescription = "";
+
if (enabled) {
if (connected) {
- state.icon = new BluetoothConnectedTileIcon();
+ state.icon = ResourceIcon.get(R.drawable.qs_bluetooth_icon_on);
if (!TextUtils.isEmpty(mController.getConnectedDeviceName())) {
state.label = mController.getConnectedDeviceName();
}
@@ -145,21 +146,19 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
+ ", " + state.secondaryLabel;
} else if (state.isTransient) {
state.icon = ResourceIcon.get(
- com.android.internal.R.drawable.ic_bluetooth_transient_animation);
+ R.drawable.qs_bluetooth_icon_search);
state.stateDescription = state.secondaryLabel;
} else {
state.icon =
- ResourceIcon.get(com.android.internal.R.drawable.ic_qs_bluetooth);
+ ResourceIcon.get(R.drawable.qs_bluetooth_icon_off);
state.stateDescription = mContext.getString(R.string.accessibility_not_connected);
}
state.state = Tile.STATE_ACTIVE;
} else {
- state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_bluetooth);
+ state.icon = ResourceIcon.get(R.drawable.qs_bluetooth_icon_off);
state.state = Tile.STATE_INACTIVE;
}
- state.dualLabelContentDescription = mContext.getResources().getString(
- R.string.accessibility_quick_settings_open_settings, getTileLabel());
state.expandedAccessibilityClassName = Switch.class.getName();
}
@@ -185,10 +184,8 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
List<CachedBluetoothDevice> connectedDevices = mController.getConnectedDevices();
if (enabled && connected && !connectedDevices.isEmpty()) {
if (connectedDevices.size() > 1) {
- // TODO(b/76102598): add a new string for "X connected devices" after P
- return mContext.getResources().getQuantityString(
- R.plurals.quick_settings_hotspot_secondary_label_num_devices,
- connectedDevices.size(),
+ return icuMessageFormat(mContext.getResources(),
+ R.string.quick_settings_hotspot_secondary_label_num_devices,
connectedDevices.size());
}
@@ -244,22 +241,4 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
refreshState();
}
};
-
- /**
- * Bluetooth icon wrapper (when connected with no battery indicator) for Quick Settings. This is
- * used instead of {@link com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon} in order to
- * use a context that reflects dark/light theme attributes.
- */
- private class BluetoothConnectedTileIcon extends Icon {
-
- BluetoothConnectedTileIcon() {
- // Do nothing. Default constructor to limit visibility.
- }
-
- @Override
- public Drawable getDrawable(Context context) {
- // This method returns Pair<Drawable, String> - the first value is the drawable.
- return context.getDrawable(R.drawable.ic_bluetooth_connected);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index fa2d4447f26d..ee41f1d1077d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -72,9 +72,9 @@ public class CameraToggleTile extends SensorPrivacyToggleTile {
@Override
public @DrawableRes int getIconRes(boolean isBlocked) {
if (isBlocked) {
- return com.android.internal.R.drawable.ic_camera_blocked;
+ return R.drawable.qs_camera_access_icon_off;
} else {
- return com.android.internal.R.drawable.ic_camera_allowed;
+ return R.drawable.qs_camera_access_icon_on;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 4afd39e36ce3..dce137f5b9f3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -33,10 +33,12 @@ import android.widget.Button;
import androidx.annotation.Nullable;
import com.android.internal.app.MediaRouteDialogPresenter;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -63,6 +65,9 @@ import javax.inject.Inject;
/** Quick settings tile: Cast **/
public class CastTile extends QSTileImpl<BooleanState> {
+
+ private static final String INTERACTION_JANK_TAG = "cast";
+
private static final Intent CAST_SETTINGS =
new Intent(Settings.ACTION_CAST_SETTINGS);
@@ -211,7 +216,9 @@ public class CastTile extends QSTileImpl<BooleanState> {
mUiHandler.post(() -> {
if (view != null) {
- mDialogLaunchAnimator.showFromView(dialog, view);
+ mDialogLaunchAnimator.showFromView(dialog, view,
+ new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+ INTERACTION_JANK_TAG));
} else {
dialog.show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 1bbe411eee25..9fdf5940c392 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -24,10 +24,12 @@ import android.widget.Switch;
import androidx.annotation.Nullable;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -46,6 +48,8 @@ import javax.inject.Inject;
public class DataSaverTile extends QSTileImpl<BooleanState> implements
DataSaverController.Listener{
+ private static final String INTERACTION_JANK_TAG = "start_data_saver";
+
private final DataSaverController mDataSaverController;
private final DialogLaunchAnimator mDialogLaunchAnimator;
@@ -102,7 +106,9 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements
dialog.setShowForAllUsers(true);
if (view != null) {
- mDialogLaunchAnimator.showFromView(dialog, view);
+ mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj(
+ InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+ INTERACTION_JANK_TAG));
} else {
dialog.show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 6cff4cd03b0d..8b7f53fa5a3f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -39,11 +39,13 @@ import android.widget.Switch;
import androidx.annotation.Nullable;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.notification.EnableZenModeDialog;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -71,6 +73,8 @@ public class DndTile extends QSTileImpl<BooleanState> {
private static final Intent ZEN_PRIORITY_SETTINGS =
new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS);
+ private static final String INTERACTION_JANK_TAG = "start_zen_mode";
+
private final ZenModeController mController;
private final SharedPreferences mSharedPreferences;
private final SettingObserver mSettingZenDuration;
@@ -175,7 +179,10 @@ public class DndTile extends QSTileImpl<BooleanState> {
mUiHandler.post(() -> {
Dialog dialog = makeZenModeDialog();
if (view != null) {
- mDialogLaunchAnimator.showFromView(dialog, view, false);
+ mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj(
+ InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+ INTERACTION_JANK_TAG),
+ /* animateBackgroundBoundsChange= */ false);
} else {
dialog.show();
}
@@ -219,16 +226,15 @@ public class DndTile extends QSTileImpl<BooleanState> {
if (mController == null) return;
final int zen = arg instanceof Integer ? (Integer) arg : mController.getZen();
final boolean newValue = zen != ZEN_MODE_OFF;
- final boolean valueChanged = state.value != newValue;
- if (state.slash == null) state.slash = new SlashState();
state.dualTarget = true;
state.value = newValue;
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
- state.slash.isSlashed = !state.value;
+ state.icon = ResourceIcon.get(state.value
+ ? R.drawable.qs_dnd_icon_on
+ : R.drawable.qs_dnd_icon_off);
state.label = getTileLabel();
state.secondaryLabel = TextUtils.emptyIfNull(ZenModeConfig.getDescription(mContext,
zen != Global.ZEN_MODE_OFF, mController.getConfig(), false));
- state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_dnd);
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
// Keeping the secondaryLabel in contentDescription instead of stateDescription is easier
// to understand.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
new file mode 100644
index 000000000000..bebd5803fabf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.qs.tiles;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.service.dreams.IDreamManager;
+import android.service.quicksettings.Tile;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.dagger.DreamModule;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.SecureSettings;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/** Quick settings tile: Screensaver (dream) **/
+public class DreamTile extends QSTileImpl<QSTile.BooleanState> {
+
+ private static final String LOG_TAG = "QSDream";
+ // TODO: consider 1 animated icon instead
+ private final Icon mIconDocked = ResourceIcon.get(R.drawable.ic_qs_screen_saver);
+ private final Icon mIconUndocked = ResourceIcon.get(R.drawable.ic_qs_screen_saver_undocked);
+ private final IDreamManager mDreamManager;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final SettingObserver mEnabledSettingObserver;
+ private final SettingObserver mDreamSettingObserver;
+ private final UserTracker mUserTracker;
+ private final boolean mDreamSupported;
+ private final boolean mDreamOnlyEnabledForSystemUser;
+
+ private boolean mIsDocked = false;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) {
+ mIsDocked = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, -1)
+ != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ }
+ refreshState();
+ }
+ };
+
+ @Inject
+ public DreamTile(
+ QSHost host,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ FalsingManager falsingManager,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger,
+ IDreamManager dreamManager,
+ SecureSettings secureSettings,
+ BroadcastDispatcher broadcastDispatcher,
+ UserTracker userTracker,
+ @Named(DreamModule.DREAM_SUPPORTED) boolean dreamSupported,
+ @Named(DreamModule.DREAM_ONLY_ENABLED_FOR_SYSTEM_USER)
+ boolean dreamOnlyEnabledForSystemUser
+ ) {
+ super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
+ mDreamManager = dreamManager;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mEnabledSettingObserver = new SettingObserver(secureSettings, mHandler,
+ Settings.Secure.SCREENSAVER_ENABLED) {
+ @Override
+ protected void handleValueChanged(int value, boolean observedChange) {
+ refreshState();
+ }
+ };
+ mDreamSettingObserver = new SettingObserver(secureSettings, mHandler,
+ Settings.Secure.SCREENSAVER_COMPONENTS) {
+ @Override
+ protected void handleValueChanged(int value, boolean observedChange) {
+ refreshState();
+ }
+ };
+ mUserTracker = userTracker;
+ mDreamSupported = dreamSupported;
+ mDreamOnlyEnabledForSystemUser = dreamOnlyEnabledForSystemUser;
+ }
+
+ @Override
+ public void handleSetListening(boolean listening) {
+ super.handleSetListening(listening);
+
+ if (listening) {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_DREAMING_STARTED);
+ filter.addAction(Intent.ACTION_DREAMING_STOPPED);
+ filter.addAction(Intent.ACTION_DOCK_EVENT);
+ mBroadcastDispatcher.registerReceiver(mReceiver, filter);
+ } else {
+ mBroadcastDispatcher.unregisterReceiver(mReceiver);
+ }
+ mEnabledSettingObserver.setListening(listening);
+ mDreamSettingObserver.setListening(listening);
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ @Override
+ protected void handleClick(@Nullable View view) {
+ try {
+ if (mDreamManager.isDreaming()) {
+ mDreamManager.awaken();
+ } else {
+ mDreamManager.dream();
+ }
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can't dream", e);
+ }
+ }
+
+ @Override
+ protected void handleLongClick(@Nullable View view) {
+ try {
+ // Need to wake on long click so bouncer->settings works.
+ mDreamManager.awaken();
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can't awaken", e);
+ }
+ super.handleLongClick(view);
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ state.label = getTileLabel();
+ state.secondaryLabel = getActiveDreamName();
+ state.contentDescription = getContentDescription(state.secondaryLabel);
+ state.icon = mIsDocked ? mIconDocked : mIconUndocked;
+
+ if (getActiveDream() == null || !isScreensaverEnabled()) {
+ state.state = Tile.STATE_UNAVAILABLE;
+ } else {
+ state.state = isDreaming() ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+ }
+ }
+
+ @Nullable
+ @Override
+ public Intent getLongClickIntent() {
+ return new Intent(Settings.ACTION_DREAM_SETTINGS);
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_screensaver_label);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ // Only enable for devices that have dreams for the user(s) that can dream.
+ // For now, restrict to debug users.
+ return Build.isDebuggable()
+ && mDreamSupported
+ && (!mDreamOnlyEnabledForSystemUser || mUserTracker.getUserHandle().isSystem());
+ }
+
+ @VisibleForTesting
+ protected CharSequence getContentDescription(CharSequence dreamName) {
+ return !TextUtils.isEmpty(dreamName)
+ ? getTileLabel() + ", " + dreamName : getTileLabel();
+ }
+
+ private boolean isDreaming() {
+ try {
+ return mDreamManager.isDreaming();
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can't check if dreaming", e);
+ return false;
+ }
+ }
+
+ private ComponentName getActiveDream() {
+ try {
+ final ComponentName[] dreams = mDreamManager.getDreamComponents();
+ return dreams != null && dreams.length > 0 ? dreams[0] : null;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to get active dream", e);
+ return null;
+ }
+ }
+
+ private CharSequence getActiveDreamName() {
+ final ComponentName componentName = getActiveDream();
+ if (componentName != null) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ ServiceInfo ri = pm.getServiceInfo(componentName, 0);
+ if (ri != null) {
+ return ri.loadLabel(pm);
+ }
+ } catch (PackageManager.NameNotFoundException exc) {
+ return null; // uninstalled?
+ }
+ }
+ return null;
+ }
+
+ private boolean isScreensaverEnabled() {
+ return mEnabledSettingObserver.getValue() == 1;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 7c8e77b5d993..b6f6e933bf84 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tiles;
+import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
+
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
@@ -186,9 +188,8 @@ public class HotspotTile extends QSTileImpl<BooleanState> {
return mContext.getString(
R.string.quick_settings_hotspot_secondary_label_data_saver_enabled);
} else if (numConnectedDevices > 0 && isActive) {
- return mContext.getResources().getQuantityString(
- R.plurals.quick_settings_hotspot_secondary_label_num_devices,
- numConnectedDevices,
+ return icuMessageFormat(mContext.getResources(),
+ R.string.quick_settings_hotspot_secondary_label_num_devices,
numConnectedDevices);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index fc93f44a44aa..9466a694ea1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -48,8 +48,6 @@ import javax.inject.Inject;
/** Quick settings tile: Location **/
public class LocationTile extends QSTileImpl<BooleanState> {
- private final Icon mIcon = ResourceIcon.get(R.drawable.ic_location);
-
private final LocationController mController;
private final KeyguardStateController mKeyguard;
private final Callback mCallback = new Callback();
@@ -119,8 +117,8 @@ public class LocationTile extends QSTileImpl<BooleanState> {
if (state.disabledByPolicy == false) {
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_LOCATION);
}
- state.icon = mIcon;
- state.slash.isSlashed = !state.value;
+ state.icon = ResourceIcon.get(state.value
+ ? R.drawable.qs_location_icon_on : R.drawable.qs_location_icon_off);
state.label = mContext.getString(R.string.quick_settings_location_label);
state.contentDescription = state.label;
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index f4f0b2cdc432..e54709562c10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -72,9 +72,9 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile {
@Override
public @DrawableRes int getIconRes(boolean isBlocked) {
if (isBlocked) {
- return com.android.internal.R.drawable.ic_mic_blocked;
+ return R.drawable.qs_mic_access_off;
} else {
- return com.android.internal.R.drawable.ic_mic_allowed;
+ return R.drawable.qs_mic_access_on;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 177c82ed3d78..600874f0d01a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -57,6 +57,7 @@ import javax.inject.Inject;
/** Quick settings tile: Rotation **/
public class RotationLockTile extends QSTileImpl<BooleanState> implements
BatteryController.BatteryStateChangeCallback {
+ private static final String EMPTY_SECONDARY_STRING = "";
private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_auto_rotate);
private final RotationLockController mController;
@@ -144,13 +145,15 @@ public class RotationLockTile extends QSTileImpl<BooleanState> implements
&& mController.isCameraRotationEnabled();
state.value = !rotationLocked;
state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label);
- state.icon = mIcon;
+ state.icon = ResourceIcon.get(R.drawable.qs_auto_rotate_icon_off);
state.contentDescription = getAccessibilityString(rotationLocked);
- if (!rotationLocked && cameraRotation) {
- state.secondaryLabel = mContext.getResources().getString(
- R.string.rotation_lock_camera_rotation_on);
+ if (!rotationLocked) {
+ state.secondaryLabel = cameraRotation ? mContext.getResources().getString(
+ R.string.rotation_lock_camera_rotation_on)
+ : EMPTY_SECONDARY_STRING;
+ state.icon = ResourceIcon.get(R.drawable.qs_auto_rotate_icon_on);
} else {
- state.secondaryLabel = "";
+ state.secondaryLabel = EMPTY_SECONDARY_STRING;
}
state.stateDescription = state.secondaryLabel;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 45e43ee20d67..02d30c5224a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -27,8 +27,10 @@ import android.widget.Switch;
import androidx.annotation.Nullable;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
+import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -52,6 +54,8 @@ import javax.inject.Inject;
public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
implements RecordingController.RecordingStateChangeCallback {
private static final String TAG = "ScreenRecordTile";
+ private static final String INTERACTION_JANK_TAG = "screen_record";
+
private final RecordingController mController;
private final KeyguardDismissUtil mKeyguardDismissUtil;
private final KeyguardStateController mKeyguardStateController;
@@ -165,7 +169,8 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
ActivityStarter.OnDismissAction dismissAction = () -> {
if (shouldAnimateFromView) {
- mDialogLaunchAnimator.showFromView(dialog, view);
+ mDialogLaunchAnimator.showFromView(dialog, view, new DialogCuj(
+ InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG));
} else {
dialog.show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
index 4386169c8887..8566ca308738 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
@@ -19,7 +19,9 @@ import android.content.Context
import android.os.Handler
import android.util.Log
import android.view.View
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -45,6 +47,7 @@ class InternetDialogFactory @Inject constructor(
private val keyguardStateController: KeyguardStateController
) {
companion object {
+ private const val INTERACTION_JANK_TAG = "internet"
var internetDialog: InternetDialog? = null
}
@@ -61,12 +64,20 @@ class InternetDialogFactory @Inject constructor(
}
return
} else {
- internetDialog = InternetDialog(context, this, internetDialogController,
- canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler,
- executor, keyguardStateController)
+ internetDialog = InternetDialog(
+ context, this, internetDialogController,
+ canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler,
+ executor, keyguardStateController
+ )
if (view != null) {
- dialogLaunchAnimator.showFromView(internetDialog!!, view,
- animateBackgroundBoundsChange = true)
+ dialogLaunchAnimator.showFromView(
+ internetDialog!!, view,
+ animateBackgroundBoundsChange = true,
+ cuj = DialogCuj(
+ InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+ INTERACTION_JANK_TAG
+ )
+ )
} else {
internetDialog?.show()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index 88aa734df2b3..bdcc6b0b2a57 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -25,8 +25,10 @@ import android.provider.Settings
import android.view.LayoutInflater
import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
+import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter
@@ -67,6 +69,7 @@ class UserSwitchDialogController @VisibleForTesting constructor(
)
companion object {
+ private const val INTERACTION_JANK_TAG = "switch_user"
private val USER_SETTINGS_INTENT = Intent(Settings.ACTION_USER_SETTINGS)
}
@@ -89,14 +92,16 @@ class UserSwitchDialogController @VisibleForTesting constructor(
if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
uiEventLogger.log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
val controller = dialogLaunchAnimator.createActivityLaunchController(
- getButton(BUTTON_NEUTRAL))
+ getButton(BUTTON_NEUTRAL)
+ )
if (controller == null) {
dismiss()
}
activityStarter.postStartActivityDismissingKeyguard(
- USER_SETTINGS_INTENT, 0, controller)
+ USER_SETTINGS_INTENT, 0, controller
+ )
}
}, false /* dismissOnClick */)
val gridFrame = LayoutInflater.from(this.context)
@@ -107,7 +112,13 @@ class UserSwitchDialogController @VisibleForTesting constructor(
adapter.linkToViewGroup(gridFrame.findViewById(R.id.grid))
- dialogLaunchAnimator.showFromView(this, view)
+ dialogLaunchAnimator.showFromView(
+ this, view,
+ cuj = DialogCuj(
+ InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+ INTERACTION_JANK_TAG
+ )
+ )
uiEventLogger.log(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN)
adapter.injectDialogShower(DialogShowerImpl(this, dialogLaunchAnimator))
}
@@ -117,15 +128,16 @@ class UserSwitchDialogController @VisibleForTesting constructor(
private val animateFrom: Dialog,
private val dialogLaunchAnimator: DialogLaunchAnimator
) : DialogInterface by animateFrom, DialogShower {
- override fun showDialog(dialog: Dialog) {
+ override fun showDialog(dialog: Dialog, cuj: DialogCuj) {
dialogLaunchAnimator.showFromDialog(
dialog,
- animateFrom = animateFrom
+ animateFrom = animateFrom,
+ cuj
)
}
}
interface DialogShower : DialogInterface {
- fun showDialog(dialog: Dialog)
+ fun showDialog(dialog: Dialog, cuj: DialogCuj)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9768e706764f..438236d6a63d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -51,7 +51,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
@@ -97,6 +96,7 @@ import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.navigationbar.buttons.KeyButtonView;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
@@ -105,7 +105,6 @@ import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.wm.shell.back.BackAnimation;
@@ -297,12 +296,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
@Override
- public Rect getNonMinimizedSplitScreenSecondaryBounds() {
- // Deprecated
- return null;
- }
-
- @Override
public void setNavBarButtonAlpha(float alpha, boolean animate) {
verifyCallerAndClearCallingIdentityPostMain("setNavBarButtonAlpha", () ->
notifyNavBarButtonAlphaChanged(alpha, animate));
@@ -349,17 +342,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
@Override
- public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen,
- Insets visibleInsets, int taskId) {
- // Deprecated
- }
-
- @Override
- public void setSplitScreenMinimized(boolean minimized) {
- // Deprecated
- }
-
- @Override
public void notifySwipeToHomeFinished() {
verifyCallerAndClearCallingIdentity("notifySwipeToHomeFinished", () ->
mPipOptional.ifPresent(
@@ -736,7 +718,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
- boolean bouncerShowing, boolean isDozing) {
+ boolean bouncerShowing, boolean isDozing, boolean panelExpanded) {
mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
keyguardShowing && !keyguardOccluded)
.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
index bdad36c58480..93a2efc4e96d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.charging
+package com.android.systemui.ripple
import android.graphics.PointF
import android.graphics.RuntimeShader
@@ -148,7 +148,7 @@ class RippleShader internal constructor() : RuntimeShader(SHADER) {
val fadeOutNoise = subProgress(0.4f, 1f, value)
var fadeOutRipple = 0f
var fadeCircle = 0f
- if (shouldFadeOutRipple) {
+ if (!rippleFill) {
fadeCircle = subProgress(0f, 0.2f, value)
fadeOutRipple = subProgress(0.3f, 1f, value)
}
@@ -202,5 +202,9 @@ class RippleShader internal constructor() : RuntimeShader(SHADER) {
setFloatUniform("in_pixelDensity", value)
}
- var shouldFadeOutRipple: Boolean = true
+ /**
+ * True if the ripple should stayed filled in as it expands to give a filled-in circle effect.
+ * False for a ring effect.
+ */
+ var rippleFill: Boolean = false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
index 10e90fee1469..fc52464ecf85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.charging
+package com.android.systemui.ripple
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
@@ -30,9 +30,10 @@ import android.view.View
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
/**
- * Expanding ripple effect that shows when charging begins.
+ * A generic expanding ripple effect. To trigger the ripple expansion, set [radius] and [origin],
+ * then call [startRipple].
*/
-class ChargingRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
+open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
private val rippleShader = RippleShader()
private val defaultColor: Int = 0xffffffff.toInt()
private val ripplePaint = Paint()
@@ -74,9 +75,9 @@ class ChargingRippleView(context: Context?, attrs: AttributeSet?) : View(context
}
val animator = ValueAnimator.ofFloat(0f, 1f)
animator.duration = duration
- animator.addUpdateListener { animator ->
- val now = animator.currentPlayTime
- val progress = animator.animatedValue as Float
+ animator.addUpdateListener { updateListener ->
+ val now = updateListener.currentPlayTime
+ val progress = updateListener.animatedValue as Float
rippleShader.progress = progress
rippleShader.distortionStrength = 1 - progress
rippleShader.time = now.toFloat()
@@ -92,10 +93,20 @@ class ChargingRippleView(context: Context?, attrs: AttributeSet?) : View(context
rippleInProgress = true
}
+ /** Set the color to be used for the ripple. */
fun setColor(color: Int) {
rippleShader.color = color
}
+ /**
+ * Set whether the ripple should remain filled as the ripple expands.
+ *
+ * See [RippleShader.rippleFill].
+ */
+ fun setRippleFill(rippleFill: Boolean) {
+ rippleShader.rippleFill = rippleFill
+ }
+
override fun onDraw(canvas: Canvas?) {
if (canvas == null || !canvas.isHardwareAccelerated) {
// Drawing with the ripple shader requires hardware acceleration, so skip
@@ -107,6 +118,6 @@ class ChargingRippleView(context: Context?, attrs: AttributeSet?) : View(context
// animation implementation in the ripple shader.
val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
(1 - rippleShader.progress)) * radius * 2
- canvas?.drawCircle(origin.x, origin.y, maskRadius, ripplePaint)
+ canvas.drawCircle(origin.x, origin.y, maskRadius, ripplePaint)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 5bb3413595ba..a837cbb8a50e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -29,6 +29,7 @@ import android.graphics.drawable.Icon;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -40,6 +41,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.LongRunning;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.screenrecord.ScreenMediaRecorder.ScreenMediaRecorderListener;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
@@ -51,9 +54,10 @@ import javax.inject.Inject;
/**
* A service which records the device screen and optionally microphone input.
*/
-public class RecordingService extends Service implements MediaRecorder.OnInfoListener {
+public class RecordingService extends Service implements ScreenMediaRecorderListener {
public static final int REQUEST_CODE = 2;
+ private static final int USER_ID_NOT_SPECIFIED = -1;
private static final int NOTIFICATION_RECORDING_ID = 4274;
private static final int NOTIFICATION_PROCESSING_ID = 4275;
private static final int NOTIFICATION_VIEW_ID = 4273;
@@ -73,6 +77,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
private final RecordingController mController;
private final KeyguardDismissUtil mKeyguardDismissUtil;
+ private final Handler mMainHandler;
private ScreenRecordingAudioSource mAudioSource;
private boolean mShowTaps;
private boolean mOriginalShowTaps;
@@ -84,10 +89,12 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
@Inject
public RecordingService(RecordingController controller, @LongRunning Executor executor,
- UiEventLogger uiEventLogger, NotificationManager notificationManager,
+ @Main Handler handler, UiEventLogger uiEventLogger,
+ NotificationManager notificationManager,
UserContextProvider userContextTracker, KeyguardDismissUtil keyguardDismissUtil) {
mController = controller;
mLongExecutor = executor;
+ mMainHandler = handler;
mUiEventLogger = uiEventLogger;
mNotificationManager = notificationManager;
mUserContextTracker = userContextTracker;
@@ -138,6 +145,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
mRecorder = new ScreenMediaRecorder(
mUserContextTracker.getUserContext(),
+ mMainHandler,
currentUserId,
mAudioSource,
this
@@ -166,14 +174,8 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
}
// Check user ID - we may be getting a stop intent after user switch, in which case
// we want to post the notifications for that user, which is NOT current user
- int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (userId == -1) {
- userId = mUserContextTracker.getUserContext().getUserId();
- }
- Log.d(TAG, "notifying for user " + userId);
- stopRecording(userId);
- mNotificationManager.cancel(NOTIFICATION_RECORDING_ID);
- stopSelf();
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_ID_NOT_SPECIFIED);
+ stopService(userId);
break;
case ACTION_SHARE:
@@ -378,15 +380,39 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
return builder.build();
}
- private void stopRecording(int userId) {
+ private void stopService() {
+ stopService(USER_ID_NOT_SPECIFIED);
+ }
+
+ private void stopService(int userId) {
+ if (userId == USER_ID_NOT_SPECIFIED) {
+ userId = mUserContextTracker.getUserContext().getUserId();
+ }
+ Log.d(TAG, "notifying for user " + userId);
setTapsVisible(mOriginalShowTaps);
if (getRecorder() != null) {
- getRecorder().end();
- saveRecording(userId);
+ try {
+ getRecorder().end();
+ saveRecording(userId);
+ } catch (RuntimeException exception) {
+ // RuntimeException could happen if the recording stopped immediately after starting
+ // let's release the recorder and delete all temporary files in this case
+ getRecorder().release();
+ showErrorToast(R.string.screenrecord_start_error);
+ Log.e(TAG, "stopRecording called, but there was an error when ending"
+ + "recording");
+ exception.printStackTrace();
+ } catch (Throwable throwable) {
+ // Something unexpected happen, SystemUI will crash but let's delete
+ // the temporary files anyway
+ getRecorder().release();
+ throw new RuntimeException(throwable);
+ }
} else {
Log.e(TAG, "stopRecording called, but recorder was null");
}
updateState(false);
+ stopSelf();
}
private void saveRecording(int userId) {
@@ -446,4 +472,12 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
Log.d(TAG, "Media recorder info: " + what);
onStartCommand(getStopIntent(this), 0, 0);
}
+
+ @Override
+ public void onStopped() {
+ if (mController.isRecording()) {
+ Log.d(TAG, "Stopping recording because the system requested the stop");
+ stopService();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 2133cf63d1c3..d098b4b3442a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -40,6 +40,7 @@ import android.media.projection.IMediaProjectionManager;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.net.Uri;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -51,16 +52,19 @@ import android.view.Surface;
import android.view.WindowManager;
import java.io.File;
+import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
/**
* Recording screen and mic/internal audio
*/
-public class ScreenMediaRecorder {
+public class ScreenMediaRecorder extends MediaProjection.Callback {
private static final int TOTAL_NUM_TRACKS = 1;
private static final int VIDEO_FRAME_RATE = 30;
private static final int VIDEO_FRAME_RATE_TO_RESOLUTION_RATIO = 6;
@@ -81,14 +85,16 @@ public class ScreenMediaRecorder {
private ScreenRecordingMuxer mMuxer;
private ScreenInternalAudioRecorder mAudio;
private ScreenRecordingAudioSource mAudioSource;
+ private final Handler mHandler;
private Context mContext;
- MediaRecorder.OnInfoListener mListener;
+ ScreenMediaRecorderListener mListener;
- public ScreenMediaRecorder(Context context,
+ public ScreenMediaRecorder(Context context, Handler handler,
int user, ScreenRecordingAudioSource audioSource,
- MediaRecorder.OnInfoListener listener) {
+ ScreenMediaRecorderListener listener) {
mContext = context;
+ mHandler = handler;
mUser = user;
mListener = listener;
mAudioSource = audioSource;
@@ -105,6 +111,7 @@ public class ScreenMediaRecorder {
IBinder projection = proj.asBinder();
mMediaProjection = new MediaProjection(mContext,
IMediaProjection.Stub.asInterface(projection));
+ mMediaProjection.registerCallback(this, mHandler);
File cacheDir = mContext.getCacheDir();
cacheDir.mkdirs();
@@ -162,10 +169,15 @@ public class ScreenMediaRecorder {
metrics.densityDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mInputSurface,
- null,
- null);
-
- mMediaRecorder.setOnInfoListener(mListener);
+ new VirtualDisplay.Callback() {
+ @Override
+ public void onStopped() {
+ onStop();
+ }
+ },
+ mHandler);
+
+ mMediaRecorder.setOnInfoListener((mr, what, extra) -> mListener.onInfo(mr, what, extra));
if (mAudioSource == INTERNAL ||
mAudioSource == MIC_AND_INTERNAL) {
mTempAudioFile = File.createTempFile("temp", ".aac",
@@ -259,21 +271,34 @@ public class ScreenMediaRecorder {
}
/**
- * End screen recording
+ * End screen recording, throws an exception if stopping recording failed
*/
- void end() {
- mMediaRecorder.stop();
- mMediaRecorder.release();
- mInputSurface.release();
- mVirtualDisplay.release();
- mMediaProjection.stop();
+ void end() throws IOException {
+ Closer closer = new Closer();
+
+ // MediaRecorder might throw RuntimeException if stopped immediately after starting
+ // We should remove the recording in this case as it will be invalid
+ closer.register(mMediaRecorder::stop);
+ closer.register(mMediaRecorder::release);
+ closer.register(mInputSurface::release);
+ closer.register(mVirtualDisplay::release);
+ closer.register(mMediaProjection::stop);
+ closer.register(this::stopInternalAudioRecording);
+
+ closer.close();
+
mMediaRecorder = null;
mMediaProjection = null;
- stopInternalAudioRecording();
Log.d(TAG, "end recording");
}
+ @Override
+ public void onStop() {
+ Log.d(TAG, "The system notified about stopping the projection");
+ mListener.onStopped();
+ }
+
private void stopInternalAudioRecording() {
if (mAudioSource == INTERNAL || mAudioSource == MIC_AND_INTERNAL) {
mAudio.end();
@@ -337,6 +362,18 @@ public class ScreenMediaRecorder {
}
/**
+ * Release the resources without saving the data
+ */
+ protected void release() {
+ if (mTempVideoFile != null) {
+ mTempVideoFile.delete();
+ }
+ if (mTempAudioFile != null) {
+ mTempAudioFile.delete();
+ }
+ }
+
+ /**
* Object representing the recording
*/
public class SavedRecording {
@@ -362,4 +399,66 @@ public class ScreenMediaRecorder {
return mThumbnailBitmap;
}
}
+
+ interface ScreenMediaRecorderListener {
+ /**
+ * Called to indicate an info or a warning during recording.
+ * See {@link MediaRecorder.OnInfoListener} for the full description.
+ */
+ void onInfo(MediaRecorder mr, int what, int extra);
+
+ /**
+ * Called when the recording stopped by the system.
+ * For example, this might happen when doing partial screen sharing of an app
+ * and the app that is being captured is closed.
+ */
+ void onStopped();
+ }
+
+ /**
+ * Allows to register multiple {@link Closeable} objects and close them all by calling
+ * {@link Closer#close}. If there is an exception thrown during closing of one
+ * of the registered closeables it will continue trying closing the rest closeables.
+ * If there are one or more exceptions thrown they will be re-thrown at the end.
+ * In case of multiple exceptions only the first one will be thrown and all the rest
+ * will be printed.
+ */
+ private static class Closer implements Closeable {
+ private final List<Closeable> mCloseables = new ArrayList<>();
+
+ void register(Closeable closeable) {
+ mCloseables.add(closeable);
+ }
+
+ @Override
+ public void close() throws IOException {
+ Throwable throwable = null;
+
+ for (int i = 0; i < mCloseables.size(); i++) {
+ Closeable closeable = mCloseables.get(i);
+
+ try {
+ closeable.close();
+ } catch (Throwable e) {
+ if (throwable == null) {
+ throwable = e;
+ } else {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ if (throwable != null) {
+ if (throwable instanceof IOException) {
+ throw (IOException) throwable;
+ }
+
+ if (throwable instanceof RuntimeException) {
+ throw (RuntimeException) throwable;
+ }
+
+ throw (Error) throwable;
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
index daaa897374cb..814b8e90e0dd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
@@ -102,7 +102,7 @@ public class ActionProxyReceiver extends BroadcastReceiver {
? ACTION_TYPE_EDIT
: ACTION_TYPE_SHARE;
mScreenshotSmartActions.notifyScreenshotAction(
- context, intent.getStringExtra(EXTRA_ID), actionType, false, null);
+ intent.getStringExtra(EXTRA_ID), actionType, false, null);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
index 8d44205bef30..e0346f2e2a98 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
@@ -62,7 +62,7 @@ public class DeleteScreenshotReceiver extends BroadcastReceiver {
});
if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
mScreenshotSmartActions.notifyScreenshotAction(
- context, intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false, null);
+ intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false, null);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
new file mode 100644
index 000000000000..6224e1bf2414
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.screenshot;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ *
+ */
+@Module
+public interface ReferenceScreenshotModule {
+ /** */
+ @Provides
+ static ScreenshotNotificationSmartActionsProvider providesScrnshtNotifSmartActionsProvider() {
+ return new ScreenshotNotificationSmartActionsProvider();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 50ee1f7ba97a..f248d6913878 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -38,7 +38,6 @@ import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -49,7 +48,6 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
import com.google.common.util.concurrent.ListenableFuture;
@@ -89,7 +87,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
SaveImageInBackgroundTask(Context context, ImageExporter exporter,
ScreenshotSmartActions screenshotSmartActions,
ScreenshotController.SaveImageInBackgroundData data,
- Supplier<ActionTransition> sharedElementTransition) {
+ Supplier<ActionTransition> sharedElementTransition,
+ ScreenshotNotificationSmartActionsProvider
+ screenshotNotificationSmartActionsProvider
+ ) {
mContext = context;
mScreenshotSmartActions = screenshotSmartActions;
mImageData = new ScreenshotController.SavedImageData();
@@ -103,15 +104,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
// Initialize screenshot notification smart actions provider.
mSmartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true);
- if (mSmartActionsEnabled) {
- mSmartActionsProvider =
- SystemUIFactory.getInstance()
- .createScreenshotNotificationSmartActionsProvider(
- context, THREAD_POOL_EXECUTOR, new Handler());
- } else {
- // If smart actions is not enabled use empty implementation.
- mSmartActionsProvider = new ScreenshotNotificationSmartActionsProvider();
- }
+ mSmartActionsProvider = screenshotNotificationSmartActionsProvider;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index c213f192291a..89a15f65e98f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -263,6 +263,8 @@ public class ScreenshotController {
private final ScrollCaptureController mScrollCaptureController;
private final LongScreenshotData mLongScreenshotHolder;
private final boolean mIsLowRamDevice;
+ private final ScreenshotNotificationSmartActionsProvider
+ mScreenshotNotificationSmartActionsProvider;
private final TimeoutHandler mScreenshotHandler;
private ScreenshotView mScreenshotView;
@@ -298,7 +300,9 @@ public class ScreenshotController {
LongScreenshotData longScreenshotHolder,
ActivityManager activityManager,
TimeoutHandler timeoutHandler,
- BroadcastSender broadcastSender) {
+ BroadcastSender broadcastSender,
+ ScreenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider
+ ) {
mScreenshotSmartActions = screenshotSmartActions;
mNotificationsController = screenshotNotificationsController;
mScrollCaptureClient = scrollCaptureClient;
@@ -308,6 +312,7 @@ public class ScreenshotController {
mScrollCaptureController = scrollCaptureController;
mLongScreenshotHolder = longScreenshotHolder;
mIsLowRamDevice = activityManager.isLowRamDevice();
+ mScreenshotNotificationSmartActionsProvider = screenshotNotificationSmartActionsProvider;
mBgExecutor = Executors.newSingleThreadExecutor();
mBroadcastSender = broadcastSender;
@@ -956,7 +961,8 @@ public class ScreenshotController {
}
mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mImageExporter,
- mScreenshotSmartActions, data, getActionTransitionSupplier());
+ mScreenshotSmartActions, data, getActionTransitionSupplier(),
+ mScreenshotNotificationSmartActionsProvider);
mSaveInBgTask.execute();
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
index 0527818135dd..68b46d2b7525 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
@@ -16,8 +16,6 @@
package com.android.systemui.screenshot;
-import static android.os.AsyncTask.THREAD_POOL_EXECUTOR;
-
import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS;
import static com.android.systemui.screenshot.LogConfig.logTag;
import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType;
@@ -25,17 +23,14 @@ import static com.android.systemui.screenshot.ScreenshotNotificationSmartActions
import android.app.ActivityManager;
import android.app.Notification;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
-import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -46,6 +41,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.inject.Inject;
+import javax.inject.Provider;
/**
* Collects the static functions for retrieving and acting on smart actions.
@@ -53,9 +49,17 @@ import javax.inject.Inject;
@SysUISingleton
public class ScreenshotSmartActions {
private static final String TAG = logTag(ScreenshotSmartActions.class);
+ private final Provider<ScreenshotNotificationSmartActionsProvider>
+ mScreenshotNotificationSmartActionsProviderProvider;
@Inject
- public ScreenshotSmartActions() {}
+ public ScreenshotSmartActions(
+ Provider<ScreenshotNotificationSmartActionsProvider>
+ screenshotNotificationSmartActionsProviderProvider
+ ) {
+ mScreenshotNotificationSmartActionsProviderProvider =
+ screenshotNotificationSmartActionsProviderProvider;
+ }
@VisibleForTesting
CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
@@ -165,12 +169,11 @@ public class ScreenshotSmartActions {
}
}
- void notifyScreenshotAction(Context context, String screenshotId, String action,
+ void notifyScreenshotAction(String screenshotId, String action,
boolean isSmartAction, Intent intent) {
try {
ScreenshotNotificationSmartActionsProvider provider =
- SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
- context, THREAD_POOL_EXECUTOR, new Handler());
+ mScreenshotNotificationSmartActionsProviderProvider.get();
if (DEBUG_ACTIONS) {
Log.d(TAG, String.format("%s notifyAction: %s id=%s, isSmartAction=%b",
provider.getClass(), action, screenshotId, isSmartAction));
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
index f703058f4a0f..45af1874e9db 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java
@@ -60,7 +60,7 @@ public class SmartActionsReceiver extends BroadcastReceiver {
}
mScreenshotSmartActions.notifyScreenshotAction(
- context, intent.getStringExtra(EXTRA_ID), actionType, true,
+ intent.getStringExtra(EXTRA_ID), actionType, true,
pendingIntent.getIntent());
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java b/packages/SystemUI/src/com/android/systemui/settings/UserFileManager.kt
index 73fd6931066d..aa218dbfdd43 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManager.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,26 +14,26 @@
* limitations under the License.
*/
-package com.android.wm.shell;
+package com.android.systemui.settings
-import com.android.wm.shell.common.annotations.ExternalThread;
-
-import java.io.PrintWriter;
+import android.content.Context
+import android.content.SharedPreferences
+import java.io.File
/**
- * An entry point into the shell for dumping shell internal state and running adb commands.
- *
- * Use with {@code adb shell dumpsys activity service SystemUIService WMShell ...}.
+ * Interface for retrieving file paths for file storage of system and secondary users.
*/
-@ExternalThread
-public interface ShellCommandHandler {
+interface UserFileManager {
/**
- * Dumps the shell state.
+ * Return the file based on current user.
*/
- void dump(PrintWriter pw);
-
+ fun getFile(fileName: String, userId: Int): File
/**
- * Handles a shell command.
+ * Get shared preferences from user.
*/
- boolean handleCommand(final String[] args, PrintWriter pw);
+ fun getSharedPreferences(
+ fileName: String,
+ @Context.PreferencesMode mode: Int,
+ userId: Int
+ ): SharedPreferences
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
new file mode 100644
index 000000000000..8c8f54fe9c3d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.settings
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.SharedPreferences
+import android.os.Environment
+import android.os.UserHandle
+import android.os.UserManager
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.CoreStartable
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.concurrency.DelayableExecutor
+import java.io.File
+import javax.inject.Inject
+
+/**
+ * Implementation for retrieving file paths for file storage of system and secondary users.
+ * Files lie in {File Directory}/UserFileManager/{User Id} for secondary user.
+ * For system user, we use the conventional {File Directory}
+ */
+@SysUISingleton
+class UserFileManagerImpl @Inject constructor(
+ // Context of system process and system user.
+ val context: Context,
+ val userManager: UserManager,
+ val broadcastDispatcher: BroadcastDispatcher,
+ @Background val backgroundExecutor: DelayableExecutor
+) : UserFileManager, CoreStartable(context) {
+ companion object {
+ private const val FILES = "files"
+ private const val SHARED_PREFS = "shared_prefs"
+ internal const val ID = "UserFileManager"
+ }
+
+ private val broadcastReceiver = object : BroadcastReceiver() {
+ /**
+ * Listen to Intent.ACTION_USER_REMOVED to clear user data.
+ */
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == Intent.ACTION_USER_REMOVED) {
+ clearDeletedUserData()
+ }
+ }
+ }
+
+ /**
+ * Poll for user-specific directories to delete upon start up.
+ */
+ override fun start() {
+ clearDeletedUserData()
+ val filter = IntentFilter().apply {
+ addAction(Intent.ACTION_USER_REMOVED)
+ }
+ broadcastDispatcher.registerReceiver(broadcastReceiver, filter, backgroundExecutor)
+ }
+
+ /**
+ * Return the file based on current user.
+ */
+ override fun getFile(fileName: String, userId: Int): File {
+ return if (UserHandle(userId).isSystem) {
+ Environment.buildPath(
+ context.filesDir,
+ fileName
+ )
+ } else {
+ Environment.buildPath(
+ context.filesDir,
+ ID,
+ userId.toString(),
+ FILES,
+ fileName
+ )
+ }
+ }
+
+ /**
+ * Get shared preferences from user.
+ */
+ override fun getSharedPreferences(
+ fileName: String,
+ @Context.PreferencesMode mode: Int,
+ userId: Int
+ ): SharedPreferences {
+ if (UserHandle(userId).isSystem) {
+ return context.getSharedPreferences(fileName, mode)
+ }
+ val secondaryUserDir = Environment.buildPath(
+ context.filesDir,
+ ID,
+ userId.toString(),
+ SHARED_PREFS,
+ fileName
+ )
+
+ return context.getSharedPreferences(secondaryUserDir, mode)
+ }
+
+ /**
+ * Remove dirs for deleted users.
+ */
+ @VisibleForTesting
+ internal fun clearDeletedUserData() {
+ backgroundExecutor.execute {
+ val file = Environment.buildPath(context.filesDir, ID)
+ if (!file.exists()) return@execute
+ val aliveUsers = userManager.aliveUsers.map { it.id.toString() }
+ val dirsToDelete = file.list().filter { !aliveUsers.contains(it) }
+
+ dirsToDelete.forEach { dir ->
+ try {
+ val dirToDelete = Environment.buildPath(
+ file,
+ dir,
+ )
+ dirToDelete.deleteRecursively()
+ } catch (e: Exception) {
+ Log.e(ID, "Deletion failed.", e)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
index 7084d3ffc9ff..2f62e44ba4c4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java
@@ -21,25 +21,28 @@ import android.content.Context;
import android.os.Handler;
import android.os.UserManager;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserContentResolverProvider;
import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserFileManager;
+import com.android.systemui.settings.UserFileManagerImpl;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.UserTrackerImpl;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
/**
* Dagger Module for classes found within the com.android.systemui.settings package.
*/
@Module
-public abstract class SettingsModule {
-
-
+public abstract class MultiUserUtilsModule {
@Binds
@SysUISingleton
abstract UserContextProvider bindUserContextProvider(UserTracker tracker);
@@ -62,4 +65,12 @@ public abstract class SettingsModule {
tracker.initialize(startingUser);
return tracker;
}
+
+ @Binds
+ @IntoMap
+ @ClassKey(UserFileManagerImpl.class)
+ abstract CoreStartable bindUserFileManagerCoreStartable(UserFileManagerImpl sysui);
+
+ @Binds
+ abstract UserFileManager bindUserFileManager(UserFileManagerImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManager.kt b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManager.kt
new file mode 100644
index 000000000000..e360ec20bd9b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManager.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.shade
+
+import androidx.constraintlayout.widget.ConstraintSet
+
+typealias ConstraintChange = ConstraintSet.() -> Unit
+
+operator fun ConstraintChange?.plus(other: ConstraintChange?): ConstraintChange? {
+ // Prevent wrapping
+ if (this == null) return other
+ if (other == null) return this
+ else return {
+ this@plus()
+ other()
+ }
+}
+
+/**
+ * Contains all changes that need to be performed to the different [ConstraintSet] in
+ * [LargeScreenShadeHeaderController].
+ */
+data class ConstraintsChanges(
+ val qqsConstraintsChanges: ConstraintChange? = null,
+ val qsConstraintsChanges: ConstraintChange? = null,
+ val largeScreenConstraintsChanges: ConstraintChange? = null
+) {
+ operator fun plus(other: ConstraintsChanges) = ConstraintsChanges(
+ qqsConstraintsChanges + other.qqsConstraintsChanges,
+ qsConstraintsChanges + other.qsConstraintsChanges,
+ largeScreenConstraintsChanges + other.largeScreenConstraintsChanges
+ )
+}
+
+/**
+ * Determines [ConstraintChanges] for [LargeScreenShadeHeaderController] based on configurations.
+ *
+ * Given that the number of different scenarios is not that large, having specific methods instead
+ * of a full map between state and [ConstraintSet] was preferred.
+ */
+interface CombinedShadeHeadersConstraintManager {
+ /**
+ * Changes for when the visibility of the privacy chip changes
+ */
+ fun privacyChipVisibilityConstraints(visible: Boolean): ConstraintsChanges
+
+ /**
+ * Changes for situations with no top center cutout (there may be a corner cutout)
+ */
+ fun emptyCutoutConstraints(): ConstraintsChanges
+
+ /**
+ * Changes to incorporate side insets due to rounded corners/corner cutouts
+ */
+ fun edgesGuidelinesConstraints(
+ cutoutStart: Int,
+ paddingStart: Int,
+ cutoutEnd: Int,
+ paddingEnd: Int
+ ): ConstraintsChanges
+
+ /**
+ * Changes for situations with top center cutout (in this case, there are no corner cutouts).
+ */
+ fun centerCutoutConstraints(rtl: Boolean, offsetFromEdge: Int): ConstraintsChanges
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
new file mode 100644
index 000000000000..4063af3cbc36
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.shade
+
+import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.R
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
+
+/**
+ * Standard implementation of [CombinedShadeHeadersConstraintManager].
+ */
+@CentralSurfacesComponent.CentralSurfacesScope
+object CombinedShadeHeadersConstraintManagerImpl : CombinedShadeHeadersConstraintManager {
+
+ override fun privacyChipVisibilityConstraints(visible: Boolean): ConstraintsChanges {
+ val constraintAlpha = if (visible) 0f else 1f
+ return ConstraintsChanges(
+ qqsConstraintsChanges = {
+ setAlpha(R.id.statusIcons, constraintAlpha)
+ setAlpha(R.id.batteryRemainingIcon, constraintAlpha)
+ }
+ )
+ }
+
+ override fun emptyCutoutConstraints(): ConstraintsChanges {
+ return ConstraintsChanges(
+ qqsConstraintsChanges = {
+ connect(R.id.date, ConstraintSet.END, R.id.barrier, ConstraintSet.START)
+ createBarrier(
+ R.id.barrier,
+ ConstraintSet.START,
+ 0,
+ R.id.statusIcons,
+ R.id.privacy_container
+ )
+ connect(R.id.statusIcons, ConstraintSet.START, R.id.date, ConstraintSet.END)
+ connect(R.id.privacy_container, ConstraintSet.START, R.id.date, ConstraintSet.END)
+ constrainWidth(R.id.statusIcons, ViewGroup.LayoutParams.WRAP_CONTENT)
+ }
+ )
+ }
+
+ override fun edgesGuidelinesConstraints(
+ cutoutStart: Int,
+ paddingStart: Int,
+ cutoutEnd: Int,
+ paddingEnd: Int
+ ): ConstraintsChanges {
+ val change: ConstraintChange = {
+ setGuidelineBegin(R.id.begin_guide, Math.max(cutoutStart - paddingStart, 0))
+ setGuidelineEnd(R.id.end_guide, Math.max(cutoutEnd - paddingEnd, 0))
+ }
+ return ConstraintsChanges(
+ qqsConstraintsChanges = change,
+ qsConstraintsChanges = change
+ )
+ }
+
+ override fun centerCutoutConstraints(rtl: Boolean, offsetFromEdge: Int): ConstraintsChanges {
+ val centerStart = if (!rtl) R.id.center_left else R.id.center_right
+ val centerEnd = if (!rtl) R.id.center_right else R.id.center_left
+ // Use guidelines to block the center cutout area.
+ return ConstraintsChanges(
+ qqsConstraintsChanges = {
+ setGuidelineBegin(centerStart, offsetFromEdge)
+ setGuidelineEnd(centerEnd, offsetFromEdge)
+ connect(R.id.date, ConstraintSet.END, centerStart, ConstraintSet.START)
+ connect(
+ R.id.statusIcons,
+ ConstraintSet.START,
+ centerEnd,
+ ConstraintSet.END
+ )
+ connect(
+ R.id.privacy_container,
+ ConstraintSet.START,
+ centerEnd,
+ ConstraintSet.END
+ )
+ constrainWidth(R.id.statusIcons, 0)
+ },
+ qsConstraintsChanges = {
+ setGuidelineBegin(centerStart, offsetFromEdge)
+ setGuidelineEnd(centerEnd, offsetFromEdge)
+ connect(
+ R.id.privacy_container,
+ ConstraintSet.START,
+ centerEnd,
+ ConstraintSet.END
+ )
+ }
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
new file mode 100644
index 000000000000..5793105e481e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.shade
+
+import android.annotation.IdRes
+import android.app.StatusBarManager
+import android.content.res.Configuration
+import android.os.Trace
+import android.os.Trace.TRACE_TAG_APP
+import android.util.Pair
+import android.view.View
+import android.view.WindowInsets
+import android.widget.TextView
+import androidx.annotation.VisibleForTesting
+import androidx.constraintlayout.motion.widget.MotionLayout
+import com.android.settingslib.Utils
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+import com.android.systemui.animation.ShadeInterpolation
+import com.android.systemui.battery.BatteryMeterView
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.qs.ChipVisibilityListener
+import com.android.systemui.qs.HeaderPrivacyIconsController
+import com.android.systemui.qs.carrier.QSCarrierGroup
+import com.android.systemui.qs.carrier.QSCarrierGroupController
+import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.HEADER_TRANSITION_ID
+import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT
+import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.LARGE_SCREEN_HEADER_TRANSITION_ID
+import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
+import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.phone.StatusBarIconController
+import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
+import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.LARGE_SCREEN_BATTERY_CONTROLLER
+import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.LARGE_SCREEN_SHADE_HEADER
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.VariableDateView
+import com.android.systemui.statusbar.policy.VariableDateViewController
+import com.android.systemui.util.ViewController
+import java.io.PrintWriter
+import javax.inject.Inject
+import javax.inject.Named
+
+/**
+ * Controller for QS header on Large Screen width (large screen + landscape).
+ *
+ * Additionally, this serves as the staging ground for the combined QS headers. A single
+ * [MotionLayout] that changes constraints depending on the configuration and can animate the
+ * expansion of the headers in small screen portrait.
+ *
+ * [header] will be a [MotionLayout] if [Flags.COMBINED_QS_HEADERS] is enabled. In this case, the
+ * [MotionLayout] has 2 transitions:
+ * * [HEADER_TRANSITION_ID]: [QQS_HEADER_CONSTRAINT] <-> [QS_HEADER_CONSTRAINT] for portrait
+ * handheld device configuration.
+ * * [LARGE_SCREEN_HEADER_TRANSITION_ID]: [LARGE_SCREEN_HEADER_CONSTRAINT] (to itself) for all
+ * other configurations
+ */
+@CentralSurfacesScope
+class LargeScreenShadeHeaderController @Inject constructor(
+ @Named(LARGE_SCREEN_SHADE_HEADER) private val header: View,
+ private val statusBarIconController: StatusBarIconController,
+ private val privacyIconsController: HeaderPrivacyIconsController,
+ private val insetsProvider: StatusBarContentInsetsProvider,
+ private val configurationController: ConfigurationController,
+ private val variableDateViewControllerFactory: VariableDateViewController.Factory,
+ @Named(LARGE_SCREEN_BATTERY_CONTROLLER)
+ private val batteryMeterViewController: BatteryMeterViewController,
+ private val dumpManager: DumpManager,
+ private val featureFlags: FeatureFlags,
+ private val qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
+ private val combinedShadeHeadersConstraintManager: CombinedShadeHeadersConstraintManager
+) : ViewController<View>(header), Dumpable {
+
+ companion object {
+ /** IDs for transitions and constraints for the [MotionLayout]. These are only used when
+ * [Flags.COMBINED_QS_HEADERS] is enabled.
+ */
+ @VisibleForTesting
+ internal val HEADER_TRANSITION_ID = R.id.header_transition
+ @VisibleForTesting
+ internal val LARGE_SCREEN_HEADER_TRANSITION_ID = R.id.large_screen_header_transition
+ @VisibleForTesting
+ internal val QQS_HEADER_CONSTRAINT = R.id.qqs_header_constraint
+ @VisibleForTesting
+ internal val QS_HEADER_CONSTRAINT = R.id.qs_header_constraint
+ @VisibleForTesting
+ internal val LARGE_SCREEN_HEADER_CONSTRAINT = R.id.large_screen_header_constraint
+
+ private fun Int.stateToString() = when (this) {
+ QQS_HEADER_CONSTRAINT -> "QQS Header"
+ QS_HEADER_CONSTRAINT -> "QS Header"
+ LARGE_SCREEN_HEADER_CONSTRAINT -> "Large Screen Header"
+ else -> "Unknown state"
+ }
+ }
+
+ init {
+ loadConstraints()
+ }
+
+ private val combinedHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)
+
+ private lateinit var iconManager: StatusBarIconController.TintedIconManager
+ private lateinit var carrierIconSlots: List<String>
+ private lateinit var qsCarrierGroupController: QSCarrierGroupController
+
+ private val batteryIcon: BatteryMeterView = header.findViewById(R.id.batteryRemainingIcon)
+ private val clock: TextView = header.findViewById(R.id.clock)
+ private val date: TextView = header.findViewById(R.id.date)
+ private val iconContainer: StatusIconContainer = header.findViewById(R.id.statusIcons)
+ private val qsCarrierGroup: QSCarrierGroup = header.findViewById(R.id.carrier_group)
+
+ private var cutoutLeft = 0
+ private var cutoutRight = 0
+ private var roundedCorners = 0
+ private var lastInsets: WindowInsets? = null
+
+ private var qsDisabled = false
+ private var visible = false
+ set(value) {
+ if (field == value) {
+ return
+ }
+ field = value
+ updateListeners()
+ }
+
+ /**
+ * Whether the QQS/QS part of the shade is visible. This is particularly important in
+ * Lockscreen, as the shade is visible but QS is not.
+ */
+ var qsVisible = false
+ set(value) {
+ if (field == value) {
+ return
+ }
+ field = value
+ onShadeExpandedChanged()
+ }
+
+ /**
+ * Whether we are in a configuration with large screen width. In this case, the header is a
+ * single line.
+ */
+ var largeScreenActive = false
+ set(value) {
+ if (field == value) {
+ return
+ }
+ field = value
+ onHeaderStateChanged()
+ }
+
+ /**
+ * Expansion fraction of the QQS/QS shade. This is not the expansion between QQS <-> QS.
+ */
+ var shadeExpandedFraction = -1f
+ set(value) {
+ if (visible && field != value) {
+ header.alpha = ShadeInterpolation.getContentAlpha(value)
+ field = value
+ }
+ }
+
+ /**
+ * Expansion fraction of the QQS <-> QS animation.
+ */
+ var qsExpandedFraction = -1f
+ set(value) {
+ if (visible && field != value) {
+ field = value
+ updatePosition()
+ }
+ }
+
+ /**
+ * Current scroll of QS.
+ */
+ var qsScrollY = 0
+ set(value) {
+ if (field != value) {
+ field = value
+ updateScrollY()
+ }
+ }
+
+ private val insetListener = View.OnApplyWindowInsetsListener { view, insets ->
+ updateConstraintsForInsets(view as MotionLayout, insets)
+ lastInsets = WindowInsets(insets)
+
+ view.onApplyWindowInsets(insets)
+ }
+
+ private val chipVisibilityListener: ChipVisibilityListener = object : ChipVisibilityListener {
+ override fun onChipVisibilityRefreshed(visible: Boolean) {
+ if (header is MotionLayout) {
+ // If the privacy chip is visible, we hide the status icons and battery remaining
+ // icon, only in QQS.
+ val update = combinedShadeHeadersConstraintManager
+ .privacyChipVisibilityConstraints(visible)
+ header.updateAllConstraints(update)
+ }
+ }
+ }
+
+ private val configurationControllerListener =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ if (header !is MotionLayout) {
+ val left = header.resources.getDimensionPixelSize(
+ R.dimen.large_screen_shade_header_left_padding
+ )
+ header.setPadding(
+ left,
+ header.paddingTop,
+ header.paddingRight,
+ header.paddingBottom
+ )
+ }
+ }
+
+ override fun onDensityOrFontScaleChanged() {
+ clock.setTextAppearance(R.style.TextAppearance_QS_Status)
+ date.setTextAppearance(R.style.TextAppearance_QS_Status)
+ qsCarrierGroup.updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
+ if (header is MotionLayout) {
+ loadConstraints()
+ lastInsets?.let { updateConstraintsForInsets(header, it) }
+ }
+ updateResources()
+ }
+ }
+
+ override fun onInit() {
+ if (header is MotionLayout) {
+ variableDateViewControllerFactory.create(date as VariableDateView).init()
+ }
+ batteryMeterViewController.init()
+
+ // battery settings same as in QS icons
+ batteryMeterViewController.ignoreTunerUpdates()
+ batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
+
+ iconManager = StatusBarIconController.TintedIconManager(iconContainer, featureFlags)
+ iconManager.setTint(
+ Utils.getColorAttrDefaultColor(header.context, android.R.attr.textColorPrimary)
+ )
+
+ carrierIconSlots = if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
+ listOf(
+ header.context.getString(com.android.internal.R.string.status_bar_no_calling),
+ header.context.getString(com.android.internal.R.string.status_bar_call_strength)
+ )
+ } else {
+ listOf(header.context.getString(com.android.internal.R.string.status_bar_mobile))
+ }
+ qsCarrierGroupController = qsCarrierGroupControllerBuilder
+ .setQSCarrierGroup(qsCarrierGroup)
+ .build()
+ }
+
+ override fun onViewAttached() {
+ privacyIconsController.chipVisibilityListener = chipVisibilityListener
+ if (header is MotionLayout) {
+ header.setOnApplyWindowInsetsListener(insetListener)
+ clock.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
+ val newPivot = if (v.isLayoutRtl) v.width.toFloat() else 0f
+ v.pivotX = newPivot
+ }
+ }
+
+ dumpManager.registerDumpable(this)
+ configurationController.addCallback(configurationControllerListener)
+
+ updateVisibility()
+ updateTransition()
+ }
+
+ override fun onViewDetached() {
+ privacyIconsController.chipVisibilityListener = null
+ dumpManager.unregisterDumpable(this::class.java.simpleName)
+ configurationController.removeCallback(configurationControllerListener)
+ }
+
+ fun disable(state1: Int, state2: Int, animate: Boolean) {
+ val disabled = state2 and StatusBarManager.DISABLE2_QUICK_SETTINGS != 0
+ if (disabled == qsDisabled) return
+ qsDisabled = disabled
+ updateVisibility()
+ }
+
+ private fun loadConstraints() {
+ if (header is MotionLayout) {
+ // Use resources.getXml instead of passing the resource id due to bug b/205018300
+ header.getConstraintSet(QQS_HEADER_CONSTRAINT)
+ .load(context, resources.getXml(R.xml.qqs_header))
+ val qsConstraints = if (featureFlags.isEnabled(Flags.NEW_HEADER)) {
+ R.xml.qs_header_new
+ } else {
+ R.xml.qs_header
+ }
+ header.getConstraintSet(QS_HEADER_CONSTRAINT)
+ .load(context, resources.getXml(qsConstraints))
+ header.getConstraintSet(LARGE_SCREEN_HEADER_CONSTRAINT)
+ .load(context, resources.getXml(R.xml.large_screen_shade_header))
+ }
+ }
+
+ private fun updateConstraintsForInsets(view: MotionLayout, insets: WindowInsets) {
+ val cutout = insets.displayCutout
+
+ val sbInsets: Pair<Int, Int> = insetsProvider.getStatusBarContentInsetsForCurrentRotation()
+ cutoutLeft = sbInsets.first
+ cutoutRight = sbInsets.second
+ val hasCornerCutout: Boolean = insetsProvider.currentRotationHasCornerCutout()
+ updateQQSPaddings()
+ // Set these guides as the left/right limits for content that lives in the top row, using
+ // cutoutLeft and cutoutRight
+ var changes = combinedShadeHeadersConstraintManager
+ .edgesGuidelinesConstraints(
+ if (view.isLayoutRtl) cutoutRight else cutoutLeft,
+ header.paddingStart,
+ if (view.isLayoutRtl) cutoutLeft else cutoutRight,
+ header.paddingEnd
+ )
+
+ if (cutout != null) {
+ val topCutout = cutout.boundingRectTop
+ if (topCutout.isEmpty || hasCornerCutout) {
+ changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints()
+ } else {
+ changes += combinedShadeHeadersConstraintManager.centerCutoutConstraints(
+ view.isLayoutRtl,
+ (view.width - view.paddingLeft - view.paddingRight - topCutout.width()) / 2
+ )
+ }
+ } else {
+ changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints()
+ }
+
+ view.updateAllConstraints(changes)
+ }
+
+ private fun updateScrollY() {
+ if (!largeScreenActive && combinedHeaders) {
+ header.scrollY = qsScrollY
+ }
+ }
+
+ private fun onShadeExpandedChanged() {
+ if (qsVisible) {
+ privacyIconsController.startListening()
+ } else {
+ privacyIconsController.stopListening()
+ }
+ updateVisibility()
+ updatePosition()
+ }
+
+ private fun onHeaderStateChanged() {
+ if (largeScreenActive || combinedHeaders) {
+ privacyIconsController.onParentVisible()
+ } else {
+ privacyIconsController.onParentInvisible()
+ }
+ updateVisibility()
+ updateTransition()
+ }
+
+ /**
+ * If not using [combinedHeaders] this should only be visible on large screen. Else, it should
+ * be visible any time the QQS/QS shade is open.
+ */
+ private fun updateVisibility() {
+ val visibility = if (!largeScreenActive && !combinedHeaders || qsDisabled) {
+ View.GONE
+ } else if (qsVisible) {
+ View.VISIBLE
+ } else {
+ View.INVISIBLE
+ }
+ if (header.visibility != visibility) {
+ header.visibility = visibility
+ visible = visibility == View.VISIBLE
+ }
+ }
+
+ private fun updateTransition() {
+ if (!combinedHeaders) {
+ return
+ }
+ header as MotionLayout
+ if (largeScreenActive) {
+ header.setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID)
+ header.getConstraintSet(LARGE_SCREEN_HEADER_CONSTRAINT).applyTo(header)
+ } else {
+ header.setTransition(HEADER_TRANSITION_ID)
+ header.transitionToStart()
+ updatePosition()
+ updateScrollY()
+ }
+ }
+
+ private fun updatePosition() {
+ if (header is MotionLayout && !largeScreenActive && visible) {
+ Trace.instantForTrack(
+ TRACE_TAG_APP,
+ "LargeScreenHeaderController - updatePosition",
+ "position: $qsExpandedFraction"
+ )
+ header.progress = qsExpandedFraction
+ }
+ }
+
+ private fun updateListeners() {
+ qsCarrierGroupController.setListening(visible)
+ if (visible) {
+ updateSingleCarrier(qsCarrierGroupController.isSingleCarrier)
+ qsCarrierGroupController.setOnSingleCarrierChangedListener { updateSingleCarrier(it) }
+ statusBarIconController.addIconGroup(iconManager)
+ } else {
+ qsCarrierGroupController.setOnSingleCarrierChangedListener(null)
+ statusBarIconController.removeIconGroup(iconManager)
+ }
+ }
+
+ private fun updateSingleCarrier(singleCarrier: Boolean) {
+ if (singleCarrier) {
+ iconContainer.removeIgnoredSlots(carrierIconSlots)
+ } else {
+ iconContainer.addIgnoredSlots(carrierIconSlots)
+ }
+ }
+
+ private fun updateResources() {
+ roundedCorners = resources.getDimensionPixelSize(R.dimen.rounded_corner_content_padding)
+ val padding = resources.getDimensionPixelSize(R.dimen.qs_panel_padding)
+ header.setPadding(padding, header.paddingTop, padding, header.paddingBottom)
+ updateQQSPaddings()
+ }
+
+ private fun updateQQSPaddings() {
+ if (header is MotionLayout) {
+ val clockPaddingStart = resources
+ .getDimensionPixelSize(R.dimen.status_bar_left_clock_starting_padding)
+ val clockPaddingEnd = resources
+ .getDimensionPixelSize(R.dimen.status_bar_left_clock_end_padding)
+ clock.setPaddingRelative(
+ clockPaddingStart,
+ clock.paddingTop,
+ clockPaddingEnd,
+ clock.paddingBottom
+ )
+ }
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("visible: $visible")
+ pw.println("shadeExpanded: $qsVisible")
+ pw.println("shadeExpandedFraction: $shadeExpandedFraction")
+ pw.println("active: $largeScreenActive")
+ pw.println("qsExpandedFraction: $qsExpandedFraction")
+ pw.println("qsScrollY: $qsScrollY")
+ if (combinedHeaders) {
+ header as MotionLayout
+ pw.println("currentState: ${header.currentState.stateToString()}")
+ }
+ }
+
+ private fun MotionLayout.updateConstraints(@IdRes state: Int, update: ConstraintChange) {
+ val constraints = getConstraintSet(state)
+ constraints.update()
+ updateState(state, constraints)
+ }
+
+ /**
+ * Updates the [ConstraintSet] for the case of combined headers.
+ *
+ * Only non-`null` changes are applied to reduce the number of rebuilding in the [MotionLayout].
+ */
+ private fun MotionLayout.updateAllConstraints(updates: ConstraintsChanges) {
+ if (updates.qqsConstraintsChanges != null) {
+ updateConstraints(QQS_HEADER_CONSTRAINT, updates.qqsConstraintsChanges)
+ }
+ if (updates.qsConstraintsChanges != null) {
+ updateConstraints(QS_HEADER_CONSTRAINT, updates.qsConstraintsChanges)
+ }
+ if (updates.largeScreenConstraintsChanges != null) {
+ updateConstraints(LARGE_SCREEN_HEADER_CONSTRAINT, updates.largeScreenConstraintsChanges)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVCDownEventState.kt b/packages/SystemUI/src/com/android/systemui/shade/NPVCDownEventState.kt
index d44a56942065..07e8b9fe3123 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVCDownEventState.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NPVCDownEventState.kt
@@ -11,7 +11,7 @@
* 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
+package com.android.systemui.shade
import android.view.MotionEvent
import com.android.systemui.dump.DumpsysTableLogger
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEvents.kt b/packages/SystemUI/src/com/android/systemui/shade/NotifPanelEvents.kt
index a385e22c1aff..ce9d89f89ae1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEvents.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotifPanelEvents.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone
+package com.android.systemui.shade
/** Provides certain notification panel events. */
interface NotifPanelEvents {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEventsModule.java b/packages/SystemUI/src/com/android/systemui/shade/NotifPanelEventsModule.java
index 2aaf6a5f4391..67723843086a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEventsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotifPanelEventsModule.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.shade;
import com.android.systemui.dagger.SysUISingleton;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
index ff48755f750a..e0cd482166b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone
+package com.android.systemui.shade
import android.content.Context
import android.view.ViewGroup
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelView.java
index d9ba494a4d63..e0997ff0f905 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.shade;
import android.content.Context;
import android.graphics.Canvas;
@@ -25,6 +25,8 @@ import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.PanelView;
+import com.android.systemui.statusbar.phone.TapAgainView;
public class NotificationPanelView extends PanelView {
@@ -35,8 +37,8 @@ public class NotificationPanelView extends PanelView {
*/
public static final int FLING_EXPAND = 0;
- static final String COUNTER_PANEL_OPEN = "panel_open";
- static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
+ public static final String COUNTER_PANEL_OPEN = "panel_open";
+ public static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
private int mCurrentPanelAlpha;
private final Paint mAlphaPaint = new Paint();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 3580fe6cdbc5..61d19634af38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.shade;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
@@ -32,6 +32,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_N
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD;
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_CLOSED;
@@ -39,8 +40,6 @@ import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStat
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPENING;
import static com.android.systemui.util.DumpUtilsKt.asIndenting;
-import static java.lang.Float.isNaN;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -49,15 +48,13 @@ import android.app.ActivityManager;
import android.app.Fragment;
import android.app.StatusBarManager;
import android.content.ContentResolver;
-import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Insets;
import android.graphics.Paint;
+import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
@@ -80,6 +77,7 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
+import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.ViewStub;
@@ -116,6 +114,7 @@ import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.camera.CameraGestureHelper;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.controls.dagger.ControlsComponent;
@@ -141,10 +140,10 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.GestureRecorder;
-import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -178,12 +177,34 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
+import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
+import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
+import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
+import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
+import com.android.systemui.statusbar.phone.PanelView;
+import com.android.systemui.statusbar.phone.PanelViewController;
+import com.android.systemui.statusbar.phone.PhoneStatusBarView;
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
+import com.android.systemui.statusbar.phone.TapAgainViewController;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.phone.panelstate.PanelState;
-import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -196,7 +217,6 @@ import com.android.systemui.util.Compile;
import com.android.systemui.util.LargeScreenUtils;
import com.android.systemui.util.ListenerSet;
import com.android.systemui.util.Utils;
-import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.wallet.controller.QuickAccessWalletController;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -208,14 +228,13 @@ import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
-import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Provider;
@CentralSurfacesComponent.CentralSurfacesScope
-public class NotificationPanelViewController extends PanelViewController {
+public final class NotificationPanelViewController extends PanelViewController {
private static final boolean DEBUG_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
private static final boolean SPEW_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
@@ -251,9 +270,6 @@ public class NotificationPanelViewController extends PanelViewController {
private final OnOverscrollTopChangedListener
mOnOverscrollTopChangedListener =
new OnOverscrollTopChangedListener();
- private final KeyguardAffordanceHelperCallback
- mKeyguardAffordanceHelperCallback =
- new KeyguardAffordanceHelperCallback();
private final OnEmptySpaceClickListener
mOnEmptySpaceClickListener =
new OnEmptySpaceClickListener();
@@ -264,7 +280,8 @@ public class NotificationPanelViewController extends PanelViewController {
private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
private final SettingsChangeObserver mSettingsChangeObserver;
- @VisibleForTesting final StatusBarStateListener mStatusBarStateListener =
+ @VisibleForTesting
+ final StatusBarStateListener mStatusBarStateListener =
new StatusBarStateListener();
private final NotificationPanelView mView;
private final VibratorHelper mVibratorHelper;
@@ -337,23 +354,24 @@ public class NotificationPanelViewController extends PanelViewController {
// Current max allowed keyguard notifications determined by measuring the panel
private int mMaxAllowedKeyguardNotifications;
- private ViewGroup mPreviewContainer;
- private KeyguardAffordanceHelper mAffordanceHelper;
private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
private KeyguardStatusBarView mKeyguardStatusBar;
private KeyguardStatusBarViewController mKeyguardStatusBarViewController;
- @VisibleForTesting QS mQs;
+ @VisibleForTesting
+ QS mQs;
private FrameLayout mQsFrame;
- private QsFrameTranslateController mQsFrameTranslateController;
+ private final QsFrameTranslateController mQsFrameTranslateController;
private KeyguardStatusViewController mKeyguardStatusViewController;
- private LockIconViewController mLockIconViewController;
+ private final LockIconViewController mLockIconViewController;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
- private NotificationsQSContainerController mNotificationsQSContainerController;
+ private final NotificationsQSContainerController mNotificationsQSContainerController;
+ private final Provider<KeyguardBottomAreaViewController>
+ mKeyguardBottomAreaViewControllerProvider;
private boolean mAnimateNextPositionUpdate;
private float mQuickQsHeaderHeight;
- private ScreenOffAnimationController mScreenOffAnimationController;
- private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ private final ScreenOffAnimationController mScreenOffAnimationController;
+ private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private int mTrackingPointer;
private VelocityTracker mQsVelocityTracker;
@@ -406,12 +424,6 @@ public class NotificationPanelViewController extends PanelViewController {
private int mLargeScreenShadeHeaderHeight;
private int mSplitShadeNotificationsScrimMarginBottom;
- /**
- * Vertical overlap allowed between the bottom of the notification shelf and
- * the top of the lock icon or the under-display fingerprint sensor background.
- */
- private int mShelfAndLockIconOverlap;
-
private final KeyguardClockPositionAlgorithm
mClockPositionAlgorithm =
new KeyguardClockPositionAlgorithm();
@@ -426,7 +438,8 @@ public class NotificationPanelViewController extends PanelViewController {
* Determines if QS should be already expanded when expanding shade.
* Used for split shade, two finger gesture as well as accessibility shortcut to QS.
*/
- @VisibleForTesting boolean mQsExpandImmediate;
+ @VisibleForTesting
+ boolean mQsExpandImmediate;
private boolean mTwoFingerQsExpandPossible;
private String mHeaderDebugInfo;
@@ -436,8 +449,6 @@ public class NotificationPanelViewController extends PanelViewController {
*/
private boolean mQsAnimatorExpand;
private boolean mIsLaunchTransitionFinished;
- private boolean mIsLaunchTransitionRunning;
- private Runnable mLaunchAnimationEndRunnable;
private boolean mOnlyAffordanceInThisMotion;
private ValueAnimator mQsSizeChangeAnimator;
@@ -450,16 +461,13 @@ public class NotificationPanelViewController extends PanelViewController {
private int mNavigationBarBottomHeight;
private boolean mExpandingFromHeadsUp;
private boolean mCollapsedOnDown;
- private int mPositionMinSideMargin;
private boolean mClosingWithAlphaFadeOut;
private boolean mHeadsUpAnimatingAway;
private boolean mLaunchingAffordance;
- private boolean mAffordanceHasPreview;
private final FalsingManager mFalsingManager;
private final FalsingCollector mFalsingCollector;
- private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
- private Runnable mHeadsUpExistenceChangedRunnable = () -> {
+ private final Runnable mHeadsUpExistenceChangedRunnable = () -> {
setHeadsUpAnimatingAway(false);
updatePanelExpansionAndVisibility();
};
@@ -469,9 +477,6 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean mIsFullWidth;
private boolean mBlockingExpansionForCurrentTouch;
- // TODO (b/204204226): no longer needed once refactor is complete
- private final boolean mUseCombinedQSHeaders;
-
/**
* Following variables maintain state of events when input focus transfer may occur.
*/
@@ -490,7 +495,6 @@ public class NotificationPanelViewController extends PanelViewController {
private float mLinearDarkAmount;
private boolean mPulsing;
- private boolean mUserSetupComplete;
private boolean mHideIconsDuringLaunchAnimation = true;
private int mStackScrollerMeasuringPass;
/**
@@ -516,10 +520,10 @@ public class NotificationPanelViewController extends PanelViewController {
mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_OUT);
private final AnimationProperties mPanelAlphaInPropertiesAnimator =
new AnimationProperties().setDuration(200).setAnimationEndAction((property) -> {
- if (mPanelAlphaEndAction != null) {
- mPanelAlphaEndAction.run();
- }
- }).setCustomInterpolator(
+ if (mPanelAlphaEndAction != null) {
+ mPanelAlphaEndAction.run();
+ }
+ }).setCustomInterpolator(
mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
private final NotificationEntryManager mEntryManager;
@@ -528,19 +532,10 @@ public class NotificationPanelViewController extends PanelViewController {
private final MediaDataManager mMediaDataManager;
private final SysUiState mSysUiState;
- private NotificationShadeDepthController mDepthController;
- private int mDisplayId;
+ private final NotificationShadeDepthController mDepthController;
+ private final int mDisplayId;
- /**
- * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged.
- *
- * onThemeChanged is forced when the theme might not have changed. So, to avoid unncessary
- * work, check the current id with the cached id.
- */
- private int mThemeResId;
private KeyguardIndicationController mKeyguardIndicationController;
- private int mShelfHeight;
- private int mDarkIconSize;
private int mHeadsUpInset;
private boolean mHeadsUpPinnedMode;
private boolean mAllowExpandForSmallExpansion;
@@ -646,6 +641,7 @@ public class NotificationPanelViewController extends PanelViewController {
private int mScrimCornerRadius;
private int mScreenCornerRadius;
private boolean mQSAnimatingHiddenFromCollapsed;
+ private boolean mUseLargeScreenShadeHeader;
private int mQsClipTop;
private int mQsClipBottom;
@@ -653,25 +649,29 @@ public class NotificationPanelViewController extends PanelViewController {
private final ContentResolver mContentResolver;
private float mMinFraction;
- private final Executor mUiExecutor;
- private final SecureSettings mSecureSettings;
-
- private KeyguardMediaController mKeyguardMediaController;
+ private final KeyguardMediaController mKeyguardMediaController;
private boolean mStatusViewCentered = true;
- private Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition;
- private Optional<NotificationPanelUnfoldAnimationController>
+ private final Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition;
+ private final Optional<NotificationPanelUnfoldAnimationController>
mNotificationPanelUnfoldAnimationController;
+ /** The drag distance required to fully expand the split shade. */
+ private int mSplitShadeFullTransitionDistance;
+
private final NotificationListContainer mNotificationListContainer;
private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
private final NPVCDownEventState.Buffer mLastDownEvents;
- private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
+ private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable =
+ () -> mKeyguardBottomArea.setVisibility(View.GONE);
+
+ private final AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
@Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
@@ -679,7 +679,8 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
- if (action == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId()
+ if (action
+ == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId()
|| action
== AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId()) {
mStatusBarKeyguardViewManager.showBouncer(true);
@@ -702,9 +703,10 @@ public class NotificationPanelViewController extends PanelViewController {
}
};
+ private final CameraGestureHelper mCameraGestureHelper;
+
@Inject
public NotificationPanelViewController(NotificationPanelView view,
- @Main Resources resources,
@Main Handler handler,
LayoutInflater layoutInflater,
FeatureFlags featureFlags,
@@ -754,8 +756,6 @@ public class NotificationPanelViewController extends PanelViewController {
QuickAccessWalletController quickAccessWalletController,
QRCodeScannerController qrCodeScannerController,
RecordingController recordingController,
- @Main Executor uiExecutor,
- SecureSettings secureSettings,
LargeScreenShadeHeaderController largeScreenShadeHeaderController,
ScreenOffAnimationController screenOffAnimationController,
LockscreenGestureLogger lockscreenGestureLogger,
@@ -766,13 +766,15 @@ public class NotificationPanelViewController extends PanelViewController {
InteractionJankMonitor interactionJankMonitor,
QsFrameTranslateController qsFrameTranslateController,
SysUiState sysUiState,
+ Provider<KeyguardBottomAreaViewController> keyguardBottomAreaViewControllerProvider,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
NotificationListContainer notificationListContainer,
PanelEventsEmitter panelEventsEmitter,
NotificationStackSizeCalculator notificationStackSizeCalculator,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
ShadeTransitionController shadeTransitionController,
- SystemClock systemClock) {
+ SystemClock systemClock,
+ CameraGestureHelper cameraGestureHelper) {
super(view,
falsingManager,
dozeLog,
@@ -806,6 +808,7 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationsQSContainerController = notificationsQSContainerController;
mNotificationListContainer = notificationListContainer;
mNotificationStackSizeCalculator = notificationStackSizeCalculator;
+ mKeyguardBottomAreaViewControllerProvider = keyguardBottomAreaViewControllerProvider;
mNotificationsQSContainerController.init();
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mNotificationIconAreaController = notificationIconAreaController;
@@ -839,8 +842,6 @@ public class NotificationPanelViewController extends PanelViewController {
mUserManager = userManager;
mMediaDataManager = mediaDataManager;
mTapAgainViewController = tapAgainViewController;
- mUiExecutor = uiExecutor;
- mSecureSettings = secureSettings;
mInteractionJankMonitor = interactionJankMonitor;
mSysUiState = sysUiState;
mPanelEventsEmitter = panelEventsEmitter;
@@ -850,7 +851,6 @@ public class NotificationPanelViewController extends PanelViewController {
}
});
statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
- mThemeResId = mView.getContext().getThemeResId();
mKeyguardBypassController = bypassController;
mUpdateMonitor = keyguardUpdateMonitor;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
@@ -897,14 +897,14 @@ public class NotificationPanelViewController extends PanelViewController {
mView.getOverlay().add(new DebugDrawable());
}
- mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition());
+ mKeyguardUnfoldTransition = unfoldComponent.map(
+ SysUIUnfoldComponent::getKeyguardUnfoldTransition);
mNotificationPanelUnfoldAnimationController = unfoldComponent.map(
SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController);
mQsFrameTranslateController = qsFrameTranslateController;
updateUserSwitcherFlags();
onFinishInflate();
- mUseCombinedQSHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS);
keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
@Override
@@ -956,6 +956,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
});
+ mCameraGestureHelper = cameraGestureHelper;
}
@VisibleForTesting
@@ -979,8 +980,8 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardStatusBarViewController =
mKeyguardStatusBarViewComponentFactory.build(
- mKeyguardStatusBar,
- mNotificationPanelViewStateProvider)
+ mKeyguardStatusBar,
+ mNotificationPanelViewStateProvider)
.getKeyguardStatusBarViewController();
mKeyguardStatusBarViewController.init();
@@ -1003,8 +1004,6 @@ public class NotificationPanelViewController extends PanelViewController {
mOnEmptySpaceClickListener);
addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp);
mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area);
- mPreviewContainer = mView.findViewById(R.id.preview_container);
- mKeyguardBottomArea.setPreviewContainer(mPreviewContainer);
initBottomArea();
@@ -1028,7 +1027,6 @@ public class NotificationPanelViewController extends PanelViewController {
mView.setRtlChangeListener(layoutDirection -> {
if (layoutDirection != mOldLayoutDirection) {
- mAffordanceHelper.onRtlPropertiesChanged();
mOldLayoutDirection = layoutDirection;
}
});
@@ -1039,6 +1037,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
mTapAgainViewController.init();
+ mLargeScreenShadeHeaderController.init();
mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
mNotificationPanelUnfoldAnimationController.ifPresent(controller ->
controller.setup(mNotificationContainerParent));
@@ -1054,12 +1053,8 @@ public class NotificationPanelViewController extends PanelViewController {
mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height);
mClockPositionAlgorithm.loadDimens(mResources);
mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold);
- mPositionMinSideMargin = mResources.getDimensionPixelSize(
- R.dimen.notification_panel_min_side_margin);
mIndicationBottomPadding = mResources.getDimensionPixelSize(
R.dimen.keyguard_indication_bottom_padding);
- mShelfHeight = mResources.getDimensionPixelSize(R.dimen.notification_shelf_height);
- mDarkIconSize = mResources.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark);
int statusbarHeight = SystemBarUtils.getStatusBarHeight(mView.getContext());
mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
R.dimen.heads_up_status_bar_padding);
@@ -1124,33 +1119,31 @@ public class NotificationPanelViewController extends PanelViewController {
private void setCentralSurfaces(CentralSurfaces centralSurfaces) {
// TODO: this can be injected.
mCentralSurfaces = centralSurfaces;
- mKeyguardBottomArea.setCentralSurfaces(mCentralSurfaces);
}
public void updateResources() {
mSplitShadeNotificationsScrimMarginBottom =
mResources.getDimensionPixelSize(
R.dimen.split_shade_notifications_scrim_margin_bottom);
- mShelfAndLockIconOverlap =
- mResources.getDimensionPixelSize(R.dimen.shelf_and_lock_icon_overlap);
-
final boolean newSplitShadeEnabled =
LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
final boolean splitShadeChanged = mSplitShadeEnabled != newSplitShadeEnabled;
mSplitShadeEnabled = newSplitShadeEnabled;
- boolean useLargeScreenShadeHeader =
- LargeScreenUtils.shouldUseLargeScreenShadeHeader(mView.getResources());
if (mQs != null) {
mQs.setInSplitShade(mSplitShadeEnabled);
}
+
+ mUseLargeScreenShadeHeader =
+ LargeScreenUtils.shouldUseLargeScreenShadeHeader(mView.getResources());
+
mLargeScreenShadeHeaderHeight =
mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
- mQuickQsHeaderHeight = useLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
+ mQuickQsHeaderHeight = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
SystemBarUtils.getQuickQsOffsetHeight(mView.getContext());
- int topMargin = useLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
+ int topMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight :
mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
- mLargeScreenShadeHeaderController.setActive(useLargeScreenShadeHeader);
+ mLargeScreenShadeHeaderController.setLargeScreenActive(mUseLargeScreenShadeHeader);
mAmbientState.setStackTopMargin(topMargin);
mNotificationsQSContainerController.updateResources();
@@ -1159,15 +1152,31 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardMediaController.refreshMediaPosition();
if (splitShadeChanged) {
- // when we switch from split shade to regular shade we want to enforce setting qs to
- // the default state: expanded for split shade and collapsed otherwise
- if (!isOnKeyguard() && mPanelExpanded) {
- setQsExpanded(mSplitShadeEnabled);
- }
- updateClockAppearance();
- updateQsState();
- mNotificationStackScrollLayoutController.updateFooter();
+ onSplitShadeEnabledChanged();
+ }
+
+ mSplitShadeFullTransitionDistance =
+ mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance);
+ }
+
+ private void onSplitShadeEnabledChanged() {
+ // when we switch between split shade and regular shade we want to enforce setting qs to
+ // the default state: expanded for split shade and collapsed otherwise
+ if (!isOnKeyguard() && mPanelExpanded) {
+ setQsExpanded(mSplitShadeEnabled);
+ }
+ if (isOnKeyguard() && mQsExpanded && mSplitShadeEnabled) {
+ // In single column keyguard - when you swipe from the top - QS is fully expanded and
+ // StatusBarState is KEYGUARD. That state doesn't make sense for split shade,
+ // where notifications are always visible and we effectively go to fully expanded
+ // shade, that is SHADE_LOCKED.
+ // Also we might just be switching from regular expanded shade, so we don't want
+ // to force state transition if it's already correct.
+ mStatusBarStateController.setState(StatusBarState.SHADE_LOCKED, /* force= */false);
}
+ updateClockAppearance();
+ updateQsState();
+ mNotificationStackScrollLayoutController.updateFooter();
}
private View reInflateStub(int viewId, int stubId, int layoutId, boolean enabled) {
@@ -1241,10 +1250,8 @@ public class NotificationPanelViewController extends PanelViewController {
int index = mView.indexOfChild(mKeyguardBottomArea);
mView.removeView(mKeyguardBottomArea);
KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
- mKeyguardBottomArea = (KeyguardBottomAreaView) mLayoutInflater.inflate(
- R.layout.keyguard_bottom_area, mView, false);
+ mKeyguardBottomArea = mKeyguardBottomAreaViewControllerProvider.get().getView();
mKeyguardBottomArea.initFrom(oldBottomArea);
- mKeyguardBottomArea.setPreviewContainer(mPreviewContainer);
mView.addView(mKeyguardBottomArea, index);
initBottomArea();
mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
@@ -1281,15 +1288,11 @@ public class NotificationPanelViewController extends PanelViewController {
}
private void initBottomArea() {
- mAffordanceHelper = new KeyguardAffordanceHelper(
- mKeyguardAffordanceHelperCallback, mView.getContext(), mFalsingManager);
- mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
- mKeyguardBottomArea.setCentralSurfaces(mCentralSurfaces);
- mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
- mKeyguardBottomArea.setFalsingManager(mFalsingManager);
- mKeyguardBottomArea.initWallet(mQuickAccessWalletController);
- mKeyguardBottomArea.initControls(mControlsComponent);
- mKeyguardBottomArea.initQRCodeScanner(mQRCodeScannerController);
+ mKeyguardBottomArea.init(
+ mFalsingManager,
+ mQuickAccessWalletController,
+ mControlsComponent,
+ mQRCodeScannerController);
}
@VisibleForTesting
@@ -1299,7 +1302,7 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateMaxDisplayedNotifications(boolean recompute) {
if (recompute) {
- mMaxAllowedKeyguardNotifications = Math.max(computeMaxKeyguardNotifications(), 1);
+ setMaxDisplayedNotifications(Math.max(computeMaxKeyguardNotifications(), 1));
} else {
if (SPEW_LOGCAT) Log.d(TAG, "Skipping computeMaxKeyguardNotifications() by request");
}
@@ -1327,7 +1330,7 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateGestureExclusionRect() {
Rect exclusionRect = calculateGestureExclusionRect();
- mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.EMPTY_LIST
+ mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.emptyList()
: Collections.singletonList(exclusionRect));
}
@@ -1355,14 +1358,11 @@ public class NotificationPanelViewController extends PanelViewController {
mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight);
mQsSizeChangeAnimator.setDuration(300);
mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- requestScrollerTopPaddingUpdate(false /* animate */);
- requestPanelHeightUpdate();
- int height = (int) mQsSizeChangeAnimator.getAnimatedValue();
- mQs.setHeightOverride(height);
- }
+ mQsSizeChangeAnimator.addUpdateListener(animation -> {
+ requestScrollerTopPaddingUpdate(false /* animate */);
+ requestPanelHeightUpdate();
+ int height = (int) mQsSizeChangeAnimator.getAnimatedValue();
+ mQs.setHeightOverride(height);
});
mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -1647,14 +1647,9 @@ public class NotificationPanelViewController extends PanelViewController {
setQsExpansionEnabled();
}
- @Override
public void resetViews(boolean animate) {
mIsLaunchTransitionFinished = false;
mBlockTouches = false;
- if (!mLaunchingAffordance) {
- mAffordanceHelper.reset(false);
- mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
- }
mCentralSurfaces.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
if (animate && !isFullyCollapsed()) {
@@ -1708,11 +1703,16 @@ public class NotificationPanelViewController extends PanelViewController {
setQsExpansion(mQsMinExpansionHeight);
}
+ @Override
+ @VisibleForTesting
+ protected void cancelHeightAnimator() {
+ super.cancelHeightAnimator();
+ }
+
public void cancelAnimation() {
mView.animate().cancel();
}
-
/**
* Animate QS closing by flinging it.
* If QS is expanded, it will collapse into QQS and stop.
@@ -1912,12 +1912,8 @@ public class NotificationPanelViewController extends PanelViewController {
mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
mAllowExpandForSmallExpansion = mExpectingSynthesizedDown;
mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown;
- if (mExpectingSynthesizedDown) {
- mLastEventSynthesizedDown = true;
- } else {
- // down but not synthesized motion event.
- mLastEventSynthesizedDown = false;
- }
+ // When false, down but not synthesized motion event.
+ mLastEventSynthesizedDown = mExpectingSynthesizedDown;
mLastDownEvents.insert(
mSystemClock.currentTimeMillis(),
mDownX,
@@ -1942,7 +1938,6 @@ public class NotificationPanelViewController extends PanelViewController {
*
* @param downX the x location where the touch started
* @param downY the y location where the touch started
- *
* @return true if the panel could be collapsed because it stared on QQS
*/
private boolean canPanelCollapseOnQQS(float downX, float downY) {
@@ -1951,7 +1946,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
View header = mQs == null ? mKeyguardStatusBar : mQs.getHeader();
return downX >= mQsFrame.getX() && downX <= mQsFrame.getX() + mQsFrame.getWidth()
- && downY <= header.getBottom();
+ && downY <= header.getBottom();
}
@@ -2096,7 +2091,7 @@ public class NotificationPanelViewController extends PanelViewController {
return false;
}
return y <= mNotificationStackScrollLayoutController.getBottomMostNotificationBottom()
- || y <= mQs.getView().getY() + mQs.getView().getHeight();
+ || y <= mQs.getView().getY() + mQs.getView().getHeight();
}
private boolean isOpenQsEvent(MotionEvent event) {
@@ -2197,11 +2192,6 @@ public class NotificationPanelViewController extends PanelViewController {
return isFullyCollapsed() || mBarState != StatusBarState.SHADE;
}
- @Override
- protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) {
- return !mAffordanceHelper.isOnAffordanceIcon(x, y);
- }
-
private void onQsTouch(MotionEvent event) {
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
@@ -2280,15 +2270,11 @@ public class NotificationPanelViewController extends PanelViewController {
}
private void onQsExpansionStarted() {
- onQsExpansionStarted(0);
- }
-
- protected void onQsExpansionStarted(int overscrollAmount) {
cancelQsAnimation();
cancelHeightAnimator();
// Reset scroll position and apply that position to the expanded height.
- float height = mQsExpansionHeight - overscrollAmount;
+ float height = mQsExpansionHeight;
setQsExpansion(height);
requestPanelHeightUpdate();
mNotificationStackScrollLayoutController.checkSnoozeLeavebehind();
@@ -2300,7 +2286,8 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
- @VisibleForTesting void setQsExpanded(boolean expanded) {
+ @VisibleForTesting
+ void setQsExpanded(boolean expanded) {
boolean changed = mQsExpanded != expanded;
if (changed) {
mQsExpanded = expanded;
@@ -2325,13 +2312,6 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
- private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() {
- @Override
- public void run() {
- mKeyguardBottomArea.setVisibility(View.GONE);
- }
- };
-
private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) {
mKeyguardBottomArea.animate().cancel();
if (goingToFullShade) {
@@ -2437,7 +2417,7 @@ public class NotificationPanelViewController extends PanelViewController {
: getExpandedFraction();
mLargeScreenShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction);
mLargeScreenShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
- mLargeScreenShadeHeaderController.setShadeExpanded(mQsVisible);
+ mLargeScreenShadeHeaderController.setQsVisible(mQsVisible);
}
private void onStackYChanged(boolean shouldAnimate) {
@@ -2449,7 +2429,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
setQSClippingBounds();
}
- };
+ }
private void onNotificationScrolled(int newScrollPosition) {
updateQSExpansionEnabledAmbient();
@@ -2463,8 +2443,8 @@ public class NotificationPanelViewController extends PanelViewController {
}
/**
- * Updates scrim bounds, QS clipping, and KSV clipping as well based on the bounds of the shade
- * and QS state.
+ * Updates scrim bounds, QS clipping, notifications clipping and keyguard status view clipping
+ * as well based on the bounds of the shade and QS state.
*/
private void setQSClippingBounds() {
final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction());
@@ -2540,6 +2520,13 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
+ /**
+ * Applies clipping to quick settings, notifications layout and
+ * updates bounds of the notifications background (notifications scrim).
+ *
+ * The parameters are bounds of the notifications area rectangle, this function
+ * calculates bounds for the QS clipping based on the notifications bounds.
+ */
private void applyQSClippingBounds(int left, int top, int right, int bottom,
boolean qsVisible) {
if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) {
@@ -2607,7 +2594,7 @@ public class NotificationPanelViewController extends PanelViewController {
boolean pulseExpanding = mPulseExpansionHandler.isExpanding();
if (mTransitioningToFullShadeProgress > 0.0f || pulseExpanding
|| (mQsClippingAnimation != null
- && (mIsQsTranslationResetAnimator || mIsPulseExpansionResetAnimator))) {
+ && (mIsQsTranslationResetAnimator || mIsPulseExpansionResetAnimator))) {
if (pulseExpanding || mIsPulseExpansionResetAnimator) {
// qsTranslation should only be positive during pulse expansion because it's
// already translating in from the top
@@ -2626,8 +2613,8 @@ public class NotificationPanelViewController extends PanelViewController {
mQs.setFancyClipping(
mQsClipTop,
mQsClipBottom,
- radius, qsVisible
- && !mSplitShadeEnabled);
+ radius,
+ qsVisible && !mSplitShadeEnabled);
}
mKeyguardStatusViewController.setClipBounds(
clipStatusView ? mKeyguardStatusAreaClipBounds : null);
@@ -2675,12 +2662,10 @@ public class NotificationPanelViewController extends PanelViewController {
if (mTransitioningToFullShadeProgress > 0.0f) {
return mTransitionToFullShadeQSPosition;
} else {
- int qsBottomY = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight();
- if (qsExpansionFraction != 0.0) {
- qsBottomY = (int) MathUtils.lerp(
- qsBottomY, mQs.getDesiredHeight(), qsExpansionFraction);
- }
- return qsBottomY;
+ int qsBottomYFrom = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight();
+ int expandedTopMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight : 0;
+ int qsBottomYTo = mQs.getDesiredHeight() + expandedTopMargin;
+ return (int) MathUtils.lerp(qsBottomYFrom, qsBottomYTo, qsExpansionFraction);
}
}
@@ -2699,8 +2684,8 @@ public class NotificationPanelViewController extends PanelViewController {
}
private float calculateNotificationsTopPadding() {
- if (mSplitShadeEnabled && !mKeyguardShowing) {
- return 0;
+ if (mSplitShadeEnabled) {
+ return mKeyguardShowing ? getKeyguardNotificationStaticPadding() : 0;
}
if (mKeyguardShowing && (mQsExpandImmediate
|| mIsExpanding && mQsExpandedWhenExpandingStarted)) {
@@ -2753,8 +2738,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
-
- protected void requestScrollerTopPaddingUpdate(boolean animate) {
+ private void requestScrollerTopPaddingUpdate(boolean animate) {
mNotificationStackScrollLayoutController.updateTopPadding(
calculateNotificationsTopPadding(), animate);
if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) {
@@ -2774,6 +2758,9 @@ public class NotificationPanelViewController extends PanelViewController {
mIsQsTranslationResetAnimator = mQsTranslationForFullShadeTransition > 0.0f;
}
+ if (mSplitShadeEnabled) {
+ updateQsExpansionForLockscreenToShadeTransition(pxAmount);
+ }
float endPosition = 0;
if (pxAmount > 0.0f) {
if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() == 0
@@ -2813,6 +2800,18 @@ public class NotificationPanelViewController extends PanelViewController {
updateQsExpansion();
}
+ private void updateQsExpansionForLockscreenToShadeTransition(float pxAmount) {
+ float qsExpansion = 0;
+ if (pxAmount > 0.0f) {
+ qsExpansion = MathUtils.lerp(mQsMinExpansionHeight, mQsMaxExpansionHeight,
+ mLockscreenShadeTransitionController.getQSDragProgress());
+ }
+ // SHADE_LOCKED means transition is over and we don't want further updates
+ if (mBarState != SHADE_LOCKED) {
+ setQsExpansion(qsExpansion);
+ }
+ }
+
/**
* Notify the panel that the pulse expansion has finished and that we're going to the full
* shade
@@ -2887,7 +2886,7 @@ public class NotificationPanelViewController extends PanelViewController {
* @param onFinishRunnable Runnable to be executed at the end of animation.
* @param isClick If originated by click (different interpolator and duration.)
*/
- protected void flingSettings(float vel, int type, final Runnable onFinishRunnable,
+ private void flingSettings(float vel, int type, final Runnable onFinishRunnable,
boolean isClick) {
float target;
switch (type) {
@@ -2929,11 +2928,11 @@ public class NotificationPanelViewController extends PanelViewController {
if (oppositeDirection) {
animator.setDuration(350);
}
- animator.addUpdateListener(animation -> {
- setQsExpansion((Float) animation.getAnimatedValue());
- });
+ animator.addUpdateListener(
+ animation -> setQsExpansion((Float) animation.getAnimatedValue()));
animator.addListener(new AnimatorListenerAdapter() {
private boolean mIsCanceled;
+
@Override
public void onAnimationStart(Animator animation) {
notifyExpandingStarted();
@@ -3007,7 +3006,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
@Override
- protected int getMaxPanelHeight() {
+ public int getMaxPanelHeight() {
int min = mStatusBarMinHeight;
if (!(mBarState == KEYGUARD)
&& mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) {
@@ -3016,13 +3015,22 @@ public class NotificationPanelViewController extends PanelViewController {
}
int maxHeight;
if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted
- || mPulsing) {
- maxHeight = calculatePanelHeightQsExpanded();
+ || mPulsing || mSplitShadeEnabled) {
+ if (mSplitShadeEnabled && mBarState == SHADE) {
+ // Max panel height is used to calculate the fraction of the shade expansion.
+ // Traditionally the value is based on the number of notifications.
+ // On split-shade, we want the required distance to be a specific and constant
+ // value, to make sure the expansion motion has the expected speed.
+ // We also only want this on non-lockscreen for now.
+ maxHeight = mSplitShadeFullTransitionDistance;
+ } else {
+ maxHeight = calculatePanelHeightQsExpanded();
+ }
} else {
maxHeight = calculatePanelHeightShade();
}
maxHeight = Math.max(min, maxHeight);
- if (maxHeight == 0 || isNaN(maxHeight)) {
+ if (maxHeight == 0) {
Log.wtf(TAG, "maxPanelHeight is invalid. mOverExpansion: "
+ mOverExpansion + ", calculatePanelHeightQsExpanded: "
+ calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: "
@@ -3102,7 +3110,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
- boolean isPanelExpanded() {
+ public boolean isPanelExpanded() {
return mPanelExpanded;
}
@@ -3187,7 +3195,7 @@ public class NotificationPanelViewController extends PanelViewController {
updateQsExpansion();
}
- protected float getHeaderTranslation() {
+ private float getHeaderTranslation() {
if (mBarState == KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
return -mQs.getQsMinExpansionHeight();
}
@@ -3218,14 +3226,7 @@ public class NotificationPanelViewController extends PanelViewController {
getExpandedFraction());
float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction());
alpha *= mBottomAreaShadeAlpha;
- mKeyguardBottomArea.setAffordanceAlpha(alpha);
- mKeyguardBottomArea.setImportantForAccessibility(
- alpha == 0f ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- View ambientIndicationContainer = mCentralSurfaces.getAmbientIndicationContainer();
- if (ambientIndicationContainer != null) {
- ambientIndicationContainer.setAlpha(alpha);
- }
+ mKeyguardBottomArea.setComponentAlphas(alpha);
mLockIconViewController.setAlpha(alpha);
}
@@ -3257,22 +3258,13 @@ public class NotificationPanelViewController extends PanelViewController {
mMediaHierarchyManager.setCollapsingShadeFromQS(false);
mMediaHierarchyManager.setQsExpanded(mQsExpanded);
if (isFullyCollapsed()) {
- DejankUtils.postAfterTraversal(new Runnable() {
- @Override
- public void run() {
- setListening(false);
- }
- });
+ DejankUtils.postAfterTraversal(() -> setListening(false));
// Workaround b/22639032: Make sure we invalidate something because else RenderThread
// thinks we are actually drawing a frame put in reality we don't, so RT doesn't go
// ahead with rendering and we jank.
- mView.postOnAnimation(new Runnable() {
- @Override
- public void run() {
- mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT);
- }
- });
+ mView.postOnAnimation(
+ () -> mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT));
} else {
setListening(true);
}
@@ -3337,9 +3329,6 @@ public class NotificationPanelViewController extends PanelViewController {
mQsExpandImmediate = true;
setShowShelfOnly(true);
}
- if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
- mAffordanceHelper.animateHideLeftRightIcon();
- }
mNotificationStackScrollLayoutController.onPanelTrackingStarted();
cancelPendingPanelCollapse();
}
@@ -3353,12 +3342,6 @@ public class NotificationPanelViewController extends PanelViewController {
true /* animate */);
}
mNotificationStackScrollLayoutController.onPanelTrackingStopped();
- if (expand && (mBarState == KEYGUARD
- || mBarState == StatusBarState.SHADE_LOCKED)) {
- if (!mHintAnimationRunning) {
- mAffordanceHelper.reset(true);
- }
- }
// If we unlocked from a swipe, the user's finger might still be down after the
// unlock animation ends. We need to wait until ACTION_UP to enable blurs again.
@@ -3431,10 +3414,6 @@ public class NotificationPanelViewController extends PanelViewController {
return mIsLaunchTransitionFinished;
}
- public boolean isLaunchTransitionRunning() {
- return mIsLaunchTransitionRunning;
- }
-
@Override
public void setIsLaunchAnimationRunning(boolean running) {
boolean wasRunning = mIsLaunchAnimationRunning;
@@ -3453,10 +3432,6 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
- public void setLaunchTransitionEndRunnable(Runnable r) {
- mLaunchAnimationEndRunnable = r;
- }
-
private void updateDozingVisibilities(boolean animate) {
mKeyguardBottomArea.setDozing(mDozing, animate);
if (!mDozing && animate) {
@@ -3489,13 +3464,13 @@ public class NotificationPanelViewController extends PanelViewController {
if (mUpdateMonitor.isFaceEnrolled()
&& !mUpdateMonitor.isFaceDetectionRunning()
&& !mUpdateMonitor.getUserCanSkipBouncer(
- KeyguardUpdateMonitor.getCurrentUser())) {
+ KeyguardUpdateMonitor.getCurrentUser())) {
mUpdateMonitor.requestFaceAuth(true);
} else {
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
mLockscreenGestureLogger
- .log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT);
+ .log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT);
startUnlockHintAnimation();
}
if (mUpdateMonitor.isFaceEnrolled()) {
@@ -3574,7 +3549,7 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationStackScrollLayoutController.forceNoOverlappingRendering(closing);
}
- protected void updateExpandedHeight(float expandedHeight) {
+ private void updateExpandedHeight(float expandedHeight) {
if (mTracking) {
mNotificationStackScrollLayoutController
.setExpandingVelocity(getCurrentExpandVelocity());
@@ -3597,12 +3572,10 @@ public class NotificationPanelViewController extends PanelViewController {
}
private void updateStatusBarIcons() {
- boolean
- showIconsWhenExpanded =
+ boolean showIconsWhenExpanded =
(isPanelVisibleBecauseOfHeadsUp() || isFullWidth())
&& getExpandedHeight() < getOpeningHeight();
- boolean noVisibleNotifications = true;
- if (showIconsWhenExpanded && noVisibleNotifications && isOnKeyguard()) {
+ if (showIconsWhenExpanded && isOnKeyguard()) {
showIconsWhenExpanded = false;
}
if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
@@ -3635,30 +3608,13 @@ public class NotificationPanelViewController extends PanelViewController {
&& mBarState == StatusBarState.SHADE;
}
- public void launchCamera(boolean animate, int source) {
- if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
- mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP;
- } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) {
- mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE;
- } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER) {
- mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER;
- } else {
-
- // Default.
- mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
- }
-
- // If we are launching it when we are occluded already we don't want it to animate,
- // nor setting these flags, since the occluded state doesn't change anymore, hence it's
- // never reset.
+ /** Launches the camera. */
+ public void launchCamera(int source) {
if (!isFullyCollapsed()) {
setLaunchingAffordance(true);
- } else {
- animate = false;
}
- mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null;
- mAffordanceHelper.launchAffordance(
- animate, mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
+
+ mCameraGestureHelper.launchCamera(source);
}
public void onAffordanceLaunchEnded() {
@@ -3671,9 +3627,6 @@ public class NotificationPanelViewController extends PanelViewController {
*/
private void setLaunchingAffordance(boolean launchingAffordance) {
mLaunchingAffordance = launchingAffordance;
- mKeyguardAffordanceHelperCallback.getLeftIcon().setLaunchingAffordance(launchingAffordance);
- mKeyguardAffordanceHelperCallback.getRightIcon().setLaunchingAffordance(
- launchingAffordance);
mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
}
@@ -3681,34 +3634,14 @@ public class NotificationPanelViewController extends PanelViewController {
* Return true when a bottom affordance is launching an occluded activity with a splash screen.
*/
public boolean isLaunchingAffordanceWithPreview() {
- return mLaunchingAffordance && mAffordanceHasPreview;
+ return mLaunchingAffordance;
}
/**
* Whether the camera application can be launched for the camera launch gesture.
*/
public boolean canCameraGestureBeLaunched() {
- if (!mCentralSurfaces.isCameraAllowedByAdmin()) {
- return false;
- }
-
- ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent();
- String
- packageToLaunch =
- (resolveInfo == null || resolveInfo.activityInfo == null) ? null
- : resolveInfo.activityInfo.packageName;
- return packageToLaunch != null && (mBarState != StatusBarState.SHADE || !isForegroundApp(
- packageToLaunch)) && !mAffordanceHelper.isSwipingInProgress();
- }
-
- /**
- * Return true if the applications with the package name is running in foreground.
- *
- * @param pkgName application package name.
- */
- private boolean isForegroundApp(String pkgName) {
- List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
- return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
+ return mCameraGestureHelper.canCameraGestureBeLaunched(mBarState);
}
public boolean hideStatusBarIconsWhenExpanded() {
@@ -3787,9 +3720,6 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public void setTouchAndAnimationDisabled(boolean disabled) {
super.setTouchAndAnimationDisabled(disabled);
- if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) {
- mAffordanceHelper.reset(false /* animate */);
- }
mNotificationStackScrollLayoutController.setAnimationsEnabled(!disabled);
}
@@ -3872,11 +3802,6 @@ public class NotificationPanelViewController extends PanelViewController {
return mKeyguardBottomArea;
}
- public void setUserSetupComplete(boolean userSetupComplete) {
- mUserSetupComplete = userSetupComplete;
- mKeyguardBottomArea.setUserSetupComplete(userSetupComplete);
- }
-
public void applyLaunchAnimationProgress(float linearProgress) {
boolean hideIcons = LaunchAnimator.getProgress(ActivityLaunchAnimator.TIMINGS,
linearProgress, ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
@@ -3942,21 +3867,22 @@ public class NotificationPanelViewController extends PanelViewController {
*/
public void startFoldToAodAnimation(Runnable endAction) {
mView.animate()
- .translationX(0)
- .alpha(1f)
- .setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
- .setInterpolator(EMPHASIZED_DECELERATE)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationCancel(Animator animation) {
- endAction.run();
- }
- @Override
- public void onAnimationEnd(Animator animation) {
- endAction.run();
- }
- })
- .start();
+ .translationX(0)
+ .alpha(1f)
+ .setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
+ .setInterpolator(EMPHASIZED_DECELERATE)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ endAction.run();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endAction.run();
+ }
+ })
+ .start();
mKeyguardStatusViewController.animateFoldToAod();
}
@@ -3970,7 +3896,6 @@ public class NotificationPanelViewController extends PanelViewController {
resetTranslation();
}
- /** */
public void setImportantForAccessibility(int mode) {
mView.setImportantForAccessibility(mode);
}
@@ -4018,7 +3943,6 @@ public class NotificationPanelViewController extends PanelViewController {
* {@link ShadeViewManager}.
*/
public void updateNotificationViews(String reason) {
- mNotificationStackScrollLayoutController.updateSectionBoundaries(reason);
mNotificationStackScrollLayoutController.updateFooter();
mNotificationIconAreaController.updateNotificationIcons(createVisibleEntriesList());
@@ -4230,10 +4154,6 @@ public class NotificationPanelViewController extends PanelViewController {
mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
}
boolean handled = false;
- if ((!mIsExpanding || mHintAnimationRunning) && !mQsExpanded
- && mBarState != StatusBarState.SHADE && !mDozing) {
- handled |= mAffordanceHelper.onTouchEvent(event);
- }
if (mOnlyAffordanceInThisMotion) {
return true;
}
@@ -4309,8 +4229,7 @@ public class NotificationPanelViewController extends PanelViewController {
};
@Override
- protected PanelViewController.OnConfigurationChangedListener
- createOnConfigurationChangedListener() {
+ protected OnConfigurationChangedListener createOnConfigurationChangedListener() {
return new OnConfigurationChangedListener();
}
@@ -4367,7 +4286,7 @@ public class NotificationPanelViewController extends PanelViewController {
+ isFullyExpanded() + " inQs=" + isInSettings());
}
mSysUiState.setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
- isFullyExpanded() && !isInSettings())
+ isFullyExpanded() && !isInSettings())
.setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED, isInSettings())
.commitUpdate(mDisplayId);
}
@@ -4476,147 +4395,6 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
- private class KeyguardAffordanceHelperCallback implements KeyguardAffordanceHelper.Callback {
- @Override
- public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) {
- boolean
- start =
- mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? rightPage
- : !rightPage;
- mIsLaunchTransitionRunning = true;
- mLaunchAnimationEndRunnable = null;
- float displayDensity = mCentralSurfaces.getDisplayDensity();
- int lengthDp = Math.abs((int) (translation / displayDensity));
- int velocityDp = Math.abs((int) (vel / displayDensity));
- if (start) {
- mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp);
- mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_DIALER);
- mFalsingCollector.onLeftAffordanceOn();
- if (mFalsingCollector.shouldEnforceBouncer()) {
- mCentralSurfaces.executeRunnableDismissingKeyguard(
- () -> mKeyguardBottomArea.launchLeftAffordance(), null,
- true /* dismissShade */, false /* afterKeyguardGone */,
- true /* deferred */);
- } else {
- mKeyguardBottomArea.launchLeftAffordance();
- }
- } else {
- if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals(
- mLastCameraLaunchSource)) {
- mLockscreenGestureLogger.write(
- MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp);
- mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_CAMERA);
- }
- mFalsingCollector.onCameraOn();
- if (mFalsingCollector.shouldEnforceBouncer()) {
- mCentralSurfaces.executeRunnableDismissingKeyguard(
- () -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null,
- true /* dismissShade */, false /* afterKeyguardGone */,
- true /* deferred */);
- } else {
- mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
- }
- }
- mCentralSurfaces.startLaunchTransitionTimeout();
- mBlockTouches = true;
- }
-
- @Override
- public void onAnimationToSideEnded() {
- mIsLaunchTransitionRunning = false;
- mIsLaunchTransitionFinished = true;
- if (mLaunchAnimationEndRunnable != null) {
- mLaunchAnimationEndRunnable.run();
- mLaunchAnimationEndRunnable = null;
- }
- mCentralSurfaces.readyForKeyguardDone();
- }
-
- @Override
- public float getMaxTranslationDistance() {
- return (float) Math.hypot(mView.getWidth(), getHeight());
- }
-
- @Override
- public void onSwipingStarted(boolean rightIcon) {
- mFalsingCollector.onAffordanceSwipingStarted(rightIcon);
- boolean
- camera =
- mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
- : rightIcon;
- if (camera) {
- mKeyguardBottomArea.bindCameraPrewarmService();
- }
- mView.requestDisallowInterceptTouchEvent(true);
- mOnlyAffordanceInThisMotion = true;
- mQsTracking = false;
- }
-
- @Override
- public void onSwipingAborted() {
- mFalsingCollector.onAffordanceSwipingAborted();
- mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */);
- }
-
- @Override
- public void onIconClicked(boolean rightIcon) {
- if (mHintAnimationRunning) {
- return;
- }
- mHintAnimationRunning = true;
- mAffordanceHelper.startHintAnimation(rightIcon, () -> {
- mHintAnimationRunning = false;
- mCentralSurfaces.onHintFinished();
- });
- rightIcon =
- mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
- : rightIcon;
- if (rightIcon) {
- mCentralSurfaces.onCameraHintStarted();
- } else {
- if (mKeyguardBottomArea.isLeftVoiceAssist()) {
- mCentralSurfaces.onVoiceAssistHintStarted();
- } else {
- mCentralSurfaces.onPhoneHintStarted();
- }
- }
- }
-
- @Override
- public KeyguardAffordanceView getLeftIcon() {
- return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
- ? mKeyguardBottomArea.getRightView() : mKeyguardBottomArea.getLeftView();
- }
-
- @Override
- public KeyguardAffordanceView getRightIcon() {
- return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
- ? mKeyguardBottomArea.getLeftView() : mKeyguardBottomArea.getRightView();
- }
-
- @Override
- public View getLeftPreview() {
- return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
- ? mKeyguardBottomArea.getRightPreview() : mKeyguardBottomArea.getLeftPreview();
- }
-
- @Override
- public View getRightPreview() {
- return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
- ? mKeyguardBottomArea.getLeftPreview() : mKeyguardBottomArea.getRightPreview();
- }
-
- @Override
- public float getAffordanceFalsingFactor() {
- return mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
- }
-
- @Override
- public boolean needsAntiFalsing() {
- return mBarState == KEYGUARD;
- }
- }
-
private class OnEmptySpaceClickListener implements
NotificationStackScrollLayout.OnEmptySpaceClickListener {
@Override
@@ -4684,7 +4462,6 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public void onThemeChanged() {
if (DEBUG_LOGCAT) Log.d(TAG, "onThemeChanged");
- mThemeResId = mView.getContext().getThemeResId();
reInflateViews();
}
@@ -4772,11 +4549,6 @@ public class NotificationPanelViewController extends PanelViewController {
duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
}
mKeyguardStatusBarViewController.animateKeyguardStatusBarOut(startDelay, duration);
- if (mSplitShadeEnabled) {
- // temporary workaround for QS height not being updated during lockscreen to
- // shade transition
- setQsExpanded(true);
- }
updateQSMinHeight();
} else if (oldState == StatusBarState.SHADE_LOCKED
&& statusBarState == KEYGUARD) {
@@ -4839,6 +4611,7 @@ public class NotificationPanelViewController extends PanelViewController {
public interface NotificationPanelViewStateProvider {
/** Returns the expanded height of the panel view. */
float getPanelViewExpandedHeight();
+
/**
* Returns true if heads up should be visible.
*
@@ -4900,7 +4673,7 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public void onViewAttachedToWindow(View v) {
mFragmentService.getFragmentHostManager(mView)
- .addTagListener(QS.TAG, mFragmentListener);
+ .addTagListener(QS.TAG, mFragmentListener);
mStatusBarStateController.addCallback(mStatusBarStateListener);
mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState());
mConfigurationController.addCallback(mConfigurationListener);
@@ -4917,7 +4690,7 @@ public class NotificationPanelViewController extends PanelViewController {
public void onViewDetachedFromWindow(View v) {
unregisterSettingsChangeListener();
mFragmentService.getFragmentHostManager(mView)
- .removeTagListener(QS.TAG, mFragmentListener);
+ .removeTagListener(QS.TAG, mFragmentListener);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mConfigurationController.removeCallback(mConfigurationListener);
mFalsingManager.removeTapListener(mFalsingTapListener);
@@ -4935,7 +4708,7 @@ public class NotificationPanelViewController extends PanelViewController {
setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth());
// Update Clock Pivot
- mKeyguardStatusViewController.setPivotX(mView.getWidth() / 2);
+ mKeyguardStatusViewController.setPivotX(((float) mView.getWidth()) / 2f);
mKeyguardStatusViewController.setPivotY(
(FONT_HEIGHT - CAP_HEIGHT) / 2048f
* mKeyguardStatusViewController.getClockTextSize());
@@ -5002,7 +4775,7 @@ public class NotificationPanelViewController extends PanelViewController {
private final Paint mDebugPaint = new Paint();
@Override
- public void draw(@NonNull Canvas canvas) {
+ public void draw(@androidx.annotation.NonNull @NonNull Canvas canvas) {
mDebugTextUsedYPositions.clear();
mDebugPaint.setColor(Color.RED);
@@ -5047,7 +4820,8 @@ public class NotificationPanelViewController extends PanelViewController {
mDebugPaint.setColor(color);
canvas.drawLine(/* startX= */ 0, /* startY= */ y, /* stopX= */ mView.getWidth(),
/* stopY= */ y, mDebugPaint);
- canvas.drawText(label, /* x= */ 0, /* y= */ computeDebugYTextPosition(y), mDebugPaint);
+ canvas.drawText(label + " = " + y + "px", /* x= */ 0,
+ /* y= */ computeDebugYTextPosition(y), mDebugPaint);
}
private int computeDebugYTextPosition(int lineY) {
@@ -5075,16 +4849,7 @@ public class NotificationPanelViewController extends PanelViewController {
@Override
public int getOpacity() {
- return 0;
- }
- }
-
- private class OnConfigurationChangedListener extends
- PanelViewController.OnConfigurationChangedListener {
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mAffordanceHelper.onConfigurationChanged();
+ return PixelFormat.UNKNOWN;
}
}
@@ -5138,6 +4903,16 @@ public class NotificationPanelViewController extends PanelViewController {
return mStatusBarViewTouchEventHandler;
}
+ @VisibleForTesting
+ StatusBarStateController getStatusBarStateController() {
+ return mStatusBarStateController;
+ }
+
+ @VisibleForTesting
+ boolean isHintAnimationRunning() {
+ return mHintAnimationRunning;
+ }
+
private void onStatusBarWindowStateChanged(@StatusBarManager.WindowVisibleState int state) {
if (state != WINDOW_STATE_SHOWING
&& mStatusBarStateController.getState() == StatusBarState.SHADE) {
@@ -5154,15 +4929,16 @@ public class NotificationPanelViewController extends PanelViewController {
private final ListenerSet<Listener> mListeners = new ListenerSet<>();
@Inject
- PanelEventsEmitter() {}
+ PanelEventsEmitter() {
+ }
@Override
- public void registerListener(@NonNull Listener listener) {
+ public void registerListener(@androidx.annotation.NonNull @NonNull Listener listener) {
mListeners.addIfAbsent(listener);
}
@Override
- public void unregisterListener(@NonNull Listener listener) {
+ public void unregisterListener(@androidx.annotation.NonNull @NonNull Listener listener) {
mListeners.remove(listener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index 1e3a02b0606b..121d69d34678 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.shade;
import static android.view.WindowInsets.Type.systemBars;
@@ -52,6 +52,7 @@ import android.widget.FrameLayout;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
/**
* Combined keyguard and notification panel view. Also holding backdrop and scrims.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index be5b33eb0da0..b8546df75f5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.shade;
import android.app.StatusBarManager;
import android.hardware.display.AmbientDisplayConfiguration;
@@ -46,6 +46,9 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index ebedbf987aa2..13a5615a8b54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -1,4 +1,4 @@
-package com.android.systemui.statusbar.phone
+package com.android.systemui.shade
import android.view.View
import android.view.ViewGroup
@@ -6,11 +6,7 @@ import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowInsets
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
-import androidx.constraintlayout.widget.ConstraintSet.END
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import androidx.constraintlayout.widget.ConstraintSet.START
-import androidx.constraintlayout.widget.ConstraintSet.TOP
+import androidx.constraintlayout.widget.ConstraintSet.*
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index 2446cf7ba412..587e0e6dd834 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.shade;
import android.app.Fragment;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/OWNERS b/packages/SystemUI/src/com/android/systemui/shade/OWNERS
new file mode 100644
index 000000000000..133711ee5ab2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/OWNERS
@@ -0,0 +1,13 @@
+per-file *Notification* = set noparent
+per-file *Notification* = file:../statusbar/notification/OWNERS
+
+per-file NotificationsQuickSettingsContainer.java = kozynski@google.com, asc@google.com
+per-file NotificationsQSContainerController.kt = kozynski@google.com, asc@google.com
+
+per-file NotificationShadeWindowViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com
+per-file NotificationShadeWindowView.java = pixel@google.com, cinek@google.com, juliacr@google.com
+
+per-file NotificationPanelUnfoldAnimationController.kt = alexflo@google.com, jeffdq@google.com, juliacr@google.com
+
+per-file NotificationPanelView.java = pixel@google.com, cinek@google.com, juliacr@google.com, justinweir@google.com
+per-file NotificationPanelViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com, justinweir@google.com \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/NoOpOverScroller.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/NoOpOverScroller.kt
index 2789db874249..f4db3ab9289b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/NoOpOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/NoOpOverScroller.kt
@@ -1,4 +1,4 @@
-package com.android.systemui.statusbar.phone.shade.transition
+package com.android.systemui.shade.transition
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
new file mode 100644
index 000000000000..afd57daca10b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
@@ -0,0 +1,106 @@
+package com.android.systemui.shade.transition
+
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.util.MathUtils.constrain
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent
+import com.android.systemui.statusbar.phone.panelstate.PanelState
+import com.android.systemui.statusbar.phone.panelstate.STATE_OPENING
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.LargeScreenUtils
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/** Controls the scrim properties during the shade expansion transition on non-lockscreen. */
+@SysUISingleton
+class ScrimShadeTransitionController
+@Inject
+constructor(
+ configurationController: ConfigurationController,
+ dumpManager: DumpManager,
+ private val scrimController: ScrimController,
+ @Main private val resources: Resources,
+ private val statusBarStateController: SysuiStatusBarStateController,
+) {
+
+ private var inSplitShade = false
+ private var splitShadeScrimTransitionDistance = 0
+ private var lastExpansionFraction: Float? = null
+ private var lastExpansionEvent: PanelExpansionChangeEvent? = null
+ private var currentPanelState: Int? = null
+
+ init {
+ updateResources()
+ configurationController.addCallback(
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ updateResources()
+ }
+ })
+ dumpManager.registerDumpable(
+ ScrimShadeTransitionController::class.java.simpleName, this::dump)
+ }
+
+ private fun updateResources() {
+ inSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(resources)
+ splitShadeScrimTransitionDistance =
+ resources.getDimensionPixelSize(R.dimen.split_shade_scrim_transition_distance)
+ }
+
+ fun onPanelStateChanged(@PanelState state: Int) {
+ currentPanelState = state
+ onStateChanged()
+ }
+
+ fun onPanelExpansionChanged(panelExpansionChangeEvent: PanelExpansionChangeEvent) {
+ lastExpansionEvent = panelExpansionChangeEvent
+ onStateChanged()
+ }
+
+ private fun onStateChanged() {
+ val expansionEvent = lastExpansionEvent ?: return
+ val panelState = currentPanelState
+ val expansionFraction = calculateScrimExpansionFraction(expansionEvent, panelState)
+ scrimController.setRawPanelExpansionFraction(expansionFraction)
+ lastExpansionFraction = expansionFraction
+ }
+
+ private fun calculateScrimExpansionFraction(
+ expansionEvent: PanelExpansionChangeEvent,
+ @PanelState panelState: Int?
+ ): Float {
+ return if (canUseCustomFraction(panelState)) {
+ constrain(expansionEvent.dragDownPxAmount / splitShadeScrimTransitionDistance, 0f, 1f)
+ } else {
+ expansionEvent.fraction
+ }
+ }
+
+ private fun canUseCustomFraction(panelState: Int?) =
+ inSplitShade && isScreenUnlocked() && panelState == STATE_OPENING
+
+ private fun isScreenUnlocked() =
+ statusBarStateController.currentOrUpcomingState == StatusBarState.SHADE
+
+ private fun dump(printWriter: PrintWriter, args: Array<String>) {
+ printWriter.println(
+ """
+ ScrimShadeTransitionController:
+ Resources:
+ inSplitShade: $inSplitShade
+ isScreenUnlocked: ${isScreenUnlocked()}
+ splitShadeScrimTransitionDistance: $splitShadeScrimTransitionDistance
+ State:
+ currentPanelState: $currentPanelState
+ lastExpansionFraction: $lastExpansionFraction
+ lastExpansionEvent: $lastExpansionEvent
+ """.trimIndent())
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeOverScroller.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeOverScroller.kt
index f1cedeb21e0a..6c3a028c2380 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeOverScroller.kt
@@ -1,4 +1,4 @@
-package com.android.systemui.statusbar.phone.shade.transition
+package com.android.systemui.shade.transition
import com.android.systemui.statusbar.phone.panelstate.PanelState
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt
index 2762b9a38e92..58acfb40ee44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt
@@ -1,16 +1,21 @@
-package com.android.systemui.statusbar.phone.shade.transition
+package com.android.systemui.shade.transition
import android.content.Context
import android.content.res.Configuration
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
+import com.android.systemui.shade.NotificationPanelViewController
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.phone.NotificationPanelViewController
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
import com.android.systemui.statusbar.phone.panelstate.PanelState
+import com.android.systemui.statusbar.phone.panelstate.panelStateToString
import com.android.systemui.statusbar.policy.ConfigurationController
+import java.io.PrintWriter
import javax.inject.Inject
/** Controls the shade expansion transition on non-lockscreen. */
@@ -20,9 +25,12 @@ class ShadeTransitionController
constructor(
configurationController: ConfigurationController,
panelExpansionStateManager: PanelExpansionStateManager,
+ dumpManager: DumpManager,
private val context: Context,
private val splitShadeOverScrollerFactory: SplitShadeOverScroller.Factory,
- private val noOpOverScroller: NoOpOverScroller
+ private val noOpOverScroller: NoOpOverScroller,
+ private val scrimShadeTransitionController: ScrimShadeTransitionController,
+ private val statusBarStateController: SysuiStatusBarStateController,
) {
lateinit var notificationPanelViewController: NotificationPanelViewController
@@ -30,13 +38,15 @@ constructor(
lateinit var qs: QS
private var inSplitShade = false
+ private var currentPanelState: Int? = null
+ private var lastPanelExpansionChangeEvent: PanelExpansionChangeEvent? = null
private val splitShadeOverScroller by lazy {
- splitShadeOverScrollerFactory.create(qs, notificationStackScrollLayoutController)
+ splitShadeOverScrollerFactory.create({ qs }, { notificationStackScrollLayoutController })
}
private val shadeOverScroller: ShadeOverScroller
get() =
- if (inSplitShade && propertiesInitialized()) {
+ if (inSplitShade && isScreenUnlocked() && propertiesInitialized()) {
splitShadeOverScroller
} else {
noOpOverScroller
@@ -52,6 +62,9 @@ constructor(
})
panelExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged)
panelExpansionStateManager.addStateListener(this::onPanelStateChanged)
+ dumpManager.registerDumpable("ShadeTransitionController") { printWriter, _ ->
+ dump(printWriter)
+ }
}
private fun updateResources() {
@@ -59,15 +72,36 @@ constructor(
}
private fun onPanelStateChanged(@PanelState state: Int) {
+ currentPanelState = state
shadeOverScroller.onPanelStateChanged(state)
+ scrimShadeTransitionController.onPanelStateChanged(state)
}
private fun onPanelExpansionChanged(event: PanelExpansionChangeEvent) {
+ lastPanelExpansionChangeEvent = event
shadeOverScroller.onDragDownAmountChanged(event.dragDownPxAmount)
+ scrimShadeTransitionController.onPanelExpansionChanged(event)
}
private fun propertiesInitialized() =
this::qs.isInitialized &&
this::notificationPanelViewController.isInitialized &&
this::notificationStackScrollLayoutController.isInitialized
+
+ private fun dump(pw: PrintWriter) {
+ pw.println(
+ """
+ ShadeTransitionController:
+ inSplitShade: $inSplitShade
+ isScreenUnlocked: ${isScreenUnlocked()}
+ currentPanelState: ${currentPanelState?.panelStateToString()}
+ lastPanelExpansionChangeEvent: $lastPanelExpansionChangeEvent
+ qs.isInitialized: ${this::qs.isInitialized}
+ npvc.isInitialized: ${this::notificationPanelViewController.isInitialized}
+ nssl.isInitialized: ${this::notificationStackScrollLayoutController.isInitialized}
+ """.trimIndent())
+ }
+
+ private fun isScreenUnlocked() =
+ statusBarStateController.currentOrUpcomingState == StatusBarState.SHADE
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt
index 71050f2e7c67..204dd3c07d8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt
@@ -1,4 +1,4 @@
-package com.android.systemui.statusbar.phone.shade.transition
+package com.android.systemui.shade.transition
import android.animation.Animator
import android.animation.ValueAnimator
@@ -28,8 +28,8 @@ constructor(
dumpManager: DumpManager,
private val context: Context,
private val scrimController: ScrimController,
- @Assisted private val qS: QS,
- @Assisted private val nsslController: NotificationStackScrollLayoutController
+ @Assisted private val qSProvider: () -> QS,
+ @Assisted private val nsslControllerProvider: () -> NotificationStackScrollLayoutController
) : ShadeOverScroller {
private var releaseOverScrollDuration = 0L
@@ -39,6 +39,12 @@ constructor(
@PanelState private var panelState: Int = STATE_CLOSED
private var releaseOverScrollAnimator: Animator? = null
+ private val qS: QS
+ get() = qSProvider()
+
+ private val nsslController: NotificationStackScrollLayoutController
+ get() = nsslControllerProvider()
+
init {
updateResources()
configurationController.addCallback(
@@ -47,7 +53,9 @@ constructor(
updateResources()
}
})
- dumpManager.registerDumpable(this::dump)
+ dumpManager.registerDumpable("SplitShadeOverScroller") { printWriter, _ ->
+ dump(printWriter)
+ }
}
private fun updateResources() {
@@ -118,7 +126,7 @@ constructor(
releaseOverScrollAnimator = null
}
- private fun dump(pw: PrintWriter, strings: Array<String>) {
+ private fun dump(pw: PrintWriter) {
pw.println(
"""
SplitShadeOverScroller:
@@ -135,8 +143,8 @@ constructor(
@AssistedFactory
fun interface Factory {
fun create(
- qS: QS,
- nsslController: NotificationStackScrollLayoutController
+ qSProvider: () -> QS,
+ nsslControllerProvider: () -> NotificationStackScrollLayoutController
): SplitShadeOverScroller
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
index d3ae198e8e35..236ba1f92d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
@@ -56,6 +56,8 @@ interface SmartspaceViewComponent {
):
BcSmartspaceDataPlugin.SmartspaceView {
val ssView = plugin.getView(parent)
+ // Currently, this is only used to provide SmartspaceView on Dream surface.
+ ssView.setIsDreaming(true)
ssView.registerDataProvider(plugin)
ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
@@ -81,4 +83,4 @@ interface SmartspaceViewComponent {
return ssView
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 6cfbb43fa25a..07455a02b2cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -66,7 +66,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
* @param entry entry to show
*/
public void showNotification(@NonNull NotificationEntry entry) {
- mLogger.logShowNotification(entry.getKey());
+ mLogger.logShowNotification(entry);
addAlertEntry(entry);
updateNotification(entry.getKey(), true /* alert */);
entry.setInterruption();
@@ -320,7 +320,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
* @param updatePostTime whether or not to refresh the post time
*/
public void updateEntry(boolean updatePostTime) {
- mLogger.logUpdateEntry(mEntry.getKey(), updatePostTime);
+ mLogger.logUpdateEntry(mEntry, updatePostTime);
long currentTime = mClock.currentTimeMillis();
mEarliestRemovaltime = currentTime + mMinimumDisplayTime;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index cc3121ddc0e9..e99244048fd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -66,6 +66,7 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IAddTileResultCallback;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.IUndoMediaTransferCallback;
+import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.GcUtils;
import com.android.internal.view.AppearanceRegion;
@@ -361,7 +362,7 @@ public class CommandQueue extends IStatusBar.Stub implements
default void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
@Behavior int behavior, InsetsVisibilities requestedVisibilities,
- String packageName) { }
+ String packageName, LetterboxDetails[] letterboxDetails) { }
/**
* @see IStatusBar#showTransient(int, int[], boolean).
@@ -1090,7 +1091,8 @@ public class CommandQueue extends IStatusBar.Stub implements
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName) {
+ @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName,
+ LetterboxDetails[] letterboxDetails) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.argi1 = displayId;
@@ -1100,6 +1102,7 @@ public class CommandQueue extends IStatusBar.Stub implements
args.argi4 = behavior;
args.arg2 = requestedVisibilities;
args.arg3 = packageName;
+ args.arg4 = letterboxDetails;
mHandler.obtainMessage(MSG_SYSTEM_BAR_CHANGED, args).sendToTarget();
}
}
@@ -1561,7 +1564,8 @@ public class CommandQueue extends IStatusBar.Stub implements
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onSystemBarAttributesChanged(args.argi1, args.argi2,
(AppearanceRegion[]) args.arg1, args.argi3 == 1, args.argi4,
- (InsetsVisibilities) args.arg2, (String) args.arg3);
+ (InsetsVisibilities) args.arg2, (String) args.arg3,
+ (LetterboxDetails[]) args.arg4);
}
args.recycle();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 9e029095ea6b..ca147286a301 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -900,16 +900,36 @@ public class KeyguardIndicationController {
mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
}
} else {
- if (!mAccessibilityManager.isEnabled()
- && !mAccessibilityManager.isTouchExplorationEnabled()
- && mKeyguardUpdateMonitor.isUdfpsSupported()
- && mKeyguardUpdateMonitor.getUserCanSkipBouncer(
- KeyguardUpdateMonitor.getCurrentUser())) {
- final int stringId = mKeyguardUpdateMonitor.getIsFaceAuthenticated()
- ? R.string.keyguard_face_successful_unlock_press
- : R.string.keyguard_unlock_press;
- showBiometricMessage(mContext.getString(stringId));
+ final boolean canSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (canSkipBouncer) {
+ final boolean faceAuthenticated = mKeyguardUpdateMonitor.getIsFaceAuthenticated();
+ final boolean udfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported();
+ final boolean a11yEnabled = mAccessibilityManager.isEnabled()
+ || mAccessibilityManager.isTouchExplorationEnabled();
+ if (udfpsSupported && faceAuthenticated) { // co-ex
+ if (a11yEnabled) {
+ showBiometricMessage(mContext.getString(
+ R.string.keyguard_face_successful_unlock_swipe));
+ } else {
+ showBiometricMessage(mContext.getString(
+ R.string.keyguard_face_successful_unlock_press));
+ }
+ } else if (faceAuthenticated) { // face-only
+ showBiometricMessage(mContext.getString(
+ R.string.keyguard_face_successful_unlock_swipe));
+ } else if (udfpsSupported) { // udfps-only
+ if (a11yEnabled) {
+ showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+ } else {
+ showBiometricMessage(mContext.getString(
+ R.string.keyguard_unlock_press));
+ }
+ } else { // no security or unlocked by a trust agent
+ showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+ }
} else {
+ // suggest swiping up for the primary authentication bouncer
showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
index 01eb4446ea8e..886ad684649f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
@@ -6,7 +6,7 @@ import android.util.MathUtils
import com.android.systemui.R
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.MediaHierarchyManager
-import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.statusbar.policy.ConfigurationController
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index c1ea6bf7cec8..74d8f1beaec1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -29,6 +29,7 @@ import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
@@ -37,7 +38,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
-import com.android.systemui.statusbar.phone.NotificationPanelViewController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.LargeScreenUtils
import java.io.PrintWriter
@@ -178,7 +178,7 @@ class LockscreenShadeTransitionController @Inject constructor(
val touchHelper = DragDownHelper(falsingManager, falsingCollector, this, context)
private val splitShadeOverScroller: SplitShadeLockScreenOverScroller by lazy {
- splitShadeOverScrollerFactory.create(qS, nsslController)
+ splitShadeOverScrollerFactory.create({ qS }, { nsslController })
}
private val phoneShadeOverScroller: SingleShadeLockScreenOverScroller by lazy {
@@ -911,4 +911,4 @@ class DragDownHelper(
host.getLocationOnScreen(temp2)
return expandCallback.getChildAtRawPosition(x + temp2[0], y + temp2[1])
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 1ce05ec7e7ad..c900c5a2ff0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -36,7 +36,6 @@ import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.AsyncTask;
import android.os.Trace;
-import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
@@ -44,7 +43,6 @@ import android.util.Log;
import android.view.View;
import android.widget.ImageView;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.Interpolators;
@@ -56,9 +54,6 @@ import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.SmartspaceMediaData;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -112,11 +107,9 @@ public class NotificationMediaManager implements Dumpable {
}
private final NotificationVisibilityProvider mVisibilityProvider;
- private final NotificationEntryManager mEntryManager;
private final MediaDataManager mMediaDataManager;
private final NotifPipeline mNotifPipeline;
private final NotifCollection mNotifCollection;
- private final boolean mUsingNotifPipeline;
@Nullable
private Lazy<NotificationShadeWindowController> mNotificationShadeWindowController;
@@ -180,12 +173,10 @@ public class NotificationMediaManager implements Dumpable {
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationVisibilityProvider visibilityProvider,
- NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
KeyguardBypassController keyguardBypassController,
NotifPipeline notifPipeline,
NotifCollection notifCollection,
- NotifPipelineFlags notifPipelineFlags,
@Main DelayableExecutor mainExecutor,
MediaDataManager mediaDataManager,
DumpManager dumpManager) {
@@ -197,19 +188,12 @@ public class NotificationMediaManager implements Dumpable {
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mNotificationShadeWindowController = notificationShadeWindowController;
mVisibilityProvider = visibilityProvider;
- mEntryManager = notificationEntryManager;
mMainExecutor = mainExecutor;
mMediaDataManager = mediaDataManager;
mNotifPipeline = notifPipeline;
mNotifCollection = notifCollection;
- if (!notifPipelineFlags.isNewPipelineEnabled()) {
- setupNEM();
- mUsingNotifPipeline = false;
- } else {
- setupNotifPipeline();
- mUsingNotifPipeline = true;
- }
+ setupNotifPipeline();
dumpManager.registerDumpable(this);
}
@@ -273,79 +257,6 @@ public class NotificationMediaManager implements Dumpable {
});
}
- private void setupNEM() {
- mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
-
- @Override
- public void onPendingEntryAdded(NotificationEntry entry) {
- mMediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn());
- }
-
- @Override
- public void onPreEntryUpdated(NotificationEntry entry) {
- mMediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn());
- }
-
- @Override
- public void onEntryInflated(NotificationEntry entry) {
- findAndUpdateMediaNotifications();
- }
-
- @Override
- public void onEntryReinflated(NotificationEntry entry) {
- findAndUpdateMediaNotifications();
- }
-
- @Override
- public void onEntryRemoved(
- @NonNull NotificationEntry entry,
- @Nullable NotificationVisibility visibility,
- boolean removedByUser,
- int reason) {
- removeEntry(entry);
- }
- });
-
- // Pending entries are never inflated, and will never generate a call to onEntryRemoved().
- // This can happen when notifications are added and canceled before inflation. Add this
- // separate listener for cleanup, since media inflation occurs onPendingEntryAdded().
- mEntryManager.addCollectionListener(new NotifCollectionListener() {
- @Override
- public void onEntryCleanUp(@NonNull NotificationEntry entry) {
- removeEntry(entry);
- }
- });
-
- mMediaDataManager.addListener(new MediaDataManager.Listener() {
- @Override
- public void onMediaDataLoaded(@NonNull String key,
- @Nullable String oldKey, @NonNull MediaData data, boolean immediately,
- int receivedSmartspaceCardLatency, boolean isSsReactivated) {
- }
-
- @Override
- public void onSmartspaceMediaDataLoaded(@NonNull String key,
- @NonNull SmartspaceMediaData data, boolean shouldPrioritize) {
-
- }
-
- @Override
- public void onMediaDataRemoved(@NonNull String key) {
- NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(key);
- if (entry != null) {
- // TODO(b/160713608): "removing" this notification won't happen and
- // won't send the 'deleteIntent' if the notification is ongoing.
- mEntryManager.performRemoveNotification(entry.getSbn(),
- getDismissedByUserStats(entry),
- NotificationListenerService.REASON_CANCEL);
- }
- }
-
- @Override
- public void onSmartspaceMediaDataRemoved(@NonNull String key, boolean immediately) {}
- });
- }
-
private DismissedByUserStats getDismissedByUserStats(NotificationEntry entry) {
return new DismissedByUserStats(
NotificationStats.DISMISSAL_SHADE, // Add DISMISSAL_MEDIA?
@@ -401,22 +312,10 @@ public class NotificationMediaManager implements Dumpable {
if (mMediaNotificationKey == null) {
return null;
}
- if (mUsingNotifPipeline) {
- return Optional.ofNullable(mNotifPipeline.getEntry(mMediaNotificationKey))
- .map(entry -> entry.getIcons().getShelfIcon())
- .map(StatusBarIconView::getSourceIcon)
- .orElse(null);
- } else {
- synchronized (mEntryManager) {
- NotificationEntry entry = mEntryManager
- .getActiveNotificationUnfiltered(mMediaNotificationKey);
- if (entry == null || entry.getIcons().getShelfIcon() == null) {
- return null;
- }
-
- return entry.getIcons().getShelfIcon().getSourceIcon();
- }
- }
+ return Optional.ofNullable(mNotifPipeline.getEntry(mMediaNotificationKey))
+ .map(entry -> entry.getIcons().getShelfIcon())
+ .map(StatusBarIconView::getSourceIcon)
+ .orElse(null);
}
public void addCallback(MediaListener callback) {
@@ -431,21 +330,9 @@ public class NotificationMediaManager implements Dumpable {
public void findAndUpdateMediaNotifications() {
boolean metaDataChanged;
- if (mUsingNotifPipeline) {
- // TODO(b/169655907): get the semi-filtered notifications for current user
- Collection<NotificationEntry> allNotifications = mNotifPipeline.getAllNotifs();
- metaDataChanged = findPlayingMediaNotification(allNotifications);
- } else {
- synchronized (mEntryManager) {
- Collection<NotificationEntry> allNotifications = mEntryManager.getAllNotifs();
- metaDataChanged = findPlayingMediaNotification(allNotifications);
- }
-
- if (metaDataChanged) {
- mEntryManager.updateNotifications("NotificationMediaManager - metaDataChanged");
- }
-
- }
+ // TODO(b/169655907): get the semi-filtered notifications for current user
+ Collection<NotificationEntry> allNotifications = mNotifPipeline.getAllNotifs();
+ metaDataChanged = findPlayingMediaNotification(allNotifications);
dispatchUpdateMediaMetaData(metaDataChanged, true /* allowEnterAnimation */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index d71dec8ff396..78b3b0a65351 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -68,6 +68,7 @@ import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.util.DumpUtilsKt;
+import com.android.systemui.util.ListenerSet;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -75,6 +76,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.function.Consumer;
import dagger.Lazy;
@@ -118,6 +120,8 @@ public class NotificationRemoteInputManager implements Dumpable {
protected Callback mCallback;
private final List<RemoteInputController.Callback> mControllerCallbacks = new ArrayList<>();
+ private final ListenerSet<Consumer<NotificationEntry>> mActionPressListeners =
+ new ListenerSet<>();
private final InteractionHandler mInteractionHandler = new InteractionHandler() {
@@ -282,10 +286,6 @@ public class NotificationRemoteInputManager implements Dumpable {
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mRebuilder = rebuilder;
- if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
- mRemoteInputListener = createLegacyRemoteInputLifetimeExtender(mainHandler,
- notificationEntryManager, smartReplyController);
- }
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mStatusBarStateController = statusBarStateController;
mRemoteInputUriController = remoteInputUriController;
@@ -309,34 +309,19 @@ public class NotificationRemoteInputManager implements Dumpable {
int reason) {
// We're removing the notification, the smart controller can forget about it.
mSmartReplyController.stopSending(entry);
-
- if (removedByUser && entry != null) {
- onPerformRemoveNotification(entry, entry.getKey());
- }
}
});
}
/** Add a listener for various remote input events. Works with NEW pipeline only. */
public void setRemoteInputListener(@NonNull RemoteInputListener remoteInputListener) {
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
- if (mRemoteInputListener != null) {
- throw new IllegalStateException("mRemoteInputListener is already set");
- }
- mRemoteInputListener = remoteInputListener;
- if (mRemoteInputController != null) {
- mRemoteInputListener.setRemoteInputController(mRemoteInputController);
- }
+ if (mRemoteInputListener != null) {
+ throw new IllegalStateException("mRemoteInputListener is already set");
+ }
+ mRemoteInputListener = remoteInputListener;
+ if (mRemoteInputController != null) {
+ mRemoteInputListener.setRemoteInputController(mRemoteInputController);
}
- }
-
- @NonNull
- @VisibleForTesting
- protected LegacyRemoteInputLifetimeExtender createLegacyRemoteInputLifetimeExtender(
- Handler mainHandler,
- NotificationEntryManager notificationEntryManager,
- SmartReplyController smartReplyController) {
- return new LegacyRemoteInputLifetimeExtender();
}
/** Initializes this component with the provided dependencies. */
@@ -377,12 +362,6 @@ public class NotificationRemoteInputManager implements Dumpable {
}
}
});
- if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
- mSmartReplyController.setCallback((entry, reply) -> {
- StatusBarNotification newSbn = mRebuilder.rebuildForSendingSmartReply(entry, reply);
- mEntryManager.updateNotification(newSbn, null /* ranking */);
- });
- }
}
public void addControllerCallback(RemoteInputController.Callback callback) {
@@ -401,6 +380,14 @@ public class NotificationRemoteInputManager implements Dumpable {
}
}
+ public void addActionPressListener(Consumer<NotificationEntry> listener) {
+ mActionPressListeners.addIfAbsent(listener);
+ }
+
+ public void removeActionPressListener(Consumer<NotificationEntry> listener) {
+ mActionPressListeners.remove(listener);
+ }
+
/**
* Activates a given {@link RemoteInput}
*
@@ -576,19 +563,6 @@ public class NotificationRemoteInputManager implements Dumpable {
return v.findViewWithTag(RemoteInputView.VIEW_TAG);
}
- public ArrayList<NotificationLifetimeExtender> getLifetimeExtenders() {
- // OLD pipeline code ONLY; can assume implementation
- return ((LegacyRemoteInputLifetimeExtender) mRemoteInputListener).mLifetimeExtenders;
- }
-
- @VisibleForTesting
- void onPerformRemoveNotification(NotificationEntry entry, final String key) {
- // OLD pipeline code ONLY; can assume implementation
- ((LegacyRemoteInputLifetimeExtender) mRemoteInputListener)
- .mKeysKeptForRemoteInputHistory.remove(key);
- cleanUpRemoteInputForUserRemoval(entry);
- }
-
/**
* Disable remote input on the entry and remove the remote input view.
* This should be called when a user dismisses a notification that won't be lifetime extended.
@@ -634,6 +608,9 @@ public class NotificationRemoteInputManager implements Dumpable {
if (mRemoteInputListener != null) {
mRemoteInputListener.releaseNotificationIfKeptForRemoteInputHistory(entry);
}
+ for (Consumer<NotificationEntry> listener : mActionPressListeners) {
+ listener.accept(entry);
+ }
}
/** Returns whether the notification should be lifetime extended for smart reply history */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 0d604014e8f1..221428747558 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -229,6 +229,17 @@ public class NotificationShelf extends ActivatableNotificationView implements
mActualWidth = actualWidth;
}
+ @Override
+ public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
+ super.getBoundsOnScreen(outRect, clipToParent);
+ final int actualWidth = getActualWidth();
+ if (isLayoutRtl()) {
+ outRect.left = outRect.right - actualWidth;
+ } else {
+ outRect.right = outRect.left + actualWidth;
+ }
+ }
+
/**
* @return Actual width of shelf, accounting for possible ongoing width animation
*/
@@ -387,7 +398,14 @@ public class NotificationShelf extends ActivatableNotificationView implements
if (child instanceof ActivatableNotificationView) {
ActivatableNotificationView anv =
(ActivatableNotificationView) child;
- updateCornerRoundnessOnScroll(anv, viewStart, shelfStart);
+ // Because we show whole notifications on the lockscreen, the bottom notification is
+ // always "just about to enter the shelf" by normal scrolling rules. This is fine
+ // if the shelf is visible, but if the shelf is hidden, it causes incorrect curling.
+ // notificationClipEnd handles the discrepancy between a visible and hidden shelf,
+ // so we use that when on the keyguard (and while animating away) to reduce curling.
+ final float keyguardSafeShelfStart =
+ mAmbientState.isOnKeyguard() ? notificationClipEnd : shelfStart;
+ updateCornerRoundnessOnScroll(anv, viewStart, keyguardSafeShelfStart);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
index 96ce6b45dc71..13d8adb079de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
@@ -9,6 +9,7 @@ import android.view.animation.PathInterpolator
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.ScrimController
@@ -16,16 +17,18 @@ import com.android.systemui.statusbar.policy.ConfigurationController
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import java.io.PrintWriter
class SplitShadeLockScreenOverScroller
@AssistedInject
constructor(
configurationController: ConfigurationController,
+ dumpManager: DumpManager,
private val context: Context,
private val scrimController: ScrimController,
private val statusBarStateController: SysuiStatusBarStateController,
- @Assisted private val qS: QS,
- @Assisted private val nsslController: NotificationStackScrollLayoutController
+ @Assisted private val qSProvider: () -> QS,
+ @Assisted private val nsslControllerProvider: () -> NotificationStackScrollLayoutController
) : LockScreenShadeOverScroller {
private var releaseOverScrollAnimator: Animator? = null
@@ -34,6 +37,12 @@ constructor(
private var maxOverScrollAmount = 0
private var previousOverscrollAmount = 0
+ private val qS: QS
+ get() = qSProvider()
+
+ private val nsslController: NotificationStackScrollLayoutController
+ get() = nsslControllerProvider()
+
init {
updateResources()
configurationController.addCallback(
@@ -42,6 +51,9 @@ constructor(
updateResources()
}
})
+ dumpManager.registerDumpable("SplitShadeLockscreenOverScroller") { printWriter, _ ->
+ dump(printWriter)
+ }
}
private fun updateResources() {
@@ -114,11 +126,25 @@ constructor(
releaseOverScrollAnimator = null
}
+ private fun dump(printWriter: PrintWriter) {
+ printWriter.println(
+ """
+ SplitShadeLockScreenOverScroller:
+ Resources:
+ transitionToFullShadeDistance: $transitionToFullShadeDistance
+ maxOverScrollAmount: $maxOverScrollAmount
+ releaseOverScrollDuration: $releaseOverScrollDuration
+ State:
+ previousOverscrollAmount: $previousOverscrollAmount
+ expansionDragDownAmount: $expansionDragDownAmount
+ """.trimIndent())
+ }
+
@AssistedFactory
fun interface Factory {
fun create(
- qS: QS,
- nsslController: NotificationStackScrollLayoutController
+ qSProvider: () -> QS,
+ nsslControllerProvider: () -> NotificationStackScrollLayoutController
): SplitShadeLockScreenOverScroller
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
index 718bc5caf414..f78b067279bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
@@ -22,18 +22,21 @@ package com.android.systemui.statusbar;
public class StatusBarState {
/**
- * The status bar is in the "normal" shade mode.
+ * The status bar is in the "normal", unlocked mode or the device is still locked but we're
+ * accessing camera from power button double-tap shortcut.
*/
public static final int SHADE = 0;
/**
- * Status bar is currently the Keyguard.
+ * Status bar is currently the Keyguard. In single column mode, when you swipe from the top of
+ * the keyguard to expand QS immediately, it's still KEYGUARD state.
*/
public static final int KEYGUARD = 1;
/**
- * Status bar is in the special mode, where it is fully interactive but still locked. So
- * dismissing the shade will still show the bouncer.
+ * Status bar is in the special mode, where it was transitioned from lockscreen to shade.
+ * Depending on user's security settings, dismissing the shade will either show the
+ * bouncer or go directly to unlocked {@link #SHADE} mode.
*/
public static final int SHADE_LOCKED = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index d49e1e6acc23..a1dc7b41d42b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -73,6 +73,9 @@ import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.LogLevel;
+import com.android.systemui.log.dagger.StatusBarNetworkControllerLog;
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -94,9 +97,12 @@ import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
import javax.inject.Inject;
+import kotlin.Unit;
+
/** Platform implementation of the network controller. **/
@SysUISingleton
public class NetworkControllerImpl extends BroadcastReceiver
@@ -133,6 +139,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
private final CarrierConfigTracker mCarrierConfigTracker;
private final FeatureFlags mFeatureFlags;
private final DumpManager mDumpManager;
+ private final LogBuffer mLogBuffer;
private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -231,7 +238,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
@Main Handler handler,
InternetDialogFactory internetDialogFactory,
FeatureFlags featureFlags,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ @StatusBarNetworkControllerLog LogBuffer logBuffer) {
this(context, connectivityManager,
telephonyManager,
telephonyListenerManager,
@@ -251,7 +259,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
trackerFactory,
handler,
featureFlags,
- dumpManager);
+ dumpManager,
+ logBuffer);
mReceiverHandler.post(mRegisterListeners);
mInternetDialogFactory = internetDialogFactory;
}
@@ -276,7 +285,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
WifiStatusTrackerFactory trackerFactory,
@Main Handler handler,
FeatureFlags featureFlags,
- DumpManager dumpManager
+ DumpManager dumpManager,
+ LogBuffer logBuffer
) {
mContext = context;
mTelephonyListenerManager = telephonyListenerManager;
@@ -297,6 +307,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
mCarrierConfigTracker = carrierConfigTracker;
mFeatureFlags = featureFlags;
mDumpManager = dumpManager;
+ mLogBuffer = logBuffer;
// telephony
mPhone = telephonyManager;
@@ -770,6 +781,17 @@ public class NetworkControllerImpl extends BroadcastReceiver
Log.d(TAG, "onReceive: intent=" + intent);
}
final String action = intent.getAction();
+ mLogBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ logMessage -> {
+ logMessage.setStr1(action);
+ return Unit.INSTANCE;
+ },
+ logMessage -> String.format(
+ Locale.US,
+ "Received broadcast with action \"%s\"",
+ logMessage.getStr1()));
switch (action) {
case ConnectivityManager.CONNECTIVITY_ACTION:
updateConnectivity();
@@ -937,6 +959,12 @@ public class NetworkControllerImpl extends BroadcastReceiver
: lhs.getSimSlotIndex() - rhs.getSimSlotIndex();
}
});
+ Log.i(
+ TAG,
+ String.format(
+ Locale.US,
+ "Subscriptions changed: %s",
+ createSubscriptionChangeStatement(mCurrentSubscriptions, subscriptions)));
mCurrentSubscriptions = subscriptions;
SparseArray<MobileSignalController> cachedControllers =
@@ -1154,6 +1182,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
pw.print(" hasVoiceCallingFeature()=");
pw.println(hasVoiceCallingFeature());
pw.println(" mListening=" + mListening);
+ pw.println(" mActiveMobileDataSubscription=" + mActiveMobileDataSubscription);
pw.println(" - connectivity ------");
pw.print(" mConnectedTransports=");
@@ -1473,4 +1502,23 @@ public class NetworkControllerImpl extends BroadcastReceiver
* get created will also run on the BG Looper.
*/
private final Runnable mRegisterListeners = () -> registerListeners();
+
+ /** Returns a logging statement for the given old and new list of {@link SubscriptionInfo} */
+ private static String createSubscriptionChangeStatement(
+ final @Nullable List<SubscriptionInfo> oldSubscriptions,
+ final @Nullable List<SubscriptionInfo> newSubscriptions) {
+ return String.format(
+ Locale.US,
+ "old=%s, new=%s",
+ toSubscriptionIds(oldSubscriptions),
+ toSubscriptionIds(newSubscriptions));
+ }
+
+ /** Returns to a list of subscription IDs for the given list of {@link SubscriptionInfo} */
+ @Nullable
+ private static List<Integer> toSubscriptionIds(
+ final @Nullable List<SubscriptionInfo> subscriptions) {
+ return subscriptions != null ? subscriptions.stream().map(
+ SubscriptionInfo::getSubscriptionId).collect(Collectors.toList()) : null;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 29411e63b163..9e77dbc08c7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.os.Handler;
import android.service.dreams.IDreamManager;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.statusbar.IStatusBarService;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -67,6 +68,7 @@ import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBarIconList;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags;
@@ -138,12 +140,10 @@ public interface CentralSurfacesDependenciesModule {
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationVisibilityProvider visibilityProvider,
- NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
KeyguardBypassController keyguardBypassController,
NotifPipeline notifPipeline,
NotifCollection notifCollection,
- NotifPipelineFlags notifPipelineFlags,
@Main DelayableExecutor mainExecutor,
MediaDataManager mediaDataManager,
DumpManager dumpManager) {
@@ -152,12 +152,10 @@ public interface CentralSurfacesDependenciesModule {
centralSurfacesOptionalLazy,
notificationShadeWindowController,
visibilityProvider,
- notificationEntryManager,
mediaArtworkProcessor,
keyguardBypassController,
notifPipeline,
notifCollection,
- notifPipelineFlags,
mainExecutor,
mediaDataManager,
dumpManager);
@@ -259,6 +257,16 @@ public interface CentralSurfacesDependenciesModule {
*/
@Provides
@SysUISingleton
+ static StatusBarIconList provideStatusBarIconList(Context context) {
+ return new StatusBarIconList(
+ context.getResources().getStringArray(
+ com.android.internal.R.array.config_statusBarIcons));
+ }
+
+ /**
+ */
+ @Provides
+ @SysUISingleton
static OngoingCallController provideOngoingCallController(
Context context,
CommonNotifCollection notifCollection,
@@ -317,7 +325,8 @@ public interface CentralSurfacesDependenciesModule {
*/
@Provides
@SysUISingleton
- static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager) {
- return new DialogLaunchAnimator(dreamManager);
+ static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager,
+ InteractionJankMonitor interactionJankMonitor) {
+ return new DialogLaunchAnimator(dreamManager, interactionJankMonitor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index a35bced819c0..a509fcaad4ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -37,6 +37,8 @@ import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
@@ -44,13 +46,11 @@ import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.util.concurrency.Execution
import com.android.systemui.util.settings.SecureSettings
-import java.lang.RuntimeException
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -71,6 +71,7 @@ class LockscreenSmartspaceController @Inject constructor(
private val configurationController: ConfigurationController,
private val statusBarStateController: StatusBarStateController,
private val deviceProvisionedController: DeviceProvisionedController,
+ private val bypassController: KeyguardBypassController,
private val execution: Execution,
@Main private val uiExecutor: Executor,
@Main private val handler: Handler,
@@ -154,6 +155,13 @@ class LockscreenSmartspaceController @Inject constructor(
}
}
+ private val bypassStateChangedListener =
+ object : KeyguardBypassController.OnBypassStateChangedListener {
+ override fun onBypassStateChanged(isEnabled: Boolean) {
+ updateBypassEnabled()
+ }
+ }
+
init {
deviceProvisionedController.addCallback(deviceProvisionedListener)
}
@@ -164,6 +172,11 @@ class LockscreenSmartspaceController @Inject constructor(
return featureFlags.isEnabled(Flags.SMARTSPACE) && plugin != null
}
+ private fun updateBypassEnabled() {
+ val bypassEnabled = bypassController.bypassEnabled
+ smartspaceViews.forEach { it.setKeyguardBypassEnabled(bypassEnabled) }
+ }
+
/**
* Constructs the smartspace view and connects it to the smartspace service.
*/
@@ -211,6 +224,7 @@ class LockscreenSmartspaceController @Inject constructor(
}
})
ssView.setFalsingManager(falsingManager)
+ ssView.setKeyguardBypassEnabled(bypassController.bypassEnabled)
return (ssView as View).apply { addOnAttachStateChangeListener(stateChangeListener) }
}
@@ -248,11 +262,13 @@ class LockscreenSmartspaceController @Inject constructor(
)
configurationController.addCallback(configChangeListener)
statusBarStateController.addCallback(statusBarStateListener)
+ bypassController.registerOnBypassStateChangedListener(bypassStateChangedListener)
plugin.registerSmartspaceEventNotifier {
e -> session?.notifySmartspaceEvent(e)
}
+ updateBypassEnabled()
reloadSmartspace()
}
@@ -276,6 +292,7 @@ class LockscreenSmartspaceController @Inject constructor(
contentResolver.unregisterContentObserver(settingsObserver)
configurationController.removeCallback(configChangeListener)
statusBarStateController.removeCallback(statusBarStateListener)
+ bypassController.unregisterOnBypassStateChangedListener(bypassStateChangedListener)
session = null
plugin?.registerSmartspaceEventNotifier(null)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 5aeab84b677c..d24896148095 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -31,7 +31,6 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -129,11 +128,9 @@ class AnimatedImageNotificationManager @Inject constructor(
*/
@SysUISingleton
class ConversationNotificationManager @Inject constructor(
- private val bindEventManager: BindEventManager,
- private val notificationGroupManager: NotificationGroupManagerLegacy,
+ bindEventManager: BindEventManager,
private val context: Context,
private val notifCollection: CommonNotifCollection,
- private val featureFlags: NotifPipelineFlags,
@Main private val mainHandler: Handler
) {
// Need this state to be thread safe, since it's accessed from the ui thread
@@ -172,12 +169,10 @@ class ConversationNotificationManager @Inject constructor(
layout.setIsImportantConversation(important, false)
}
}
- if (changed && !featureFlags.isNewPipelineEnabled()) {
- notificationGroupManager.updateIsolation(entry)
- }
}
}
}
+
fun onEntryViewBound(entry: NotificationEntry) {
if (!entry.ranking.isConversation) {
return
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
index a0ccd5726c75..1be4c04ef804 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
@@ -80,7 +80,7 @@ public class DynamicPrivacyController implements KeyguardStateController.Callbac
@VisibleForTesting
boolean isDynamicPrivacyEnabled() {
- return !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
+ return mLockscreenUserManager.userAllowsNotificationsInPublic(
mLockscreenUserManager.getCurrentUserId());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index df412ed93f55..8cb18a06057e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -260,7 +260,7 @@ public class InstantAppNotifier extends CoreStartable
Intent browserIntent = getTaskIntent(taskId, userId);
Notification.Builder builder =
- new Notification.Builder(mContext, NotificationChannels.GENERAL);
+ new Notification.Builder(mContext, NotificationChannels.INSTANT);
if (browserIntent != null && browserIntent.isWebIntent()) {
// Make sure that this doesn't resolve back to an instant app
browserIntent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
index f6a55e6b75c6..0c6a91fe61f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -48,8 +48,7 @@ class NotifPipelineFlags @Inject constructor(
fun assertLegacyPipelineEnabled(): Unit =
check(!isNewPipelineEnabled()) { "Old pipeline code running w/ new pipeline enabled" }
- fun isNewPipelineEnabled(): Boolean =
- featureFlags.isEnabled(Flags.NEW_NOTIFICATION_PIPELINE_RENDERING)
+ fun isNewPipelineEnabled(): Boolean = true
fun isDevLoggingEnabled(): Boolean =
featureFlags.isEnabled(Flags.NOTIFICATION_PIPELINE_DEVELOPER_LOGGING)
@@ -57,4 +56,10 @@ class NotifPipelineFlags @Inject constructor(
fun isSmartspaceDedupingEnabled(): Boolean =
featureFlags.isEnabled(Flags.SMARTSPACE) &&
featureFlags.isEnabled(Flags.SMARTSPACE_DEDUPING)
-} \ No newline at end of file
+
+ fun removeUnrankedNotifs(): Boolean =
+ featureFlags.isEnabled(Flags.REMOVE_UNRANKED_NOTIFICATIONS)
+
+ fun fullScreenIntentRequiresKeyguard(): Boolean =
+ featureFlags.isEnabled(Flags.FSI_REQUIRES_KEYGUARD)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index c86bf93628dd..852d5f7a16e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -41,6 +41,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
@@ -73,6 +74,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Executor;
import dagger.Lazy;
@@ -113,6 +115,7 @@ public class NotificationEntryManager implements
private final IStatusBarService mStatusBarService;
private final NotifLiveDataStoreImpl mNotifLiveDataStore;
private final DumpManager mDumpManager;
+ private final Executor mBgExecutor;
private final Set<NotificationEntry> mAllNotifications = new ArraySet<>();
private final Set<NotificationEntry> mReadOnlyAllNotifications =
@@ -159,7 +162,8 @@ public class NotificationEntryManager implements
LeakDetector leakDetector,
IStatusBarService statusBarService,
NotifLiveDataStoreImpl notifLiveDataStore,
- DumpManager dumpManager
+ DumpManager dumpManager,
+ @Background Executor bgExecutor
) {
mLogger = logger;
mGroupManager = groupManager;
@@ -170,6 +174,7 @@ public class NotificationEntryManager implements
mStatusBarService = statusBarService;
mNotifLiveDataStore = notifLiveDataStore;
mDumpManager = dumpManager;
+ mBgExecutor = bgExecutor;
}
/** Once called, the NEM will start processing notification events from system server. */
@@ -566,17 +571,19 @@ public class NotificationEntryManager implements
private void sendNotificationRemovalToServer(
StatusBarNotification notification,
DismissedByUserStats dismissedByUserStats) {
- try {
- mStatusBarService.onNotificationClear(
- notification.getPackageName(),
- notification.getUser().getIdentifier(),
- notification.getKey(),
- dismissedByUserStats.dismissalSurface,
- dismissedByUserStats.dismissalSentiment,
- dismissedByUserStats.notificationVisibility);
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
+ mBgExecutor.execute(() -> {
+ try {
+ mStatusBarService.onNotificationClear(
+ notification.getPackageName(),
+ notification.getUser().getIdentifier(),
+ notification.getKey(),
+ dismissedByUserStats.dismissalSurface,
+ dismissedByUserStats.dismissalSentiment,
+ dismissedByUserStats.notificationVisibility);
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+ });
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt
index 2397005a1a61..52dcf02e3a19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt
@@ -17,18 +17,32 @@
package com.android.systemui.statusbar.notification
import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
import com.android.systemui.log.LogLevel.DEBUG
import com.android.systemui.log.LogLevel.INFO
import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.LogMessage
import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.util.Compile
import javax.inject.Inject
/** Logger for [NotificationEntryManager]. */
class NotificationEntryManagerLogger @Inject constructor(
+ notifPipelineFlags: NotifPipelineFlags,
@NotificationLog private val buffer: LogBuffer
) {
+ private val devLoggingEnabled by lazy { notifPipelineFlags.isDevLoggingEnabled() }
+
+ private inline fun devLog(
+ level: LogLevel,
+ initializer: LogMessage.() -> Unit,
+ noinline printer: LogMessage.() -> String
+ ) {
+ if (Compile.IS_DEBUG && devLoggingEnabled) buffer.log(TAG, level, initializer, printer)
+ }
+
fun logNotifAdded(key: String) {
- buffer.log(TAG, INFO, {
+ devLog(INFO, {
str1 = key
}, {
"NOTIF ADDED $str1"
@@ -36,7 +50,7 @@ class NotificationEntryManagerLogger @Inject constructor(
}
fun logNotifUpdated(key: String) {
- buffer.log(TAG, INFO, {
+ devLog(INFO, {
str1 = key
}, {
"NOTIF UPDATED $str1"
@@ -44,7 +58,7 @@ class NotificationEntryManagerLogger @Inject constructor(
}
fun logInflationAborted(key: String, status: String, reason: String) {
- buffer.log(TAG, DEBUG, {
+ devLog(DEBUG, {
str1 = key
str2 = status
str3 = reason
@@ -54,7 +68,7 @@ class NotificationEntryManagerLogger @Inject constructor(
}
fun logNotifInflated(key: String, isNew: Boolean) {
- buffer.log(TAG, DEBUG, {
+ devLog(DEBUG, {
str1 = key
bool1 = isNew
}, {
@@ -63,7 +77,7 @@ class NotificationEntryManagerLogger @Inject constructor(
}
fun logRemovalIntercepted(key: String) {
- buffer.log(TAG, INFO, {
+ devLog(INFO, {
str1 = key
}, {
"NOTIF REMOVE INTERCEPTED for $str1"
@@ -71,7 +85,7 @@ class NotificationEntryManagerLogger @Inject constructor(
}
fun logLifetimeExtended(key: String, extenderName: String, status: String) {
- buffer.log(TAG, INFO, {
+ devLog(INFO, {
str1 = key
str2 = extenderName
str3 = status
@@ -81,7 +95,7 @@ class NotificationEntryManagerLogger @Inject constructor(
}
fun logNotifRemoved(key: String, removedByUser: Boolean) {
- buffer.log(TAG, INFO, {
+ devLog(INFO, {
str1 = key
bool1 = removedByUser
}, {
@@ -90,7 +104,7 @@ class NotificationEntryManagerLogger @Inject constructor(
}
fun logFilterAndSort(reason: String) {
- buffer.log(TAG, INFO, {
+ devLog(INFO, {
str1 = reason
}, {
"FILTER AND SORT reason=$str1"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index b764c271de45..31b21c9b5321 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -4,10 +4,10 @@ import android.view.ViewGroup
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.LaunchAnimator
+import com.android.systemui.shade.NotificationShadeWindowViewController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
-import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.statusbar.policy.HeadsUpUtil
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
new file mode 100644
index 000000000000..ed80f33be701
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
@@ -0,0 +1,14 @@
+set noparent
+
+# Bug component: 78010
+
+aaliomer@google.com
+aroederer@google.com
+jeffdq@google.com
+juliacr@google.com
+juliatuttle@google.com
+lynhan@google.com
+steell@google.com
+yurilin@google.com
+
+per-file MediaNotificationProcessor.java = ethibodeau@google.com, asc@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
index 6be8a491eead..f6a572ec6ce6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection;
+import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED;
@@ -52,7 +53,7 @@ public class ListDumper {
sb,
true,
includeRecordKeeping,
- interactionTracker.hasUserInteractedWith(entry.getKey()));
+ interactionTracker.hasUserInteractedWith(logKey(entry)));
if (entry instanceof GroupEntry) {
GroupEntry ge = (GroupEntry) entry;
NotificationEntry summary = ge.getSummary();
@@ -63,7 +64,7 @@ public class ListDumper {
sb,
true,
includeRecordKeeping,
- interactionTracker.hasUserInteractedWith(summary.getKey()));
+ interactionTracker.hasUserInteractedWith(logKey(summary)));
}
List<NotificationEntry> children = ge.getChildren();
for (int childIndex = 0; childIndex < children.size(); childIndex++) {
@@ -74,7 +75,7 @@ public class ListDumper {
sb,
true,
includeRecordKeeping,
- interactionTracker.hasUserInteractedWith(child.getKey()));
+ interactionTracker.hasUserInteractedWith(logKey(child)));
}
}
}
@@ -115,12 +116,19 @@ public class ListDumper {
) {
sb.append(indent)
.append("[").append(index).append("] ")
- .append(entry.getKey());
+ .append(index.length() == 1 ? " " : "")
+ .append(logKey(entry));
if (includeParent) {
sb.append(" (parent=")
- .append(entry.getParent() != null ? entry.getParent().getKey() : null)
+ .append(logKey(entry.getParent()))
.append(")");
+
+ NotificationEntry notifEntry = entry.getRepresentativeEntry();
+ if (notifEntry != null) {
+ sb.append(" rank=")
+ .append(notifEntry.getRanking().getRank());
+ }
}
if (entry.getSection() != null) {
@@ -178,15 +186,15 @@ public class ListDumper {
if (notifEntry.getAttachState().getSuppressedChanges().getParent() != null) {
rksb.append("suppressedParent=")
- .append(notifEntry.getAttachState().getSuppressedChanges()
- .getParent().getKey())
+ .append(logKey(notifEntry.getAttachState().getSuppressedChanges()
+ .getParent()))
.append(" ");
}
if (notifEntry.getAttachState().getSuppressedChanges().getSection() != null) {
rksb.append("suppressedSection=")
.append(notifEntry.getAttachState().getSuppressedChanges()
- .getSection())
+ .getSection().getLabel())
.append(" ");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 6085096ee124..351a4bea2947 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -70,6 +70,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.LogBufferEulogizer;
@@ -89,6 +90,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.In
import com.android.systemui.statusbar.notification.collection.notifcollection.InternalNotifUpdater;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLoggerKt;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifEvent;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
@@ -104,11 +106,14 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
@@ -143,6 +148,7 @@ public class NotifCollection implements Dumpable {
private final NotifPipelineFlags mNotifPipelineFlags;
private final NotifCollectionLogger mLogger;
private final Handler mMainHandler;
+ private final Executor mBgExecutor;
private final LogBufferEulogizer mEulogizer;
private final DumpManager mDumpManager;
@@ -156,6 +162,8 @@ public class NotifCollection implements Dumpable {
private final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
private final List<NotifDismissInterceptor> mDismissInterceptors = new ArrayList<>();
+ private Set<String> mNotificationsWithoutRankings = Collections.emptySet();
+
private Queue<NotifEvent> mEventQueue = new ArrayDeque<>();
private boolean mAttached = false;
@@ -169,6 +177,7 @@ public class NotifCollection implements Dumpable {
NotifPipelineFlags notifPipelineFlags,
NotifCollectionLogger logger,
@Main Handler mainHandler,
+ @Background Executor bgExecutor,
LogBufferEulogizer logBufferEulogizer,
DumpManager dumpManager) {
mStatusBarService = statusBarService;
@@ -176,6 +185,7 @@ public class NotifCollection implements Dumpable {
mNotifPipelineFlags = notifPipelineFlags;
mLogger = logger;
mMainHandler = mainHandler;
+ mBgExecutor = bgExecutor;
mEulogizer = logBufferEulogizer;
mDumpManager = dumpManager;
}
@@ -266,13 +276,14 @@ public class NotifCollection implements Dumpable {
requireNonNull(stats);
NotificationEntry storedEntry = mNotificationSet.get(entry.getKey());
if (storedEntry == null) {
- mLogger.logNonExistentNotifDismissed(entry.getKey());
+ mLogger.logNonExistentNotifDismissed(entry);
continue;
}
if (entry != storedEntry) {
throw mEulogizer.record(
new IllegalStateException("Invalid entry: "
- + "different stored and dismissed entries for " + entry.getKey()));
+ + "different stored and dismissed entries for " + logKey(entry)
+ + " stored=@" + Integer.toHexString(storedEntry.hashCode())));
}
if (entry.getDismissState() == DISMISSED) {
@@ -281,30 +292,32 @@ public class NotifCollection implements Dumpable {
updateDismissInterceptors(entry);
if (isDismissIntercepted(entry)) {
- mLogger.logNotifDismissedIntercepted(entry.getKey());
+ mLogger.logNotifDismissedIntercepted(entry);
continue;
}
entriesToLocallyDismiss.add(entry);
if (!isCanceled(entry)) {
// send message to system server if this notification hasn't already been cancelled
- try {
- mStatusBarService.onNotificationClear(
- entry.getSbn().getPackageName(),
- entry.getSbn().getUser().getIdentifier(),
- entry.getSbn().getKey(),
- stats.dismissalSurface,
- stats.dismissalSentiment,
- stats.notificationVisibility);
- } catch (RemoteException e) {
- // system process is dead if we're here.
- mLogger.logRemoteExceptionOnNotificationClear(entry.getKey(), e);
- }
+ mBgExecutor.execute(() -> {
+ try {
+ mStatusBarService.onNotificationClear(
+ entry.getSbn().getPackageName(),
+ entry.getSbn().getUser().getIdentifier(),
+ entry.getSbn().getKey(),
+ stats.dismissalSurface,
+ stats.dismissalSentiment,
+ stats.notificationVisibility);
+ } catch (RemoteException e) {
+ // system process is dead if we're here.
+ mLogger.logRemoteExceptionOnNotificationClear(entry, e);
+ }
+ });
}
}
locallyDismissNotifications(entriesToLocallyDismiss);
- dispatchEventsAndRebuildList();
+ dispatchEventsAndRebuildList("dismissNotifications");
}
/**
@@ -341,14 +354,14 @@ public class NotifCollection implements Dumpable {
// interceptors the chance to filter the notification
updateDismissInterceptors(entry);
if (isDismissIntercepted(entry)) {
- mLogger.logNotifClearAllDismissalIntercepted(entry.getKey());
+ mLogger.logNotifClearAllDismissalIntercepted(entry);
}
entries.remove(i);
}
}
locallyDismissNotifications(entries);
- dispatchEventsAndRebuildList();
+ dispatchEventsAndRebuildList("dismissAllNotifications");
}
/**
@@ -362,7 +375,7 @@ public class NotifCollection implements Dumpable {
NotificationEntry entry = entries.get(i);
entry.setDismissState(DISMISSED);
- mLogger.logNotifDismissed(entry.getKey());
+ mLogger.logNotifDismissed(entry);
if (isCanceled(entry)) {
canceledEntries.add(entry);
@@ -395,7 +408,7 @@ public class NotifCollection implements Dumpable {
postNotification(sbn, requireRanking(rankingMap, sbn.getKey()));
applyRanking(rankingMap);
- dispatchEventsAndRebuildList();
+ dispatchEventsAndRebuildList("onNotificationPosted");
}
private void onNotificationGroupPosted(List<CoalescedEvent> batch) {
@@ -406,7 +419,7 @@ public class NotifCollection implements Dumpable {
for (CoalescedEvent event : batch) {
postNotification(event.getSbn(), event.getRanking());
}
- dispatchEventsAndRebuildList();
+ dispatchEventsAndRebuildList("onNotificationGroupPosted");
}
private void onNotificationRemoved(
@@ -415,26 +428,26 @@ public class NotifCollection implements Dumpable {
int reason) {
Assert.isMainThread();
- mLogger.logNotifRemoved(sbn.getKey(), reason);
+ mLogger.logNotifRemoved(sbn, reason);
final NotificationEntry entry = mNotificationSet.get(sbn.getKey());
if (entry == null) {
// TODO (b/160008901): Throw an exception here
- mLogger.logNoNotificationToRemoveWithKey(sbn.getKey(), reason);
+ mLogger.logNoNotificationToRemoveWithKey(sbn, reason);
return;
}
entry.mCancellationReason = reason;
tryRemoveNotification(entry);
applyRanking(rankingMap);
- dispatchEventsAndRebuildList();
+ dispatchEventsAndRebuildList("onNotificationRemoved");
}
private void onNotificationRankingUpdate(RankingMap rankingMap) {
Assert.isMainThread();
mEventQueue.add(new RankingUpdatedEvent(rankingMap));
applyRanking(rankingMap);
- dispatchEventsAndRebuildList();
+ dispatchEventsAndRebuildList("onNotificationRankingUpdate");
}
private void onNotificationChannelModified(
@@ -444,7 +457,7 @@ public class NotifCollection implements Dumpable {
int modificationType) {
Assert.isMainThread();
mEventQueue.add(new ChannelChangedEvent(pkgName, user, channel, modificationType));
- dispatchEventsAndRebuildList();
+ dispatchEventsAndRebuildList("onNotificationChannelModified");
}
private void onNotificationsInitialized() {
@@ -463,7 +476,7 @@ public class NotifCollection implements Dumpable {
mEventQueue.add(new BindEntryEvent(entry, sbn));
mNotificationSet.put(sbn.getKey(), entry);
- mLogger.logNotifPosted(sbn.getKey());
+ mLogger.logNotifPosted(entry);
mEventQueue.add(new EntryAddedEvent(entry));
} else {
@@ -482,7 +495,7 @@ public class NotifCollection implements Dumpable {
entry.setSbn(sbn);
mEventQueue.add(new BindEntryEvent(entry, sbn));
- mLogger.logNotifUpdated(sbn.getKey());
+ mLogger.logNotifUpdated(entry);
mEventQueue.add(new EntryUpdatedEvent(entry, true /* fromSystem */));
}
}
@@ -497,12 +510,12 @@ public class NotifCollection implements Dumpable {
if (mNotificationSet.get(entry.getKey()) != entry) {
throw mEulogizer.record(
new IllegalStateException("No notification to remove with key "
- + entry.getKey()));
+ + logKey(entry)));
}
if (!isCanceled(entry)) {
throw mEulogizer.record(
- new IllegalStateException("Cannot remove notification " + entry.getKey()
+ new IllegalStateException("Cannot remove notification " + logKey(entry)
+ ": has not been marked for removal"));
}
@@ -513,7 +526,7 @@ public class NotifCollection implements Dumpable {
}
if (!isLifetimeExtended(entry)) {
- mLogger.logNotifReleased(entry.getKey());
+ mLogger.logNotifReleased(entry);
mNotificationSet.remove(entry.getKey());
cancelDismissInterception(entry);
mEventQueue.add(new EntryRemovedEvent(entry, entry.mCancellationReason));
@@ -558,6 +571,7 @@ public class NotifCollection implements Dumpable {
}
private void applyRanking(@NonNull RankingMap rankingMap) {
+ ArrayMap<String, NotificationEntry> currentEntriesWithoutRankings = null;
for (NotificationEntry entry : mNotificationSet.values()) {
if (!isCanceled(entry)) {
@@ -571,22 +585,37 @@ public class NotifCollection implements Dumpable {
// TODO: (b/145659174) update the sbn's overrideGroupKey in
// NotificationEntry.setRanking instead of here once we fully migrate to the
// NewNotifPipeline
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
- final String newOverrideGroupKey = ranking.getOverrideGroupKey();
- if (!Objects.equals(entry.getSbn().getOverrideGroupKey(),
- newOverrideGroupKey)) {
- entry.getSbn().setOverrideGroupKey(newOverrideGroupKey);
- }
+ final String newOverrideGroupKey = ranking.getOverrideGroupKey();
+ if (!Objects.equals(entry.getSbn().getOverrideGroupKey(),
+ newOverrideGroupKey)) {
+ entry.getSbn().setOverrideGroupKey(newOverrideGroupKey);
}
} else {
- mLogger.logRankingMissing(entry.getKey(), rankingMap);
+ if (currentEntriesWithoutRankings == null) {
+ currentEntriesWithoutRankings = new ArrayMap<>();
+ }
+ currentEntriesWithoutRankings.put(entry.getKey(), entry);
}
}
}
+ NotifCollectionLoggerKt.maybeLogInconsistentRankings(
+ mLogger,
+ mNotificationsWithoutRankings,
+ currentEntriesWithoutRankings,
+ rankingMap
+ );
+ mNotificationsWithoutRankings = currentEntriesWithoutRankings == null
+ ? Collections.emptySet() : currentEntriesWithoutRankings.keySet();
+ if (currentEntriesWithoutRankings != null && mNotifPipelineFlags.removeUnrankedNotifs()) {
+ for (NotificationEntry entry : currentEntriesWithoutRankings.values()) {
+ entry.mCancellationReason = REASON_UNKNOWN;
+ tryRemoveNotification(entry);
+ }
+ }
mEventQueue.add(new RankingAppliedEvent());
}
- private void dispatchEventsAndRebuildList() {
+ private void dispatchEventsAndRebuildList(String reason) {
Trace.beginSection("NotifCollection.dispatchEventsAndRebuildList");
mAmDispatchingToOtherCode = true;
while (!mEventQueue.isEmpty()) {
@@ -595,7 +624,7 @@ public class NotifCollection implements Dumpable {
mAmDispatchingToOtherCode = false;
if (mBuildListener != null) {
- mBuildListener.onBuildList(mReadOnlyNotificationSet);
+ mBuildListener.onBuildList(mReadOnlyNotificationSet, reason);
}
Trace.endSection();
}
@@ -609,22 +638,28 @@ public class NotifCollection implements Dumpable {
}
checkForReentrantCall();
+ NotificationEntry collectionEntry = getEntry(entry.getKey());
+ String logKey = logKey(entry);
+ String collectionEntryIs = collectionEntry == null ? "null"
+ : entry == collectionEntry ? "same" : "different";
+
+ if (entry != collectionEntry) {
+ // TODO: We should probably make this throw, but that's too risky right now
+ mLogger.logEntryBeingExtendedNotInCollection(entry, extender, collectionEntryIs);
+ }
+
if (!entry.mLifetimeExtenders.remove(extender)) {
throw mEulogizer.record(new IllegalStateException(
- String.format(
- "Cannot end lifetime extension for extender \"%s\" (%s)",
- extender.getName(),
- extender)));
+ String.format("Cannot end lifetime extension for extender \"%s\""
+ + " of entry %s (collection entry is %s)",
+ extender.getName(), logKey, collectionEntryIs)));
}
- mLogger.logLifetimeExtensionEnded(
- entry.getKey(),
- extender,
- entry.mLifetimeExtenders.size());
+ mLogger.logLifetimeExtensionEnded(entry, extender, entry.mLifetimeExtenders.size());
if (!isLifetimeExtended(entry)) {
if (tryRemoveNotification(entry)) {
- dispatchEventsAndRebuildList();
+ dispatchEventsAndRebuildList("onEndLifetimeExtension");
}
}
}
@@ -647,7 +682,7 @@ public class NotifCollection implements Dumpable {
mAmDispatchingToOtherCode = true;
for (NotifLifetimeExtender extender : mLifetimeExtenders) {
if (extender.maybeExtendLifetime(entry, entry.mCancellationReason)) {
- mLogger.logLifetimeExtended(entry.getKey(), extender);
+ mLogger.logLifetimeExtended(entry, extender);
entry.mLifetimeExtenders.add(extender);
}
}
@@ -820,16 +855,19 @@ public class NotifCollection implements Dumpable {
@Override
public void dump(PrintWriter pw, @NonNull String[] args) {
final List<NotificationEntry> entries = new ArrayList<>(getAllNotifs());
+ entries.sort(Comparator.comparing(NotificationEntry::getKey));
- pw.println("\t" + TAG + " unsorted/unfiltered notifications:");
- if (entries.size() == 0) {
- pw.println("\t\t None");
- }
+ pw.println("\t" + TAG + " unsorted/unfiltered notifications: " + entries.size());
pw.println(
ListDumper.dumpList(
entries,
true,
"\t\t"));
+
+ pw.println("\n\tmNotificationsWithoutRankings: " + mNotificationsWithoutRankings.size());
+ for (String key : mNotificationsWithoutRankings) {
+ pw.println("\t * : " + key);
+ }
}
private final BatchableNotificationHandler mNotifHandler = new BatchableNotificationHandler() {
@@ -908,21 +946,21 @@ public class NotifCollection implements Dumpable {
// Make sure we have the notification to update
NotificationEntry entry = mNotificationSet.get(sbn.getKey());
if (entry == null) {
- mLogger.logNotifInternalUpdateFailed(sbn.getKey(), name, reason);
+ mLogger.logNotifInternalUpdateFailed(sbn, name, reason);
return;
}
- mLogger.logNotifInternalUpdate(sbn.getKey(), name, reason);
+ mLogger.logNotifInternalUpdate(entry, name, reason);
// First do the pieces of postNotification which are not about assuming the notification
// was sent by the app
entry.setSbn(sbn);
mEventQueue.add(new BindEntryEvent(entry, sbn));
- mLogger.logNotifUpdated(sbn.getKey());
+ mLogger.logNotifUpdated(entry);
mEventQueue.add(new EntryUpdatedEvent(entry, false /* fromSystem */));
// Skip the applyRanking step and go straight to dispatching the events
- dispatchEventsAndRebuildList();
+ dispatchEventsAndRebuildList("updateNotificationInternally");
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
index 4daed77c0954..d1aa01bb2125 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -75,8 +75,13 @@ public class NotifInflaterImpl implements NotifInflater {
}
@Override
- public void abortInflation(NotificationEntry entry) {
- entry.abortTask();
+ public boolean abortInflation(NotificationEntry entry) {
+ return entry.abortTask();
+ }
+
+ @Override
+ public void releaseViews(@NonNull NotificationEntry entry) {
+ requireBinder().releaseViews(entry);
}
private NotificationContentInflater.InflationCallback wrapInflationCallback(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
index e3ee8139d71c..f662a040fae6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.collection
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
@@ -76,7 +75,6 @@ import javax.inject.Inject
*/
@SysUISingleton
class NotifPipeline @Inject constructor(
- notifPipelineFlags: NotifPipelineFlags,
private val mNotifCollection: NotifCollection,
private val mShadeListBuilder: ShadeListBuilder,
private val mRenderStageManager: RenderStageManager
@@ -107,8 +105,6 @@ class NotifPipeline @Inject constructor(
return mNotifCollection.getEntry(key)
}
- val isNewPipelineEnabled: Boolean = notifPipelineFlags.isNewPipelineEnabled()
-
/**
* Registers a lifetime extender. Lifetime extenders can cause notifications that have been
* dismissed or retracted by system server to be temporarily retained in the collection.
@@ -253,4 +249,4 @@ class NotifPipeline @Inject constructor(
fun getInternalNotifUpdater(name: String?): InternalNotifUpdater {
return mNotifCollection.getInternalNotifUpdater(name)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index aedbd1b56622..b6392f705c49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -175,6 +175,10 @@ public final class NotificationEntry extends ListEntry {
public boolean mRemoteEditImeAnimatingAway;
public boolean mRemoteEditImeVisible;
private boolean mExpandAnimationRunning;
+ /**
+ * Flag to determine if the entry is blockable by DnD filters
+ */
+ private boolean mBlockable;
/**
* @param sbn the StatusBarNotification from system server
@@ -253,6 +257,7 @@ public final class NotificationEntry extends ListEntry {
}
mRanking = ranking.withAudiblyAlertedInfo(mRanking);
+ updateIsBlockable();
}
/*
@@ -471,11 +476,13 @@ public final class NotificationEntry extends ListEntry {
/**
* Abort all existing inflation tasks
*/
- public void abortTask() {
+ public boolean abortTask() {
if (mRunningTask != null) {
mRunningTask.abort();
mRunningTask = null;
+ return true;
}
+ return false;
}
public void setInflationTask(InflationTask abortableTask) {
@@ -781,15 +788,20 @@ public final class NotificationEntry extends ListEntry {
* or is not in an allowList).
*/
public boolean isBlockable() {
+ return mBlockable;
+ }
+
+ private void updateIsBlockable() {
if (getChannel() == null) {
- return false;
+ mBlockable = false;
+ return;
}
if (getChannel().isImportanceLockedByCriticalDeviceFunction()
&& !getChannel().isBlockable()) {
- return false;
+ mBlockable = false;
+ return;
}
-
- return true;
+ mBlockable = true;
}
private boolean shouldSuppressVisualEffect(int effect) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index df2fe4e8511f..075a0dc7555e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -304,70 +304,72 @@ public class ShadeListBuilder implements Dumpable {
private final CollectionReadyForBuildListener mReadyForBuildListener =
new CollectionReadyForBuildListener() {
@Override
- public void onBuildList(Collection<NotificationEntry> entries) {
+ public void onBuildList(Collection<NotificationEntry> entries, String reason) {
Assert.isMainThread();
mPipelineState.requireIsBefore(STATE_BUILD_STARTED);
- mLogger.logOnBuildList();
+ mLogger.logOnBuildList(reason);
mAllEntries = entries;
mChoreographer.schedule();
}
};
- private void onPreRenderInvalidated(Invalidator invalidator) {
+ private void onPreRenderInvalidated(Invalidator invalidator, @Nullable String reason) {
Assert.isMainThread();
- mLogger.logPreRenderInvalidated(invalidator.getName(), mPipelineState.getState());
+ mLogger.logPreRenderInvalidated(invalidator, mPipelineState.getState(), reason);
rebuildListIfBefore(STATE_FINALIZING);
}
- private void onPreGroupFilterInvalidated(NotifFilter filter) {
+ private void onPreGroupFilterInvalidated(NotifFilter filter, @Nullable String reason) {
Assert.isMainThread();
- mLogger.logPreGroupFilterInvalidated(filter.getName(), mPipelineState.getState());
+ mLogger.logPreGroupFilterInvalidated(filter, mPipelineState.getState(), reason);
rebuildListIfBefore(STATE_PRE_GROUP_FILTERING);
}
- private void onReorderingAllowedInvalidated(NotifStabilityManager stabilityManager) {
+ private void onReorderingAllowedInvalidated(NotifStabilityManager stabilityManager,
+ @Nullable String reason) {
Assert.isMainThread();
mLogger.logReorderingAllowedInvalidated(
- stabilityManager.getName(),
- mPipelineState.getState());
+ stabilityManager,
+ mPipelineState.getState(),
+ reason);
rebuildListIfBefore(STATE_GROUPING);
}
- private void onPromoterInvalidated(NotifPromoter promoter) {
+ private void onPromoterInvalidated(NotifPromoter promoter, @Nullable String reason) {
Assert.isMainThread();
- mLogger.logPromoterInvalidated(promoter.getName(), mPipelineState.getState());
+ mLogger.logPromoterInvalidated(promoter, mPipelineState.getState(), reason);
rebuildListIfBefore(STATE_TRANSFORMING);
}
- private void onNotifSectionInvalidated(NotifSectioner section) {
+ private void onNotifSectionInvalidated(NotifSectioner section, @Nullable String reason) {
Assert.isMainThread();
- mLogger.logNotifSectionInvalidated(section.getName(), mPipelineState.getState());
+ mLogger.logNotifSectionInvalidated(section, mPipelineState.getState(), reason);
rebuildListIfBefore(STATE_SORTING);
}
- private void onFinalizeFilterInvalidated(NotifFilter filter) {
+ private void onFinalizeFilterInvalidated(NotifFilter filter, @Nullable String reason) {
Assert.isMainThread();
- mLogger.logFinalizeFilterInvalidated(filter.getName(), mPipelineState.getState());
+ mLogger.logFinalizeFilterInvalidated(filter, mPipelineState.getState(), reason);
rebuildListIfBefore(STATE_FINALIZE_FILTERING);
}
- private void onNotifComparatorInvalidated(NotifComparator comparator) {
+ private void onNotifComparatorInvalidated(NotifComparator comparator, @Nullable String reason) {
Assert.isMainThread();
- mLogger.logNotifComparatorInvalidated(comparator.getName(), mPipelineState.getState());
+ mLogger.logNotifComparatorInvalidated(comparator, mPipelineState.getState(), reason);
rebuildListIfBefore(STATE_SORTING);
}
@@ -456,7 +458,8 @@ public class ShadeListBuilder implements Dumpable {
mLogger.logEndBuildList(
mIterationCount,
mReadOnlyNotifList.size(),
- countChildren(mReadOnlyNotifList));
+ countChildren(mReadOnlyNotifList),
+ /* enforcedVisualStability */ !mNotifStabilityManager.isEveryChangeAllowed());
if (mAlwaysLogList || mIterationCount % 10 == 0) {
Trace.beginSection("ShadeListBuilder.logFinalList");
mLogger.logFinalList(mNotifList);
@@ -579,11 +582,7 @@ public class ShadeListBuilder implements Dumpable {
if (existingSummary == null) {
group.setSummary(entry);
} else {
- mLogger.logDuplicateSummary(
- mIterationCount,
- group.getKey(),
- existingSummary.getKey(),
- entry.getKey());
+ mLogger.logDuplicateSummary(mIterationCount, group, existingSummary, entry);
// Use whichever one was posted most recently
if (entry.getSbn().getPostTime()
@@ -990,7 +989,7 @@ public class ShadeListBuilder implements Dumpable {
// Check for suppressed order changes
if (!getStabilityManager().isEveryChangeAllowed()) {
mForceReorderable = true;
- boolean isSorted = isSorted(mNotifList, mTopLevelComparator);
+ boolean isSorted = isShadeSorted();
mForceReorderable = false;
if (!isSorted) {
getStabilityManager().onEntryReorderSuppressed();
@@ -999,9 +998,23 @@ public class ShadeListBuilder implements Dumpable {
Trace.endSection();
}
+ private boolean isShadeSorted() {
+ if (!isSorted(mNotifList, mTopLevelComparator)) {
+ return false;
+ }
+ for (ListEntry entry : mNotifList) {
+ if (entry instanceof GroupEntry) {
+ if (!isSorted(((GroupEntry) entry).getChildren(), mGroupChildrenComparator)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
/** Determine whether the items in the list are sorted according to the comparator */
@VisibleForTesting
- public static <T> boolean isSorted(List<T> items, Comparator<T> comparator) {
+ public static <T> boolean isSorted(List<T> items, Comparator<? super T> comparator) {
if (items.size() <= 1) {
return true;
}
@@ -1070,7 +1083,7 @@ public class ShadeListBuilder implements Dumpable {
if (!Objects.equals(curr, prev)) {
mLogger.logEntryAttachStateChanged(
mIterationCount,
- entry.getKey(),
+ entry,
prev.getParent(),
curr.getParent());
@@ -1209,7 +1222,7 @@ public class ShadeListBuilder implements Dumpable {
};
- private final Comparator<ListEntry> mGroupChildrenComparator = (o1, o2) -> {
+ private final Comparator<NotificationEntry> mGroupChildrenComparator = (o1, o2) -> {
int index1 = canReorder(o1) ? -1 : o1.getPreviousAttachState().getStableIndex();
int index2 = canReorder(o2) ? -1 : o2.getPreviousAttachState().getStableIndex();
int cmp = Integer.compare(index1, index2);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index b923fdfe6be8..3627b4084ec7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -127,14 +127,6 @@ public class BubbleCoordinator implements Coordinator {
DismissedByUserStats dismissedByUserStats,
int reason
) {
- if (!mNotifPipeline.isNewPipelineEnabled()) {
- // The `entry` will be from whichever pipeline is active, so if the old pipeline is
- // running, make sure that we use the new pipeline's entry (if it still exists).
- NotificationEntry newPipelineEntry = mNotifPipeline.getEntry(entry.getKey());
- if (newPipelineEntry != null) {
- entry = newPipelineEntry;
- }
- }
if (isInterceptingDismissal(entry)) {
mInterceptedDismissalEntries.remove(entry.getKey());
mOnEndDismissInterception.onEndDismissInterception(mDismissInterceptor, entry,
@@ -150,7 +142,7 @@ public class BubbleCoordinator implements Coordinator {
@Override
public void invalidateNotifications(String reason) {
- mNotifFilter.invalidateList();
+ mNotifFilter.invalidateList(reason);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt
index df54ccd85e73..240540515705 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt
@@ -30,12 +30,12 @@ class DebugModeCoordinator @Inject constructor(
) : Coordinator {
override fun attach(pipeline: NotifPipeline) {
- pipeline.addPreGroupFilter(preGroupFilter)
- debugModeFilterProvider.registerInvalidationListener(preGroupFilter::invalidateList)
+ pipeline.addPreGroupFilter(filter)
+ debugModeFilterProvider.registerInvalidationListener { filter.invalidateList(null) }
}
- private val preGroupFilter = object : NotifFilter("DebugModeCoordinator") {
+ private val filter = object : NotifFilter("DebugModeFilter") {
override fun shouldFilterOut(entry: NotificationEntry, now: Long) =
debugModeFilterProvider.shouldFilterOut(entry)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
index e8652493da6b..058042c4bccd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
@@ -90,7 +90,7 @@ public class DeviceProvisionedCoordinator implements Coordinator {
new DeviceProvisionedController.DeviceProvisionedListener() {
@Override
public void onDeviceProvisionedChanged() {
- mNotifFilter.invalidateList();
+ mNotifFilter.invalidateList("onDeviceProvisionedChanged");
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index da0169bd6dc4..8278b549a7a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import android.app.Notification
import android.app.Notification.GROUP_ALERT_SUMMARY
import android.util.ArrayMap
-import android.util.ArraySet
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
@@ -36,11 +35,13 @@ import com.android.systemui.statusbar.notification.collection.render.NodeControl
import com.android.systemui.statusbar.notification.dagger.IncomingHeader
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider
+import com.android.systemui.statusbar.notification.logKey
import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.time.SystemClock
+import java.util.function.Consumer
import javax.inject.Inject
/**
@@ -85,6 +86,7 @@ class HeadsUpCoordinator @Inject constructor(
pipeline.addOnBeforeFinalizeFilterListener(::onBeforeFinalizeFilter)
pipeline.addPromoter(mNotifPromoter)
pipeline.addNotificationLifetimeExtender(mLifetimeExtender)
+ mRemoteInputManager.addActionPressListener(mActionPressListener)
}
private fun onHeadsUpViewBound(entry: NotificationEntry) {
@@ -277,8 +279,8 @@ class HeadsUpCoordinator @Inject constructor(
.firstOrNull()
?.let { posted ->
posted.entry.takeIf { entry ->
- locationLookupByKey(entry.key) == GroupLocation.Isolated
- && entry.sbn.notification.groupAlertBehavior == GROUP_ALERT_SUMMARY
+ locationLookupByKey(entry.key) == GroupLocation.Isolated &&
+ entry.sbn.notification.groupAlertBehavior == GROUP_ALERT_SUMMARY
}
}
@@ -448,6 +450,14 @@ class HeadsUpCoordinator @Inject constructor(
(entry.sbn.notification.flags and Notification.FLAG_ONLY_ALERT_ONCE) == 0)
}
+ /** When an action is pressed on a notification, end HeadsUp lifetime extension. */
+ private val mActionPressListener = Consumer<NotificationEntry> { entry ->
+ if (mNotifsExtendingLifetime.contains(entry)) {
+ val removeInMillis = mHeadsUpManager.getEarliestRemovalTime(entry.key)
+ mExecutor.executeDelayed({ endNotifLifetimeExtensionIfExtended(entry) }, removeInMillis)
+ }
+ }
+
private val mLifetimeExtender = object : NotifLifetimeExtender {
override fun getName() = TAG
@@ -503,6 +513,7 @@ class HeadsUpCoordinator @Inject constructor(
private val mOnHeadsUpChangedListener = object : OnHeadsUpChangedListener {
override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
if (!isHeadsUp) {
+ mNotifPromoter.invalidateList("headsUpEnded: ${entry.logKey}")
mHeadsUpViewBinder.unbindHeadsUpView(entry)
endNotifLifetimeExtensionIfExtended(entry)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java
index 7b5cf8511900..e4e2bc16b77e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java
@@ -41,14 +41,11 @@ import javax.inject.Inject;
@CoordinatorScope
public class HideNotifsForOtherUsersCoordinator implements Coordinator {
private final NotificationLockscreenUserManager mLockscreenUserManager;
- private final SharedCoordinatorLogger mLogger;
@Inject
public HideNotifsForOtherUsersCoordinator(
- NotificationLockscreenUserManager lockscreenUserManager,
- SharedCoordinatorLogger logger) {
+ NotificationLockscreenUserManager lockscreenUserManager) {
mLockscreenUserManager = lockscreenUserManager;
- mLogger = logger;
}
@Override
@@ -70,23 +67,19 @@ public class HideNotifsForOtherUsersCoordinator implements Coordinator {
// changes
@Override
public void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) {
- mLogger.logUserOrProfileChanged(
- mLockscreenUserManager.getCurrentUserId(),
- profileIdsToStr(currentProfiles));
- mFilter.invalidateList();
+ StringBuilder sb = new StringBuilder("onCurrentProfilesChanged:");
+ sb.append(" user=").append(mLockscreenUserManager.getCurrentUserId());
+ sb.append(" profiles=");
+ sb.append("{");
+ for (int i = 0; i < currentProfiles.size(); i++) {
+ if (i != 0) {
+ sb.append(",");
+ }
+ sb.append(currentProfiles.keyAt(i));
+ }
+ sb.append("}");
+ mFilter.invalidateList(sb.toString());
}
};
- private String profileIdsToStr(SparseArray<UserInfo> currentProfiles) {
- StringBuilder sb = new StringBuilder();
- sb.append("{");
- for (int i = 0; i < currentProfiles.size(); i++) {
- sb.append(currentProfiles.keyAt(i));
- if (i < currentProfiles.size() - 1) {
- sb.append(",");
- }
- }
- sb.append("}");
- return sb.toString();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index ff1c70ca740b..e3d71c8b29d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -16,15 +16,15 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
-import com.android.keyguard.KeyguardUpdateMonitor;
+import androidx.annotation.NonNull;
+
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
-import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
import javax.inject.Inject;
@@ -36,27 +36,18 @@ import javax.inject.Inject;
@CoordinatorScope
public class KeyguardCoordinator implements Coordinator {
private static final String TAG = "KeyguardCoordinator";
- private final StatusBarStateController mStatusBarStateController;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final HighPriorityProvider mHighPriorityProvider;
- private final SectionHeaderVisibilityProvider mSectionHeaderVisibilityProvider;
private final KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
- private final SharedCoordinatorLogger mLogger;
+ private final SectionHeaderVisibilityProvider mSectionHeaderVisibilityProvider;
+ private final StatusBarStateController mStatusBarStateController;
@Inject
public KeyguardCoordinator(
- StatusBarStateController statusBarStateController,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- HighPriorityProvider highPriorityProvider,
- SectionHeaderVisibilityProvider sectionHeaderVisibilityProvider,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
- SharedCoordinatorLogger logger) {
- mStatusBarStateController = statusBarStateController;
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mHighPriorityProvider = highPriorityProvider;
- mSectionHeaderVisibilityProvider = sectionHeaderVisibilityProvider;
+ SectionHeaderVisibilityProvider sectionHeaderVisibilityProvider,
+ StatusBarStateController statusBarStateController) {
mKeyguardNotificationVisibilityProvider = keyguardNotificationVisibilityProvider;
- mLogger = logger;
+ mSectionHeaderVisibilityProvider = sectionHeaderVisibilityProvider;
+ mStatusBarStateController = statusBarStateController;
}
@Override
@@ -72,7 +63,7 @@ public class KeyguardCoordinator implements Coordinator {
private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
@Override
- public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ public boolean shouldFilterOut(@NonNull NotificationEntry entry, long now) {
return mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry);
}
};
@@ -84,9 +75,8 @@ public class KeyguardCoordinator implements Coordinator {
}
private void invalidateListFromFilter(String reason) {
- mLogger.logKeyguardCoordinatorInvalidated(reason);
updateSectionHeadersVisibility();
- mNotifFilter.invalidateList();
+ mNotifFilter.invalidateList(reason);
}
private void updateSectionHeadersVisibility() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 359e2028d9f3..891e25ef6c25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -72,9 +72,7 @@ class NotifCoordinatorsImpl @Inject constructor(
// pipeline, such as this DataStoreCoordinator which cannot be removed, as it's a critical
// glue between the pipeline and parts of SystemUI which depend on pipeline output via the
// NotifLiveDataStore.
- if (notifPipelineFlags.isNewPipelineEnabled()) {
- mCoordinators.add(dataStoreCoordinator)
- }
+ mCoordinators.add(dataStoreCoordinator)
// Attach normal coordinators.
mCoordinators.add(hideLocallyDismissedNotifsCoordinator)
@@ -97,18 +95,14 @@ class NotifCoordinatorsImpl @Inject constructor(
if (notifPipelineFlags.isSmartspaceDedupingEnabled()) {
mCoordinators.add(smartspaceDedupingCoordinator)
}
- if (notifPipelineFlags.isNewPipelineEnabled()) {
- mCoordinators.add(headsUpCoordinator)
- mCoordinators.add(gutsCoordinator)
- mCoordinators.add(preparationCoordinator)
- mCoordinators.add(remoteInputCoordinator)
- }
+ mCoordinators.add(headsUpCoordinator)
+ mCoordinators.add(gutsCoordinator)
+ mCoordinators.add(preparationCoordinator)
+ mCoordinators.add(remoteInputCoordinator)
// Manually add Ordered Sections
// HeadsUp > FGS > People > Alerting > Silent > Minimized > Unknown/Default
- if (notifPipelineFlags.isNewPipelineEnabled()) {
- mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp
- }
+ mOrderedSections.add(headsUpCoordinator.sectioner)
mOrderedSections.add(appOpsCoordinator.sectioner) // ForegroundService
mOrderedSections.add(conversationCoordinator.sectioner) // People
mOrderedSections.add(rankingCoordinator.alertingSectioner) // Alerting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 210fe8fd0421..ef1e57b4cd3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
+import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
import static com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
import static java.util.Objects.requireNonNull;
@@ -147,7 +148,8 @@ public class PreparationCoordinator implements Coordinator {
@Override
public void attach(NotifPipeline pipeline) {
mNotifErrorManager.addInflationErrorListener(mInflationErrorListener);
- mAdjustmentProvider.addDirtyListener(mNotifInflatingFilter::invalidateList);
+ mAdjustmentProvider.addDirtyListener(
+ () -> mNotifInflatingFilter.invalidateList("adjustmentProviderChanged"));
pipeline.addCollectionListener(mNotifCollectionListener);
// Inflate after grouping/sorting since that affects what views to inflate.
@@ -245,12 +247,13 @@ public class PreparationCoordinator implements Coordinator {
} catch (RemoteException ex) {
// System server is dead, nothing to do about that
}
- mNotifInflationErrorFilter.invalidateList();
+ mNotifInflationErrorFilter.invalidateList("onNotifInflationError for " + logKey(entry));
}
@Override
public void onNotifInflationErrorCleared(NotificationEntry entry) {
- mNotifInflationErrorFilter.invalidateList();
+ mNotifInflationErrorFilter.invalidateList(
+ "onNotifInflationErrorCleared for " + logKey(entry));
}
};
@@ -360,22 +363,25 @@ public class PreparationCoordinator implements Coordinator {
}
private void abortInflation(NotificationEntry entry, String reason) {
- mLogger.logInflationAborted(entry.getKey(), reason);
- mNotifInflater.abortInflation(entry);
- mInflatingNotifs.remove(entry);
+ final boolean taskAborted = mNotifInflater.abortInflation(entry);
+ final boolean wasInflating = mInflatingNotifs.remove(entry);
+ if (taskAborted || wasInflating) {
+ mLogger.logInflationAborted(entry, reason);
+ }
}
private void onInflationFinished(NotificationEntry entry, NotifViewController controller) {
- mLogger.logNotifInflated(entry.getKey());
+ mLogger.logNotifInflated(entry);
mInflatingNotifs.remove(entry);
mViewBarn.registerViewForEntry(entry, controller);
mInflationStates.put(entry, STATE_INFLATED);
mBindEventManager.notifyViewBound(entry);
- mNotifInflatingFilter.invalidateList();
+ mNotifInflatingFilter.invalidateList("onInflationFinished for " + logKey(entry));
}
private void freeNotifViews(NotificationEntry entry) {
mViewBarn.removeViewForEntry(entry);
+ mNotifInflater.releaseViews(entry);
// TODO: clear the entry's row here, or even better, stop setting the row on the entry!
mInflationStates.put(entry, STATE_UNINFLATED);
}
@@ -397,20 +403,20 @@ public class PreparationCoordinator implements Coordinator {
return false;
}
if (isBeyondGroupInitializationWindow(group, now)) {
- mLogger.logGroupInflationTookTooLong(group.getKey());
+ mLogger.logGroupInflationTookTooLong(group);
return false;
}
if (mInflatingNotifs.contains(group.getSummary())) {
- mLogger.logDelayingGroupRelease(group.getKey(), group.getSummary().getKey());
+ mLogger.logDelayingGroupRelease(group, group.getSummary());
return true;
}
for (NotificationEntry child : group.getChildren()) {
if (mInflatingNotifs.contains(child) && !child.wasAttachedInPreviousPass()) {
- mLogger.logDelayingGroupRelease(group.getKey(), child.getKey());
+ mLogger.logDelayingGroupRelease(group, child);
return true;
}
}
- mLogger.logDoneWaitingForGroupInflation(group.getKey());
+ mLogger.logDoneWaitingForGroupInflation(group);
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
index f8352500923e..30f13152126c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
@@ -19,48 +19,51 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
class PreparationCoordinatorLogger @Inject constructor(
@NotificationLog private val buffer: LogBuffer
) {
- fun logNotifInflated(key: String) {
+ fun logNotifInflated(entry: NotificationEntry) {
buffer.log(TAG, LogLevel.DEBUG, {
- str1 = key
+ str1 = entry.logKey
}, {
"NOTIF INFLATED $str1"
})
}
- fun logInflationAborted(key: String, reason: String) {
+ fun logInflationAborted(entry: NotificationEntry, reason: String) {
buffer.log(TAG, LogLevel.DEBUG, {
- str1 = key
+ str1 = entry.logKey
str2 = reason
}, {
"NOTIF INFLATION ABORTED $str1 reason=$str2"
})
}
- fun logDoneWaitingForGroupInflation(groupKey: String) {
+ fun logDoneWaitingForGroupInflation(group: GroupEntry) {
buffer.log(TAG, LogLevel.DEBUG, {
- str1 = groupKey
+ str1 = group.logKey
}, {
"Finished inflating all members of group $str1, releasing group"
})
}
- fun logGroupInflationTookTooLong(groupKey: String) {
+ fun logGroupInflationTookTooLong(group: GroupEntry) {
buffer.log(TAG, LogLevel.WARNING, {
- str1 = groupKey
+ str1 = group.logKey
}, {
"Group inflation took too long for $str1, releasing children early"
})
}
- fun logDelayingGroupRelease(groupKey: String, childKey: String) {
+ fun logDelayingGroupRelease(group: GroupEntry, child: NotificationEntry) {
buffer.log(TAG, LogLevel.DEBUG, {
- str1 = groupKey
- str2 = childKey
+ str1 = group.logKey
+ str2 = child.logKey
}, {
"Delaying release of group $str1 because child $str2 is still inflating"
})
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 57fd1975e13a..67a8a63ad7da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.SectionClassifier;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -28,6 +27,7 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider;
import com.android.systemui.statusbar.notification.collection.render.NodeController;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.dagger.AlertingHeader;
@@ -51,7 +51,7 @@ public class RankingCoordinator implements Coordinator {
public static final boolean SHOW_ALL_SECTIONS = false;
private final StatusBarStateController mStatusBarStateController;
private final HighPriorityProvider mHighPriorityProvider;
- private final SectionClassifier mSectionClassifier;
+ private final SectionStyleProvider mSectionStyleProvider;
private final NodeController mSilentNodeController;
private final SectionHeaderController mSilentHeaderController;
private final NodeController mAlertingHeaderController;
@@ -62,13 +62,13 @@ public class RankingCoordinator implements Coordinator {
public RankingCoordinator(
StatusBarStateController statusBarStateController,
HighPriorityProvider highPriorityProvider,
- SectionClassifier sectionClassifier,
+ SectionStyleProvider sectionStyleProvider,
@AlertingHeader NodeController alertingHeaderController,
@SilentHeader SectionHeaderController silentHeaderController,
@SilentHeader NodeController silentNodeController) {
mStatusBarStateController = statusBarStateController;
mHighPriorityProvider = highPriorityProvider;
- mSectionClassifier = sectionClassifier;
+ mSectionStyleProvider = sectionStyleProvider;
mAlertingHeaderController = alertingHeaderController;
mSilentNodeController = silentNodeController;
mSilentHeaderController = silentHeaderController;
@@ -77,7 +77,7 @@ public class RankingCoordinator implements Coordinator {
@Override
public void attach(NotifPipeline pipeline) {
mStatusBarStateController.addCallback(mStatusBarStateCallback);
- mSectionClassifier.setMinimizedSections(Collections.singleton(mMinimizedNotifSectioner));
+ mSectionStyleProvider.setMinimizedSections(Collections.singleton(mMinimizedNotifSectioner));
pipeline.addPreGroupFilter(mSuspendedFilter);
pipeline.addPreGroupFilter(mDndVisualEffectsFilter);
@@ -199,7 +199,7 @@ public class RankingCoordinator implements Coordinator {
new StatusBarStateController.StateListener() {
@Override
public void onDozingChanged(boolean isDozing) {
- mDndVisualEffectsFilter.invalidateList();
+ mDndVisualEffectsFilter.invalidateList("onDozingChanged to " + isDozing);
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
index 4e9d3ac07a96..1494574b26f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -19,11 +19,11 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import android.content.Context
import com.android.systemui.R
import com.android.systemui.statusbar.notification.AssistantFeedbackController
-import com.android.systemui.statusbar.notification.SectionClassifier
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
import com.android.systemui.statusbar.notification.collection.render.NotifRowController
import javax.inject.Inject
@@ -35,7 +35,7 @@ import javax.inject.Inject
class RowAppearanceCoordinator @Inject internal constructor(
context: Context,
private var mAssistantFeedbackController: AssistantFeedbackController,
- private var mSectionClassifier: SectionClassifier
+ private var mSectionStyleProvider: SectionStyleProvider
) : Coordinator {
private var entryToExpand: NotificationEntry? = null
@@ -55,7 +55,7 @@ class RowAppearanceCoordinator @Inject internal constructor(
private fun onBeforeRenderList(list: List<ListEntry>) {
entryToExpand = list.firstOrNull()?.representativeEntry?.takeIf { entry ->
- !mSectionClassifier.isMinimizedSection(entry.section!!)
+ !mSectionStyleProvider.isMinimizedSection(entry.section!!)
}
}
@@ -68,4 +68,4 @@ class RowAppearanceCoordinator @Inject internal constructor(
// Show the "alerted" bell icon
controller.setLastAudiblyAlertedMs(entry.lastAudiblyAlertedMs)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index 3f8a39f62dfb..aeeeb4fb054d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -64,7 +64,7 @@ private class SensitiveContentCoordinatorImpl @Inject constructor(
pipeline.addPreRenderInvalidator(this)
}
- override fun onDynamicPrivacyChanged(): Unit = invalidateList()
+ override fun onDynamicPrivacyChanged(): Unit = invalidateList("onDynamicPrivacyChanged")
override fun onBeforeRenderList(entries: List<ListEntry>) {
if (keyguardStateController.isKeyguardGoingAway() ||
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SharedCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SharedCoordinatorLogger.kt
deleted file mode 100644
index 25bc641f0a84..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SharedCoordinatorLogger.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.notification.collection.coordinator
-
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.dagger.NotificationLog
-import javax.inject.Inject
-
-/**
- * Shared logging class for coordinators that don't log enough to merit their own logger.
- */
-class SharedCoordinatorLogger @Inject constructor(
- @NotificationLog private val buffer: LogBuffer
-) {
- fun logUserOrProfileChanged(userId: Int, profiles: String) {
- buffer.log("NotCurrentUserFilter", LogLevel.INFO, {
- int1 = userId
- str1 = profiles
- }, {
- "Current user or profiles changed. Current user is $int1; profiles are $str1"
- })
- }
-
- fun logKeyguardCoordinatorInvalidated(reason: String) {
- buffer.log("KeyguardCoordinator", LogLevel.DEBUG, {
- str1 = reason
- }, {
- "KeyguardCoordinator invalidated: $str1"
- })
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt
index 48f00ac4bf55..9d0f974ea68f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt
@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.notification.logKey
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.time.SystemClock
import java.util.concurrent.TimeUnit.SECONDS
@@ -65,13 +66,6 @@ class SmartspaceDedupingCoordinator @Inject constructor(
statusBarStateController.addCallback(statusBarStateListener)
smartspaceController.addListener(this::onNewSmartspaceTargets)
- if (!pipeline.isNewPipelineEnabled) {
- // TODO (b/173126564): Remove this once the old pipeline is no longer necessary
- notificationLockscreenUserManager.addKeyguardNotificationSuppressor { entry ->
- isDupedWithSmartspaceContent(entry)
- }
- }
-
recordStatusBarState(statusBarStateController.state)
}
@@ -137,7 +131,7 @@ class SmartspaceDedupingCoordinator @Inject constructor(
}
if (changed) {
- filter.invalidateList()
+ filter.invalidateList("onNewSmartspaceTargets")
notificationEntryManager.updateNotifications("Smartspace targets changed")
}
@@ -174,7 +168,7 @@ class SmartspaceDedupingCoordinator @Inject constructor(
target.cancelTimeoutRunnable = executor.executeDelayed({
target.cancelTimeoutRunnable = null
target.shouldFilter = true
- filter.invalidateList()
+ filter.invalidateList("updateAlertException: ${entry.logKey}")
notificationEntryManager.updateNotifications("deduping timeout expired")
}, alertExceptionExpires - now)
}
@@ -191,7 +185,7 @@ class SmartspaceDedupingCoordinator @Inject constructor(
isOnLockscreen = newState == StatusBarState.KEYGUARD
if (isOnLockscreen != wasOnLockscreen) {
- filter.invalidateList()
+ filter.invalidateList("recordStatusBarState: " + StatusBarState.toString(newState))
// No need to call notificationEntryManager.updateNotifications; something else already
// does it for us when the keyguard state changes
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index 31a13e6faa8c..657c394d4e30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -16,15 +16,18 @@
package com.android.systemui.statusbar.notification.collection.coordinator
+import android.util.Log
import com.android.internal.widget.MessagingGroup
import com.android.internal.widget.MessagingMessage
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.row.NotificationGutsManager
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.Compile
import javax.inject.Inject
/**
@@ -38,24 +41,50 @@ class ViewConfigCoordinator @Inject internal constructor(
private val mLockscreenUserManager: NotificationLockscreenUserManager,
private val mGutsManager: NotificationGutsManager,
private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor
-) : Coordinator, UserChangedListener, ConfigurationController.ConfigurationListener {
+) : Coordinator, ConfigurationController.ConfigurationListener {
+ private var mIsSwitchingUser = false
private var mReinflateNotificationsOnUserSwitched = false
private var mDispatchUiModeChangeOnUserSwitched = false
private var mPipeline: NotifPipeline? = null
+ private val mKeyguardUpdateCallback = object : KeyguardUpdateMonitorCallback() {
+ override fun onUserSwitching(userId: Int) {
+ log { "ViewConfigCoordinator.onUserSwitching(userId=$userId)" }
+ mIsSwitchingUser = true
+ }
+
+ override fun onUserSwitchComplete(userId: Int) {
+ log { "ViewConfigCoordinator.onUserSwitchComplete(userId=$userId)" }
+ mIsSwitchingUser = false
+ applyChangesOnUserSwitched()
+ }
+ }
+
+ private val mUserChangedListener = object : UserChangedListener {
+ override fun onUserChanged(userId: Int) {
+ log { "ViewConfigCoordinator.onUserChanged(userId=$userId)" }
+ applyChangesOnUserSwitched()
+ }
+ }
+
override fun attach(pipeline: NotifPipeline) {
mPipeline = pipeline
- if (pipeline.isNewPipelineEnabled) {
- mLockscreenUserManager.addUserChangedListener(this)
- mConfigurationController.addCallback(this)
- }
+ mLockscreenUserManager.addUserChangedListener(mUserChangedListener)
+ mConfigurationController.addCallback(this)
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback)
}
override fun onDensityOrFontScaleChanged() {
+ log {
+ val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser
+ "ViewConfigCoordinator.onDensityOrFontScaleChanged()" +
+ " isSwitchingUser=$mIsSwitchingUser" +
+ " keyguardUpdateMonitor.isSwitchingUser=$keyguardIsSwitchingUser"
+ }
MessagingMessage.dropCache()
MessagingGroup.dropCache()
- if (!mKeyguardUpdateMonitor.isSwitchingUser) {
+ if (!mIsSwitchingUser) {
updateNotificationsOnDensityOrFontScaleChanged()
} else {
mReinflateNotificationsOnUserSwitched = true
@@ -63,7 +92,13 @@ class ViewConfigCoordinator @Inject internal constructor(
}
override fun onUiModeChanged() {
- if (!mKeyguardUpdateMonitor.isSwitchingUser) {
+ log {
+ val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser
+ "ViewConfigCoordinator.onUiModeChanged()" +
+ " isSwitchingUser=$mIsSwitchingUser" +
+ " keyguardUpdateMonitor.isSwitchingUser=$keyguardIsSwitchingUser"
+ }
+ if (!mIsSwitchingUser) {
updateNotificationsOnUiModeChanged()
} else {
mDispatchUiModeChangeOnUserSwitched = true
@@ -74,7 +109,7 @@ class ViewConfigCoordinator @Inject internal constructor(
onDensityOrFontScaleChanged()
}
- override fun onUserChanged(userId: Int) {
+ private fun applyChangesOnUserSwitched() {
if (mReinflateNotificationsOnUserSwitched) {
updateNotificationsOnDensityOrFontScaleChanged()
mReinflateNotificationsOnUserSwitched = false
@@ -86,9 +121,9 @@ class ViewConfigCoordinator @Inject internal constructor(
}
private fun updateNotificationsOnUiModeChanged() {
+ log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" }
mPipeline?.allNotifs?.forEach { entry ->
- val row = entry.row
- row?.onUiModeChanged()
+ entry.row?.onUiModeChanged()
}
}
@@ -101,4 +136,13 @@ class ViewConfigCoordinator @Inject internal constructor(
}
}
}
-} \ No newline at end of file
+
+ private inline fun log(message: () -> String) {
+ if (DEBUG) Log.d(TAG, message())
+ }
+
+ companion object {
+ private const val TAG = "ViewConfigCoordinator"
+ private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index d7bd95c0949e..d3bc257d8a54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -16,8 +16,9 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
-import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
-import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
+
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -27,14 +28,15 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.NotifPanelEvents;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
-import com.android.systemui.statusbar.phone.NotifPanelEvents;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.util.Compile;
import com.android.systemui.util.concurrency.DelayableExecutor;
import java.io.PrintWriter;
@@ -54,6 +56,8 @@ import javax.inject.Inject;
@SysUISingleton
public class VisualStabilityCoordinator implements Coordinator, Dumpable,
NotifPanelEvents.Listener {
+ public static final String TAG = "VisualStability";
+ public static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
private final DelayableExecutor mDelayableExecutor;
private final HeadsUpManager mHeadsUpManager;
private final NotifPanelEvents mNotifPanelEvents;
@@ -61,7 +65,8 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
private final VisualStabilityProvider mVisualStabilityProvider;
private final WakefulnessLifecycle mWakefulnessLifecycle;
- private boolean mScreenOn;
+ private boolean mSleepy = true;
+ private boolean mFullyDozed;
private boolean mPanelExpanded;
private boolean mPulsing;
private boolean mNotifPanelCollapsing;
@@ -104,8 +109,8 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
@Override
public void attach(NotifPipeline pipeline) {
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
- mScreenOn = mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE
- || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING;
+ mSleepy = mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP;
+ mFullyDozed = mStatusBarStateController.getDozeAmount() == 1f;
mStatusBarStateController.addCallback(mStatusBarStateControllerListener);
mPulsing = mStatusBarStateController.isPulsing();
@@ -113,6 +118,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
pipeline.setVisualStabilityManager(mNotifStabilityManager);
}
+
// TODO(b/203826051): Ensure stability manager can allow reordering off-screen
// HUNs to the top of the shade
private final NotifStabilityManager mNotifStabilityManager =
@@ -174,14 +180,28 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
}
};
- private void updateAllowedStates() {
+ private void updateAllowedStates(String field, boolean value) {
+ boolean wasPipelineRunAllowed = mPipelineRunAllowed;
+ boolean wasReorderingAllowed = mReorderingAllowed;
mPipelineRunAllowed = !isPanelCollapsingOrLaunchingActivity();
mReorderingAllowed = isReorderingAllowed();
- if ((mPipelineRunAllowed && mIsSuppressingPipelineRun)
- || (mReorderingAllowed && (mIsSuppressingGroupChange
- || isSuppressingSectionChange()
- || mIsSuppressingEntryReorder))) {
- mNotifStabilityManager.invalidateList();
+ if (DEBUG && (wasPipelineRunAllowed != mPipelineRunAllowed
+ || wasReorderingAllowed != mReorderingAllowed)) {
+ Log.d(TAG, "Stability allowances changed:"
+ + " pipelineRunAllowed " + wasPipelineRunAllowed + "->" + mPipelineRunAllowed
+ + " reorderingAllowed " + wasReorderingAllowed + "->" + mReorderingAllowed
+ + " when setting " + field + "=" + value);
+ }
+ if (mPipelineRunAllowed && mIsSuppressingPipelineRun) {
+ mNotifStabilityManager.invalidateList("pipeline run suppression ended");
+ } else if (mReorderingAllowed && (mIsSuppressingGroupChange
+ || isSuppressingSectionChange()
+ || mIsSuppressingEntryReorder)) {
+ String reason = "reorder suppression ended for"
+ + " group=" + mIsSuppressingGroupChange
+ + " section=" + isSuppressingSectionChange()
+ + " sort=" + mIsSuppressingEntryReorder;
+ mNotifStabilityManager.invalidateList(reason);
}
mVisualStabilityProvider.setReorderingAllowed(mReorderingAllowed);
}
@@ -195,7 +215,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
}
private boolean isReorderingAllowed() {
- return (!mScreenOn || !mPanelExpanded) && !mPulsing;
+ return ((mFullyDozed && mSleepy) || !mPanelExpanded) && !mPulsing;
}
/**
@@ -226,7 +246,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
now + ALLOW_SECTION_CHANGE_TIMEOUT));
if (!wasSectionChangeAllowed) {
- mNotifStabilityManager.invalidateList();
+ mNotifStabilityManager.invalidateList("temporarilyAllowSectionChanges");
}
}
@@ -235,27 +255,37 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
@Override
public void onPulsingChanged(boolean pulsing) {
mPulsing = pulsing;
- updateAllowedStates();
+ updateAllowedStates("pulsing", pulsing);
}
@Override
public void onExpandedChanged(boolean expanded) {
mPanelExpanded = expanded;
- updateAllowedStates();
+ updateAllowedStates("panelExpanded", expanded);
+ }
+
+ @Override
+ public void onDozeAmountChanged(float linear, float eased) {
+ final boolean fullyDozed = linear == 1f;
+ mFullyDozed = fullyDozed;
+ updateAllowedStates("fullyDozed", fullyDozed);
}
};
final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
@Override
public void onFinishedGoingToSleep() {
- mScreenOn = false;
- updateAllowedStates();
+ // NOTE: this method is called much earlier than what we consider "finished" going to
+ // sleep (the animation isn't done), so we also need to check the doze amount is not 1
+ // and use the combo to determine that the locked shade is not visible.
+ mSleepy = true;
+ updateAllowedStates("sleepy", true);
}
@Override
public void onStartedWakingUp() {
- mScreenOn = true;
- updateAllowedStates();
+ mSleepy = false;
+ updateAllowedStates("sleepy", false);
}
};
@@ -265,7 +295,8 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
pw.println(" notifPanelCollapsing: " + mNotifPanelCollapsing);
pw.println(" launchingNotifActivity: " + mNotifPanelLaunchingActivity);
pw.println("reorderingAllowed: " + mReorderingAllowed);
- pw.println(" screenOn: " + mScreenOn);
+ pw.println(" sleepy: " + mSleepy);
+ pw.println(" fullyDozed: " + mFullyDozed);
pw.println(" panelExpanded: " + mPanelExpanded);
pw.println(" pulsing: " + mPulsing);
pw.println("isSuppressingPipelineRun: " + mIsSuppressingPipelineRun);
@@ -285,12 +316,12 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable,
@Override
public void onPanelCollapsingChanged(boolean isCollapsing) {
mNotifPanelCollapsing = isCollapsing;
- updateAllowedStates();
+ updateAllowedStates("notifPanelCollapsing", isCollapsing);
}
@Override
public void onLaunchingActivityChanged(boolean isLaunchingActivity) {
mNotifPanelLaunchingActivity = isLaunchingActivity;
- updateAllowedStates();
+ updateAllowedStates("notifPanelLaunchingActivity", isLaunchingActivity);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
index d98e7f76a11b..08e21e8f668e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
@@ -42,9 +42,14 @@ interface NotifInflater {
/**
* Request to stop the inflation of an entry. For example, called when a notification is
- * removed and no longer needs to be inflated.
+ * removed and no longer needs to be inflated. Returns whether anything may have been aborted.
*/
- fun abortInflation(entry: NotificationEntry)
+ fun abortInflation(entry: NotificationEntry): Boolean
+
+ /**
+ * Called to let the system remove the content views from the notification row.
+ */
+ fun releaseViews(entry: NotificationEntry)
/**
* Callback once all the views are inflated and bound for a given NotificationEntry.
@@ -57,4 +62,4 @@ interface NotifInflater {
* A class holding parameters used when inflating the notification row
*/
class Params(val isLowPriority: Boolean, val reason: String)
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
index 3475fcf7c12f..ee0b00807e27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
@@ -31,6 +31,7 @@ class NotifUiAdjustment internal constructor(
val smartActions: List<Notification.Action>,
val smartReplies: List<CharSequence>,
val isConversation: Boolean,
+ val isSnoozeEnabled: Boolean,
val isMinimized: Boolean,
val needsRedaction: Boolean,
) {
@@ -42,6 +43,7 @@ class NotifUiAdjustment internal constructor(
): Boolean = when {
oldAdjustment === newAdjustment -> false
oldAdjustment.isConversation != newAdjustment.isConversation -> true
+ oldAdjustment.isSnoozeEnabled != newAdjustment.isSnoozeEnabled -> true
oldAdjustment.isMinimized != newAdjustment.isMinimized -> true
oldAdjustment.needsRedaction != newAdjustment.needsRedaction -> true
areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions) -> true
@@ -57,9 +59,9 @@ class NotifUiAdjustment internal constructor(
first.size != second.size -> true
else -> first.asSequence().zip(second.asSequence()).any {
(!TextUtils.equals(it.first.title, it.second.title)) ||
- (areDifferent(it.first.getIcon(), it.second.getIcon())) ||
- (it.first.actionIntent != it.second.actionIntent) ||
- (areDifferent(it.first.remoteInputs, it.second.remoteInputs))
+ (areDifferent(it.first.getIcon(), it.second.getIcon())) ||
+ (it.first.actionIntent != it.second.actionIntent) ||
+ (areDifferent(it.first.remoteInputs, it.second.remoteInputs))
}
}
@@ -78,7 +80,7 @@ class NotifUiAdjustment internal constructor(
first.size != second.size -> true
else -> first.asSequence().zip(second.asSequence()).any {
(!TextUtils.equals(it.first.label, it.second.label)) ||
- (areDifferent(it.first.choices, it.second.choices))
+ (areDifferent(it.first.choices, it.second.choices))
}
}
@@ -94,4 +96,4 @@ class NotifUiAdjustment internal constructor(
}
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index f7b6376c717c..745d6fe1d624 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -16,12 +16,18 @@
package com.android.systemui.statusbar.notification.collection.inflation
+import android.database.ContentObserver
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.NotificationLockscreenUserManager
-import com.android.systemui.statusbar.notification.SectionClassifier
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
import com.android.systemui.util.ListenerSet
+import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject
/**
@@ -30,14 +36,23 @@ import javax.inject.Inject
*/
@SysUISingleton
class NotifUiAdjustmentProvider @Inject constructor(
+ @Main private val handler: Handler,
+ private val secureSettings: SecureSettings,
private val lockscreenUserManager: NotificationLockscreenUserManager,
- private val sectionClassifier: SectionClassifier,
+ private val sectionStyleProvider: SectionStyleProvider
) {
private val dirtyListeners = ListenerSet<Runnable>()
+ private var isSnoozeEnabled = false
fun addDirtyListener(listener: Runnable) {
if (dirtyListeners.isEmpty()) {
lockscreenUserManager.addNotificationStateChangedListener(notifStateChangedListener)
+ updateSnoozeEnabled()
+ secureSettings.registerContentObserverForUser(
+ SHOW_NOTIFICATION_SNOOZE,
+ settingsObserver,
+ UserHandle.USER_ALL
+ )
}
dirtyListeners.addIfAbsent(listener)
}
@@ -46,6 +61,7 @@ class NotifUiAdjustmentProvider @Inject constructor(
dirtyListeners.remove(listener)
if (dirtyListeners.isEmpty()) {
lockscreenUserManager.removeNotificationStateChangedListener(notifStateChangedListener)
+ secureSettings.unregisterContentObserver(settingsObserver)
}
}
@@ -54,10 +70,21 @@ class NotifUiAdjustmentProvider @Inject constructor(
dirtyListeners.forEach(Runnable::run)
}
+ private val settingsObserver = object : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean) {
+ updateSnoozeEnabled()
+ dirtyListeners.forEach(Runnable::run)
+ }
+ }
+
+ private fun updateSnoozeEnabled() {
+ isSnoozeEnabled = secureSettings.getInt(SHOW_NOTIFICATION_SNOOZE, 0) == 1
+ }
+
private fun isEntryMinimized(entry: NotificationEntry): Boolean {
val section = entry.section ?: error("Entry must have a section to determine if minimized")
val parent = entry.parent ?: error("Entry must have a parent to determine if minimized")
- val isMinimizedSection = sectionClassifier.isMinimizedSection(section)
+ val isMinimizedSection = sectionStyleProvider.isMinimizedSection(section)
val isTopLevelEntry = parent == GroupEntry.ROOT_ENTRY
val isGroupSummary = parent.summary == entry
return isMinimizedSection && (isTopLevelEntry || isGroupSummary)
@@ -73,7 +100,8 @@ class NotifUiAdjustmentProvider @Inject constructor(
smartActions = entry.ranking.smartActions,
smartReplies = entry.ranking.smartReplies,
isConversation = entry.ranking.isConversation,
+ isSnoozeEnabled = isSnoozeEnabled,
isMinimized = isEntryMinimized(entry),
needsRedaction = lockscreenUserManager.needsRedaction(entry),
)
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
index 3a4701c9ac76..46b467e8962d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
@@ -50,4 +50,9 @@ public interface NotificationRowBinder {
NotificationUiAdjustment oldAdjustment,
NotificationUiAdjustment newAdjustment,
NotificationRowContentBinder.InflationCallback callback);
+
+ /**
+ * Called when a notification is no longer likely to be displayed and can have its views freed.
+ */
+ void releaseViews(NotificationEntry entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index b84a797bbb6f..528f7203347f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.inflation;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
import static java.util.Objects.requireNonNull;
@@ -161,6 +163,18 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
}
}
+ @Override
+ public void releaseViews(NotificationEntry entry) {
+ if (!entry.rowExists()) {
+ return;
+ }
+ final RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
+ params.markContentViewsFreeable(FLAG_CONTENT_VIEW_CONTRACTED);
+ params.markContentViewsFreeable(FLAG_CONTENT_VIEW_EXPANDED);
+ params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC);
+ mRowContentBindStage.requestRebind(entry, null);
+ }
+
/**
* Bind row to various controllers and managers. This is only called when the row is first
* created.
@@ -249,6 +263,8 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
}
RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
+ params.requireContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
+ params.requireContentViews(FLAG_CONTENT_VIEW_EXPANDED);
params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
params.setUseLowPriority(isLowPriority);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index 7dd3672a6e30..a7719d3d82a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -23,6 +23,7 @@ import android.service.notification.NotificationStats;
import androidx.annotation.NonNull;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
@@ -33,10 +34,13 @@ import com.android.systemui.statusbar.notification.collection.render.Notificatio
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import javax.inject.Inject;
+
/**
* Callback for when a user interacts with a {@see ExpandableNotificationRow}. Sends relevant
* information about the interaction to the notification pipeline.
*/
+@SysUISingleton
public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback {
private final NotificationVisibilityProvider mVisibilityProvider;
private final NotifCollection mNotifCollection;
@@ -44,6 +48,7 @@ public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback
private final StatusBarStateController mStatusBarStateController;
private final VisualStabilityCoordinator mVisualStabilityCoordinator;
+ @Inject
public OnUserInteractionCallbackImpl(
NotificationVisibilityProvider visibilityProvider,
NotifCollection notifCollection,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index 70b11569f50b..24ef5808b2e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -22,7 +22,6 @@ import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -53,7 +52,6 @@ public class NotifPipelineInitializer implements Dumpable {
private final NotifInflaterImpl mNotifInflater;
private final DumpManager mDumpManager;
private final ShadeViewManagerFactory mShadeViewManagerFactory;
- private final NotifPipelineFlags mNotifPipelineFlags;
@Inject
@@ -66,8 +64,7 @@ public class NotifPipelineInitializer implements Dumpable {
NotifCoordinators notifCoordinators,
NotifInflaterImpl notifInflater,
DumpManager dumpManager,
- ShadeViewManagerFactory shadeViewManagerFactory,
- NotifPipelineFlags notifPipelineFlags
+ ShadeViewManagerFactory shadeViewManagerFactory
) {
mPipelineWrapper = pipelineWrapper;
mGroupCoalescer = groupCoalescer;
@@ -78,7 +75,6 @@ public class NotifPipelineInitializer implements Dumpable {
mDumpManager = dumpManager;
mNotifInflater = notifInflater;
mShadeViewManagerFactory = shadeViewManagerFactory;
- mNotifPipelineFlags = notifPipelineFlags;
}
/** Hooks the new pipeline up to NotificationManager */
@@ -91,26 +87,22 @@ public class NotifPipelineInitializer implements Dumpable {
mDumpManager.registerDumpable("NotifPipeline", this);
// Setup inflation
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
- mNotifInflater.setRowBinder(rowBinder);
- }
+ mNotifInflater.setRowBinder(rowBinder);
// Wire up coordinators
mNotifPluggableCoordinators.attach(mPipelineWrapper);
// Wire up pipeline
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
- mShadeViewManagerFactory
- .create(listContainer, stackController)
- .attach(mRenderStageManager);
- }
+ mShadeViewManagerFactory
+ .create(listContainer, stackController)
+ .attach(mRenderStageManager);
mRenderStageManager.attach(mListBuilder);
mListBuilder.attach(mNotifCollection);
mNotifCollection.attach(mGroupCoalescer);
mGroupCoalescer.attach(notificationService);
Log.d(TAG, "Notif pipeline initialized."
- + " rendering=" + mNotifPipelineFlags.isNewPipelineEnabled());
+ + " rendering=" + true);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java
deleted file mode 100644
index bdbb0eb48e8a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.legacy;
-
-import static com.android.systemui.statusbar.phone.CentralSurfaces.SPEW;
-
-import android.service.notification.StatusBarNotification;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
-
-import org.jetbrains.annotations.NotNull;
-
-import javax.inject.Inject;
-
-/**
- * This is some logic extracted from the
- * {@link com.android.systemui.statusbar.phone.StatusBarNotificationPresenter}
- * into a class that implements a new-pipeline interface so that the new pipeline can implement it
- * correctly.
- *
- * Specifically, this is the logic which updates notifications when uiMode and screen properties
- * change, and which closes the shade when the last notification disappears.
- */
-public class LegacyNotificationPresenterExtensions implements NotifShadeEventSource {
- private static final String TAG = "LegacyNotifPresenter";
- private final NotificationEntryManager mEntryManager;
- private boolean mEntryListenerAdded;
- private Runnable mShadeEmptiedCallback;
- private Runnable mNotifRemovedByUserCallback;
-
- @Inject
- public LegacyNotificationPresenterExtensions(NotificationEntryManager entryManager) {
- mEntryManager = entryManager;
- }
-
- private void ensureEntryListenerAdded() {
- if (mEntryListenerAdded) return;
- mEntryListenerAdded = true;
- mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
- @Override
- public void onEntryRemoved(
- @NotNull NotificationEntry entry,
- NotificationVisibility visibility,
- boolean removedByUser,
- int reason) {
- StatusBarNotification old = entry.getSbn();
- if (SPEW) {
- Log.d(TAG, "removeNotification key=" + entry.getKey()
- + " old=" + old + " reason=" + reason);
- }
-
- if (old != null && !mEntryManager.hasActiveNotifications()) {
- if (mShadeEmptiedCallback != null) mShadeEmptiedCallback.run();
- }
- if (removedByUser) {
- if (mNotifRemovedByUserCallback != null) mNotifRemovedByUserCallback.run();
- }
- }
- });
- }
-
- @Override
- public void setNotifRemovedByUserCallback(@NonNull Runnable callback) {
- if (mNotifRemovedByUserCallback != null) {
- throw new IllegalStateException("mNotifRemovedByUserCallback already set");
- }
- mNotifRemovedByUserCallback = callback;
- ensureEntryListenerAdded();
- }
-
- @Override
- public void setShadeEmptiedCallback(@NonNull Runnable callback) {
- if (mShadeEmptiedCallback != null) {
- throw new IllegalStateException("mShadeEmptiedCallback already set");
- }
- mShadeEmptiedCallback = callback;
- ensureEntryListenerAdded();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
deleted file mode 100644
index 103b14b09e9c..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.notification.collection.legacy;
-
-import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-
-import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationStats;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
-import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-
-/**
- * Callback for when a user interacts with a {@see ExpandableNotificationRow}.
- */
-public class OnUserInteractionCallbackImplLegacy implements OnUserInteractionCallback {
- private final NotificationEntryManager mNotificationEntryManager;
- private final NotificationVisibilityProvider mVisibilityProvider;
- private final HeadsUpManager mHeadsUpManager;
- private final StatusBarStateController mStatusBarStateController;
- private final VisualStabilityManager mVisualStabilityManager;
- private final GroupMembershipManager mGroupMembershipManager;
-
- public OnUserInteractionCallbackImplLegacy(
- NotificationEntryManager notificationEntryManager,
- NotificationVisibilityProvider visibilityProvider,
- HeadsUpManager headsUpManager,
- StatusBarStateController statusBarStateController,
- VisualStabilityManager visualStabilityManager,
- GroupMembershipManager groupMembershipManager
- ) {
- mNotificationEntryManager = notificationEntryManager;
- mVisibilityProvider = visibilityProvider;
- mHeadsUpManager = headsUpManager;
- mStatusBarStateController = statusBarStateController;
- mVisualStabilityManager = visualStabilityManager;
- mGroupMembershipManager = groupMembershipManager;
- }
-
- /**
- * Callback triggered when a user:
- * 1. Manually dismisses a notification {@see ExpandableNotificationRow}.
- * 2. Clicks on a notification with flag {@link android.app.Notification#FLAG_AUTO_CANCEL}.
- * {@see StatusBarNotificationActivityStarter}
- *
- * @param groupSummaryToDismiss the group summary that should be dismissed
- * along with this dismissal. If null, does not additionally
- * dismiss any notifications.
- */
- private void onDismiss(
- NotificationEntry entry,
- @NotificationListenerService.NotificationCancelReason int cancellationReason,
- @Nullable NotificationEntry groupSummaryToDismiss
- ) {
- int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
- if (mHeadsUpManager.isAlerting(entry.getKey())) {
- dismissalSurface = NotificationStats.DISMISSAL_PEEK;
- } else if (mStatusBarStateController.isDozing()) {
- dismissalSurface = NotificationStats.DISMISSAL_AOD;
- }
-
- if (groupSummaryToDismiss != null) {
- onDismiss(groupSummaryToDismiss, cancellationReason, null);
- }
-
- mNotificationEntryManager.performRemoveNotification(
- entry.getSbn(),
- new DismissedByUserStats(
- dismissalSurface,
- DISMISS_SENTIMENT_NEUTRAL,
- mVisibilityProvider.obtain(entry, true)),
- cancellationReason
- );
-
- }
-
- @Override
- public void onImportanceChanged(NotificationEntry entry) {
- mVisualStabilityManager.temporarilyAllowReordering();
- }
-
- /**
- * @param entry that is being dismissed
- * @return the group summary to dismiss along with this entry if this is the last entry in
- * the group. Else, returns null.
- */
- @Nullable
- private NotificationEntry getGroupSummaryToDismiss(NotificationEntry entry) {
- if (mGroupMembershipManager.isOnlyChildInGroup(entry)) {
- NotificationEntry groupSummary = mGroupMembershipManager.getLogicalGroupSummary(entry);
- return groupSummary.isDismissable() ? groupSummary : null;
- }
- return null;
- }
-
- @Override
- @NonNull
- public Runnable registerFutureDismissal(@NonNull NotificationEntry entry,
- @CancellationReason int cancellationReason) {
- NotificationEntry groupSummaryToDismiss = getGroupSummaryToDismiss(entry);
- return () -> onDismiss(entry, cancellationReason, groupSummaryToDismiss);
- }
-}
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
index 263737e20a13..ea66f3b6dd42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
@@ -25,12 +25,9 @@ data class NotifSection(
val sectioner: NotifSectioner,
val index: Int
) {
- val label: String
- get() = "Section($index, $bucket, \"${sectioner.name}\")"
-
+ @PriorityBucket
+ val bucket: Int = sectioner.bucket
+ val label: String = "$index:$bucket:${sectioner.name}"
val headerController: NodeController? = sectioner.headerNodeController
-
val comparator: NotifComparator? = sectioner.comparator
-
- @PriorityBucket val bucket: Int = sectioner.bucket
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index f8bf85f92eae..4c406e3ba0b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -21,101 +21,121 @@ import com.android.systemui.log.LogLevel.DEBUG
import com.android.systemui.log.LogLevel.INFO
import com.android.systemui.log.LogLevel.WARNING
import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.StateName
+import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.getStateName
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
+import com.android.systemui.statusbar.notification.logKey
+import com.android.systemui.util.Compile
import javax.inject.Inject
class ShadeListBuilderLogger @Inject constructor(
+ notifPipelineFlags: NotifPipelineFlags,
@NotificationLog private val buffer: LogBuffer
) {
- fun logOnBuildList() {
+ fun logOnBuildList(reason: String?) {
buffer.log(TAG, INFO, {
+ str1 = reason
}, {
- "Request received from NotifCollection"
+ "Request received from NotifCollection for $str1"
})
}
- fun logEndBuildList(buildId: Int, topLevelEntries: Int, numChildren: Int) {
+ fun logEndBuildList(
+ buildId: Int,
+ topLevelEntries: Int,
+ numChildren: Int,
+ enforcedVisualStability: Boolean
+ ) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
int1 = topLevelEntries
int2 = numChildren
+ bool1 = enforcedVisualStability
}, {
- "(Build $long1) Build complete ($int1 top-level entries, $int2 children)"
+ "(Build $long1) Build complete ($int1 top-level entries, $int2 children)" +
+ " enforcedVisualStability=$bool1"
})
}
- fun logPreRenderInvalidated(filterName: String, pipelineState: Int) {
+ private fun logPluggableInvalidated(
+ type: String,
+ pluggable: Pluggable<*>,
+ @StateName pipelineState: Int,
+ reason: String?
+ ) {
buffer.log(TAG, DEBUG, {
- str1 = filterName
+ str1 = type
+ str2 = pluggable.name
int1 = pipelineState
+ str3 = reason
}, {
- """Pre-render Invalidator "$str1" invalidated; pipeline state is $int1"""
+ """Invalidated while ${getStateName(int1)} by $str1 "$str2" because $str3"""
})
}
- fun logPreGroupFilterInvalidated(filterName: String, pipelineState: Int) {
- buffer.log(TAG, DEBUG, {
- str1 = filterName
- int1 = pipelineState
- }, {
- """Pre-group NotifFilter "$str1" invalidated; pipeline state is $int1"""
- })
- }
+ fun logPreRenderInvalidated(
+ invalidator: Invalidator,
+ @StateName pipelineState: Int,
+ reason: String?
+ ) = logPluggableInvalidated("Pre-render Invalidator", invalidator, pipelineState, reason)
- fun logReorderingAllowedInvalidated(name: String, pipelineState: Int) {
- buffer.log(TAG, DEBUG, {
- str1 = name
- int1 = pipelineState
- }, {
- """ReorderingNowAllowed "$str1" invalidated; pipeline state is $int1"""
- })
- }
+ fun logPreGroupFilterInvalidated(
+ filter: NotifFilter,
+ @StateName pipelineState: Int,
+ reason: String?
+ ) = logPluggableInvalidated("Pre-group NotifFilter", filter, pipelineState, reason)
- fun logPromoterInvalidated(name: String, pipelineState: Int) {
- buffer.log(TAG, DEBUG, {
- str1 = name
- int1 = pipelineState
- }, {
- """NotifPromoter "$str1" invalidated; pipeline state is $int1"""
- })
- }
+ fun logReorderingAllowedInvalidated(
+ stabilityManager: NotifStabilityManager,
+ @StateName pipelineState: Int,
+ reason: String?
+ ) = logPluggableInvalidated("ReorderingNowAllowed", stabilityManager, pipelineState, reason)
- fun logNotifSectionInvalidated(name: String, pipelineState: Int) {
- buffer.log(TAG, DEBUG, {
- str1 = name
- int1 = pipelineState
- }, {
- """NotifSection "$str1" invalidated; pipeline state is $int1"""
- })
- }
+ fun logPromoterInvalidated(
+ promoter: NotifPromoter,
+ @StateName pipelineState: Int,
+ reason: String?
+ ) = logPluggableInvalidated("NotifPromoter", promoter, pipelineState, reason)
- fun logNotifComparatorInvalidated(name: String, pipelineState: Int) {
- buffer.log(TAG, DEBUG, {
- str1 = name
- int1 = pipelineState
- }, {
- """NotifComparator "$str1" invalidated; pipeline state is $int1"""
- })
- }
+ fun logNotifSectionInvalidated(
+ sectioner: NotifSectioner,
+ @StateName pipelineState: Int,
+ reason: String?
+ ) = logPluggableInvalidated("NotifSection", sectioner, pipelineState, reason)
- fun logFinalizeFilterInvalidated(name: String, pipelineState: Int) {
- buffer.log(TAG, DEBUG, {
- str1 = name
- int1 = pipelineState
- }, {
- """Finalize NotifFilter "$str1" invalidated; pipeline state is $int1"""
- })
- }
+ fun logNotifComparatorInvalidated(
+ comparator: NotifComparator,
+ @StateName pipelineState: Int,
+ reason: String?
+ ) = logPluggableInvalidated("NotifComparator", comparator, pipelineState, reason)
- fun logDuplicateSummary(buildId: Int, groupKey: String, existingKey: String, newKey: String) {
+ fun logFinalizeFilterInvalidated(
+ filter: NotifFilter,
+ @StateName pipelineState: Int,
+ reason: String?
+ ) = logPluggableInvalidated("Finalize NotifFilter", filter, pipelineState, reason)
+
+ fun logDuplicateSummary(
+ buildId: Int,
+ group: GroupEntry,
+ existingSummary: NotificationEntry,
+ newSummary: NotificationEntry
+ ) {
buffer.log(TAG, WARNING, {
long1 = buildId.toLong()
- str1 = groupKey
- str2 = existingKey
- str3 = newKey
+ str1 = group.logKey
+ str2 = existingSummary.logKey
+ str3 = newSummary.logKey
}, {
"""(Build $long1) Duplicate summary for group "$str1": "$str2" vs. "$str3""""
})
@@ -124,7 +144,7 @@ class ShadeListBuilderLogger @Inject constructor(
fun logDuplicateTopLevelKey(buildId: Int, topLevelKey: String) {
buffer.log(TAG, WARNING, {
long1 = buildId.toLong()
- str1 = topLevelKey
+ str1 = logKey(topLevelKey)
}, {
"(Build $long1) Duplicate top-level key: $str1"
})
@@ -132,15 +152,15 @@ class ShadeListBuilderLogger @Inject constructor(
fun logEntryAttachStateChanged(
buildId: Int,
- key: String,
+ entry: ListEntry,
prevParent: GroupEntry?,
newParent: GroupEntry?
) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
- str1 = key
- str2 = prevParent?.key
- str3 = newParent?.key
+ str1 = entry.logKey
+ str2 = prevParent?.logKey
+ str3 = newParent?.logKey
}, {
val action = if (str2 == null && str3 != null) {
@@ -160,8 +180,8 @@ class ShadeListBuilderLogger @Inject constructor(
fun logParentChanged(buildId: Int, prevParent: GroupEntry?, newParent: GroupEntry?) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
- str1 = prevParent?.key
- str2 = newParent?.key
+ str1 = prevParent?.logKey
+ str2 = newParent?.logKey
}, {
if (str1 == null && str2 != null) {
"(Build $long1) Parent is {$str2}"
@@ -180,8 +200,8 @@ class ShadeListBuilderLogger @Inject constructor(
) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
- str1 = suppressedParent?.key
- str2 = keepingParent?.key
+ str1 = suppressedParent?.logKey
+ str2 = keepingParent?.logKey
}, {
"(Build $long1) Change of parent to '$str1' suppressed; keeping parent '$str2'"
})
@@ -193,7 +213,7 @@ class ShadeListBuilderLogger @Inject constructor(
) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
- str1 = keepingParent?.key
+ str1 = keepingParent?.logKey
}, {
"(Build $long1) Group pruning suppressed; keeping parent '$str1'"
})
@@ -273,6 +293,8 @@ class ShadeListBuilderLogger @Inject constructor(
})
}
+ val logRankInFinalList = Compile.IS_DEBUG && notifPipelineFlags.isDevLoggingEnabled()
+
fun logFinalList(entries: List<ListEntry>) {
if (entries.isEmpty()) {
buffer.log(TAG, DEBUG, {}, { "(empty list)" })
@@ -281,26 +303,32 @@ class ShadeListBuilderLogger @Inject constructor(
val entry = entries[i]
buffer.log(TAG, DEBUG, {
int1 = i
- str1 = entry.key
+ str1 = entry.logKey
+ bool1 = logRankInFinalList
+ int2 = entry.representativeEntry!!.ranking.rank
}, {
- "[$int1] $str1"
+ "[$int1] $str1".let { if (bool1) "$it rank=$int2" else it }
})
if (entry is GroupEntry) {
entry.summary?.let {
buffer.log(TAG, DEBUG, {
- str1 = it.key
+ str1 = it.logKey
+ bool1 = logRankInFinalList
+ int2 = it.ranking.rank
}, {
- " [*] $str1 (summary)"
+ " [*] $str1 (summary)".let { if (bool1) "$it rank=$int2" else it }
})
}
for (j in entry.children.indices) {
val child = entry.children[j]
buffer.log(TAG, DEBUG, {
int1 = j
- str1 = child.key
+ str1 = child.logKey
+ bool1 = logRankInFinalList
+ int2 = child.ranking.rank
}, {
- " [$int1] $str1"
+ " [$int1] $str1".let { if (bool1) "$it rank=$int2" else it }
})
}
}
@@ -308,7 +336,7 @@ class ShadeListBuilderLogger @Inject constructor(
}
fun logPipelineRunSuppressed() =
- buffer.log(TAG, INFO, {}) { "Suppressing pipeline run during animation." }
+ buffer.log(TAG, INFO, {}) { "Suppressing pipeline run during animation." }
}
-private const val TAG = "ShadeListBuilder" \ No newline at end of file
+private const val TAG = "ShadeListBuilder"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
index b981a9621526..966ab4c61b50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
@@ -49,10 +49,10 @@ public abstract class Pluggable<This> {
* Call this method when something has caused this pluggable's behavior to change. The pipeline
* will be re-run.
*/
- public final void invalidateList() {
+ public final void invalidateList(@Nullable String reason) {
if (mListener != null) {
Trace.beginSection("Pluggable<" + mName + ">.invalidateList");
- mListener.onPluggableInvalidated((This) this);
+ mListener.onPluggableInvalidated((This) this, reason);
Trace.endSection();
}
}
@@ -74,7 +74,7 @@ public abstract class Pluggable<This> {
* @param <T> The type of pluggable that is being listened to.
*/
public interface PluggableListener<T> {
- /** Called whenever {@link #invalidateList()} is called on this pluggable. */
- void onPluggableInvalidated(T pluggable);
+ /** Called whenever {@link #invalidateList(String)} is called on this pluggable. */
+ void onPluggableInvalidated(T pluggable, @Nullable String reason);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CollectionReadyForBuildListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CollectionReadyForBuildListener.java
index 4023474bf6a7..941b2ae4f771 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CollectionReadyForBuildListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CollectionReadyForBuildListener.java
@@ -29,5 +29,5 @@ public interface CollectionReadyForBuildListener {
* Called by the NotifCollection to indicate that something in the collection has changed and
* that the list builder should regenerate the list.
*/
- void onBuildList(Collection<NotificationEntry> entries);
+ void onBuildList(Collection<NotificationEntry> entries, String reason);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
index 7e7936717b84..ebcac6b6afb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection.notifcollection
import android.os.RemoteException
import android.service.notification.NotificationListenerService
import android.service.notification.NotificationListenerService.RankingMap
+import android.service.notification.StatusBarNotification
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel.DEBUG
import com.android.systemui.log.LogLevel.ERROR
@@ -65,9 +66,9 @@ fun cancellationReasonDebugString(@CancellationReason reason: Int) =
class NotifCollectionLogger @Inject constructor(
@NotificationLog private val buffer: LogBuffer
) {
- fun logNotifPosted(key: String) {
+ fun logNotifPosted(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
}, {
"POSTED $str1"
})
@@ -75,49 +76,49 @@ class NotifCollectionLogger @Inject constructor(
fun logNotifGroupPosted(groupKey: String, batchSize: Int) {
buffer.log(TAG, INFO, {
- str1 = groupKey
+ str1 = logKey(groupKey)
int1 = batchSize
}, {
"POSTED GROUP $str1 ($int1 events)"
})
}
- fun logNotifUpdated(key: String) {
+ fun logNotifUpdated(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
}, {
"UPDATED $str1"
})
}
- fun logNotifRemoved(key: String, @CancellationReason reason: Int) {
+ fun logNotifRemoved(sbn: StatusBarNotification, @CancellationReason reason: Int) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = sbn.logKey
int1 = reason
}, {
"REMOVED $str1 reason=${cancellationReasonDebugString(int1)}"
})
}
- fun logNotifReleased(key: String) {
+ fun logNotifReleased(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
}, {
"RELEASED $str1"
})
}
- fun logNotifDismissed(key: String) {
+ fun logNotifDismissed(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
}, {
"DISMISSED $str1"
})
}
- fun logNonExistentNotifDismissed(key: String) {
+ fun logNonExistentNotifDismissed(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
}, {
"DISMISSED Non Existent $str1"
})
@@ -125,7 +126,7 @@ class NotifCollectionLogger @Inject constructor(
fun logChildDismissed(entry: NotificationEntry) {
buffer.log(TAG, DEBUG, {
- str1 = entry.key
+ str1 = entry.logKey
}, {
"CHILD DISMISSED (inferred): $str1"
})
@@ -141,31 +142,31 @@ class NotifCollectionLogger @Inject constructor(
fun logDismissOnAlreadyCanceledEntry(entry: NotificationEntry) {
buffer.log(TAG, DEBUG, {
- str1 = entry.key
+ str1 = entry.logKey
}, {
"Dismiss on $str1, which was already canceled. Trying to remove..."
})
}
- fun logNotifDismissedIntercepted(key: String) {
+ fun logNotifDismissedIntercepted(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
}, {
"DISMISS INTERCEPTED $str1"
})
}
- fun logNotifClearAllDismissalIntercepted(key: String) {
+ fun logNotifClearAllDismissalIntercepted(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
}, {
"CLEAR ALL DISMISSAL INTERCEPTED $str1"
})
}
- fun logNotifInternalUpdate(key: String, name: String, reason: String) {
+ fun logNotifInternalUpdate(entry: NotificationEntry, name: String, reason: String) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
str2 = name
str3 = reason
}, {
@@ -173,9 +174,9 @@ class NotifCollectionLogger @Inject constructor(
})
}
- fun logNotifInternalUpdateFailed(key: String, name: String, reason: String) {
+ fun logNotifInternalUpdateFailed(sbn: StatusBarNotification, name: String, reason: String) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = sbn.logKey
str2 = name
str3 = reason
}, {
@@ -183,26 +184,49 @@ class NotifCollectionLogger @Inject constructor(
})
}
- fun logNoNotificationToRemoveWithKey(key: String, @CancellationReason reason: Int) {
+ fun logNoNotificationToRemoveWithKey(
+ sbn: StatusBarNotification,
+ @CancellationReason reason: Int
+ ) {
buffer.log(TAG, ERROR, {
- str1 = key
+ str1 = sbn.logKey
int1 = reason
}, {
"No notification to remove with key $str1 reason=${cancellationReasonDebugString(int1)}"
})
}
- fun logRankingMissing(key: String, rankingMap: RankingMap) {
- buffer.log(TAG, WARNING, { str1 = key }, { "Ranking update is missing ranking for $str1" })
- buffer.log(TAG, DEBUG, {}, { "Ranking map contents:" })
- for (entry in rankingMap.orderedKeys) {
- buffer.log(TAG, DEBUG, { str1 = entry }, { " $str1" })
- }
+ fun logMissingRankings(
+ newlyInconsistentEntries: List<NotificationEntry>,
+ totalInconsistent: Int,
+ rankingMap: RankingMap
+ ) {
+ buffer.log(TAG, WARNING, {
+ int1 = totalInconsistent
+ int2 = newlyInconsistentEntries.size
+ str1 = newlyInconsistentEntries.joinToString { it.logKey ?: "null" }
+ }, {
+ "Ranking update is missing ranking for $int1 entries ($int2 new): $str1"
+ })
+ buffer.log(TAG, DEBUG, {
+ str1 = rankingMap.orderedKeys.map { logKey(it) ?: "null" }.toString()
+ }, {
+ "Ranking map contents: $str1"
+ })
+ }
+
+ fun logRecoveredRankings(newlyConsistentKeys: List<String>) {
+ buffer.log(TAG, INFO, {
+ int1 = newlyConsistentKeys.size
+ str1 = newlyConsistentKeys.joinToString { logKey(it) ?: "null" }
+ }, {
+ "Ranking update now contains rankings for $int1 previously inconsistent entries: $str1"
+ })
}
- fun logRemoteExceptionOnNotificationClear(key: String, e: RemoteException) {
+ fun logRemoteExceptionOnNotificationClear(entry: NotificationEntry, e: RemoteException) {
buffer.log(TAG, WTF, {
- str1 = key
+ str1 = entry.logKey
str2 = e.toString()
}, {
"RemoteException while attempting to clear $str1:\n$str2"
@@ -217,9 +241,9 @@ class NotifCollectionLogger @Inject constructor(
})
}
- fun logLifetimeExtended(key: String, extender: NotifLifetimeExtender) {
+ fun logLifetimeExtended(entry: NotificationEntry, extender: NotifLifetimeExtender) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
str2 = extender.name
}, {
"LIFETIME EXTENDED: $str1 by $str2"
@@ -227,12 +251,12 @@ class NotifCollectionLogger @Inject constructor(
}
fun logLifetimeExtensionEnded(
- key: String,
+ entry: NotificationEntry,
extender: NotifLifetimeExtender,
totalExtenders: Int
) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
str2 = extender.name
int1 = totalExtenders
}, {
@@ -248,6 +272,20 @@ class NotifCollectionLogger @Inject constructor(
})
}
+ fun logEntryBeingExtendedNotInCollection(
+ entry: NotificationEntry,
+ extender: NotifLifetimeExtender,
+ collectionEntryIs: String
+ ) {
+ buffer.log(TAG, WARNING, {
+ str1 = entry.logKey
+ str2 = extender.name
+ str3 = collectionEntryIs
+ }, {
+ "While ending lifetime extension by $str2 of $str1, entry in collection is $str3"
+ })
+ }
+
fun logFutureDismissalReused(dismissal: FutureDismissal) {
buffer.log(TAG, INFO, {
str1 = dismissal.label
@@ -324,4 +362,29 @@ class NotifCollectionLogger @Inject constructor(
}
}
+fun maybeLogInconsistentRankings(
+ logger: NotifCollectionLogger,
+ oldKeysWithoutRankings: Set<String>,
+ newEntriesWithoutRankings: Map<String, NotificationEntry>?,
+ rankingMap: RankingMap
+) {
+ if (oldKeysWithoutRankings.isEmpty() && newEntriesWithoutRankings == null) return
+ val newlyConsistent: List<String> = oldKeysWithoutRankings
+ .mapNotNull { key ->
+ key.takeIf { key !in (newEntriesWithoutRankings ?: emptyMap()) }
+ .takeIf { key in rankingMap.orderedKeys }
+ }.sorted()
+ if (newlyConsistent.isNotEmpty()) {
+ logger.logRecoveredRankings(newlyConsistent)
+ }
+ val newlyInconsistent: List<NotificationEntry> = newEntriesWithoutRankings
+ ?.mapNotNull { (key, entry) ->
+ entry.takeIf { key !in oldKeysWithoutRankings }
+ }?.sortedBy { it.key } ?: emptyList()
+ if (newlyInconsistent.isNotEmpty()) {
+ val totalInconsistent: Int = newEntriesWithoutRankings?.size ?: 0
+ logger.logMissingRankings(newlyInconsistent, totalInconsistent, rankingMap)
+ }
+}
+
private const val TAG = "NotifCollection"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt
index 6a1e36f4f469..ec10aaf3cfe3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.provider
import com.android.internal.statusbar.NotificationVisibility
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -25,6 +26,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger
import javax.inject.Inject
/** pipeline-agnostic implementation for getting [NotificationVisibility]. */
+@SysUISingleton
class NotificationVisibilityProviderImpl @Inject constructor(
private val notifDataStore: NotifLiveDataStore,
private val notifCollection: CommonNotifCollection
@@ -46,4 +48,4 @@ class NotificationVisibilityProviderImpl @Inject constructor(
NotificationLogger.getNotificationLocation(notifCollection.getEntry(key))
private fun getCount() = notifDataStore.activeNotifCount.value
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt
index 68bdd18c9881..82c7aae08f6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification
+package com.android.systemui.statusbar.notification.collection.provider
import android.content.Context
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
/**
@@ -34,7 +34,7 @@ import javax.inject.Inject
class SectionHeaderVisibilityProvider @Inject constructor(
context: Context
) {
- var neverShowSectionHeaders = context.resources.getBoolean(R.bool.config_notification_never_show_section_headers)
- private set
+ val neverShowSectionHeaders =
+ context.resources.getBoolean(R.bool.config_notification_never_show_section_headers)
var sectionHeadersVisible = true
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionClassifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt
index 1f2d0fe6c46e..7b9483022fd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionClassifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification
+package com.android.systemui.statusbar.notification.collection.provider
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
@@ -26,7 +26,7 @@ import javax.inject.Inject
* NOTE: This class exists to avoid putting metadata like "isMinimized" on the NotifSection
*/
@SysUISingleton
-class SectionClassifier @Inject constructor() {
+class SectionStyleProvider @Inject constructor() {
private lateinit var lowPrioritySections: Set<NotifSectioner>
/**
@@ -43,4 +43,4 @@ class SectionClassifier @Inject constructor() {
fun isMinimizedSection(section: NotifSection): Boolean {
return lowPrioritySections.contains(section.sectioner)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
index a2cb9509a621..1b3f83d1892f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.render;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -28,10 +29,13 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
+import javax.inject.Inject;
+
/**
* Provides grouping information for notification entries including information about a group's
* expanded state.
*/
+@SysUISingleton
public class GroupExpansionManagerImpl implements GroupExpansionManager, Coordinator {
private final GroupMembershipManager mGroupMembershipManager;
private final Set<OnGroupExpansionChangeListener> mOnGroupChangeListeners = new HashSet<>();
@@ -39,6 +43,7 @@ public class GroupExpansionManagerImpl implements GroupExpansionManager, Coordin
// Set of summary keys whose groups are expanded
private final Set<NotificationEntry> mExpandedGroups = new HashSet<>();
+ @Inject
public GroupExpansionManagerImpl(GroupMembershipManager groupMembershipManager) {
mGroupMembershipManager = groupMembershipManager;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
index 8be710c8842c..d234e54e6725 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -16,12 +16,12 @@
package com.android.systemui.statusbar.notification.collection.render
-import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
+import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.util.Compile
import com.android.systemui.util.traceSection
@@ -107,4 +107,4 @@ class NodeSpecBuilder(
.apply { entry.children.forEach { children.add(buildNotifNode(this, it)) } }
else -> throw RuntimeException("Unexpected entry: $entry")
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt
index 3501b44a2c2e..38e3d496a60c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt
@@ -19,20 +19,24 @@ package com.android.systemui.statusbar.notification.collection.render
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.util.Compile
import javax.inject.Inject
class NodeSpecBuilderLogger @Inject constructor(
+ notifPipelineFlags: NotifPipelineFlags,
@NotificationLog private val buffer: LogBuffer
) {
+ private val devLoggingEnabled by lazy { notifPipelineFlags.isDevLoggingEnabled() }
+
fun logBuildNodeSpec(
oldSections: Set<NotifSection?>,
newHeaders: Map<NotifSection?, NodeController?>,
newCounts: Map<NotifSection?, Int>,
newSectionOrder: List<NotifSection?>
) {
- if (!Compile.IS_DEBUG)
+ if (!(Compile.IS_DEBUG && devLoggingEnabled))
return
buffer.log(TAG, LogLevel.DEBUG, {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index f460644ce71c..9a9941e7a19c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -123,7 +123,7 @@ class ShadeViewDiffer(
when (childNode.parent) {
null -> {
// A new child (either newly created or coming from some other parent)
- logger.logAttachingChild(childNode.label, parentNode.label)
+ logger.logAttachingChild(childNode.label, parentNode.label, index)
parentNode.addChildAt(childNode, index)
childNode.parent = parentNode
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
index 4c0357243d48..6d1071c283e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
@@ -43,12 +43,13 @@ class ShadeViewDifferLogger @Inject constructor(
})
}
- fun logAttachingChild(key: String, parent: String) {
+ fun logAttachingChild(key: String, parent: String, atIndex: Int) {
buffer.log(TAG, LogLevel.DEBUG, {
str1 = key
str2 = parent
+ int1 = atIndex
}, {
- "Attaching view $str1 to $str2"
+ "Attaching view $str1 to $str2 at index $int1"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 6ed81078c3a4..51dc72848d9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -19,10 +19,10 @@ package com.android.systemui.statusbar.notification.collection.render
import android.content.Context
import android.view.View
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
-import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.util.traceSection
import dagger.assisted.Assisted
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index c9c7fe9e0ab6..bf08fc7f53e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -35,29 +35,26 @@ import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.shade.NotifPanelEventsModule;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotifPipelineChoreographerModule;
import com.android.systemui.statusbar.notification.collection.coordinator.ShadeEventCoordinator;
-import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorsModule;
import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager;
import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
-import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationPresenterExtensions;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.legacy.OnUserInteractionCallbackImplLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
@@ -88,9 +85,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationSectionsMan
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotifPanelEventsModule;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.wmshell.BubblesManager;
@@ -116,12 +111,10 @@ import dagger.Provides;
})
public interface NotificationsModule {
@Binds
- StackScrollAlgorithm.SectionProvider bindSectionProvider(
- NotificationSectionsManager impl);
+ StackScrollAlgorithm.SectionProvider bindSectionProvider(NotificationSectionsManager impl);
@Binds
- StackScrollAlgorithm.BypassController bindBypassController(
- KeyguardBypassController impl);
+ StackScrollAlgorithm.BypassController bindBypassController(KeyguardBypassController impl);
/** Provides an instance of {@link NotificationEntryManager} */
@SysUISingleton
@@ -135,7 +128,8 @@ public interface NotificationsModule {
LeakDetector leakDetector,
IStatusBarService statusBarService,
NotifLiveDataStoreImpl notifLiveDataStore,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ @Background Executor bgExecutor) {
return new NotificationEntryManager(
logger,
groupManager,
@@ -145,7 +139,8 @@ public interface NotificationsModule {
leakDetector,
statusBarService,
notifLiveDataStore,
- dumpManager);
+ dumpManager,
+ bgExecutor);
}
/** Provides an instance of {@link NotificationGutsManager} */
@@ -194,12 +189,8 @@ public interface NotificationsModule {
}
/** Provides an instance of {@link NotifGutsViewManager} */
- @SysUISingleton
- @Provides
- static NotifGutsViewManager provideNotifGutsViewManager(
- NotificationGutsManager notificationGutsManager) {
- return notificationGutsManager;
- }
+ @Binds
+ NotifGutsViewManager bindNotifGutsViewManager(NotificationGutsManager notificationGutsManager);
/** Provides an instance of {@link VisualStabilityManager} */
@SysUISingleton
@@ -226,10 +217,8 @@ public interface NotificationsModule {
static NotificationLogger provideNotificationLogger(
NotificationListener notificationListener,
@UiBackground Executor uiBgExecutor,
- NotifPipelineFlags notifPipelineFlags,
NotifLiveDataStore notifLiveDataStore,
NotificationVisibilityProvider visibilityProvider,
- NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
StatusBarStateController statusBarStateController,
NotificationLogger.ExpansionStateLogger expansionStateLogger,
@@ -237,10 +226,8 @@ public interface NotificationsModule {
return new NotificationLogger(
notificationListener,
uiBgExecutor,
- notifPipelineFlags,
notifLiveDataStore,
visibilityProvider,
- entryManager,
notifPipeline,
statusBarStateController,
expansionStateLogger,
@@ -257,25 +244,13 @@ public interface NotificationsModule {
/** Provides an instance of {@link GroupMembershipManager} */
@SysUISingleton
@Provides
- static GroupMembershipManager provideGroupMembershipManager(
- NotifPipelineFlags notifPipelineFlags,
- Lazy<NotificationGroupManagerLegacy> groupManagerLegacy) {
- return notifPipelineFlags.isNewPipelineEnabled()
- ? new GroupMembershipManagerImpl()
- : groupManagerLegacy.get();
+ static GroupMembershipManager provideGroupMembershipManager() {
+ return new GroupMembershipManagerImpl();
}
/** Provides an instance of {@link GroupExpansionManager} */
- @SysUISingleton
- @Provides
- static GroupExpansionManager provideGroupExpansionManager(
- NotifPipelineFlags notifPipelineFlags,
- Lazy<GroupMembershipManager> groupMembershipManager,
- Lazy<NotificationGroupManagerLegacy> groupManagerLegacy) {
- return notifPipelineFlags.isNewPipelineEnabled()
- ? new GroupExpansionManagerImpl(groupMembershipManager.get())
- : groupManagerLegacy.get();
- }
+ @Binds
+ GroupExpansionManager provideGroupExpansionManager(GroupExpansionManagerImpl impl);
/** Initializes the notification data pipeline (can be disabled via config). */
@SysUISingleton
@@ -294,69 +269,28 @@ public interface NotificationsModule {
/**
* Provide the active notification collection managing the notifications to render.
*/
- @Provides
- @SysUISingleton
- static CommonNotifCollection provideCommonNotifCollection(
- NotifPipelineFlags notifPipelineFlags,
- Lazy<NotifPipeline> pipeline,
- NotificationEntryManager entryManager) {
- return notifPipelineFlags.isNewPipelineEnabled()
- ? pipeline.get() : entryManager;
- }
+ @Binds
+ CommonNotifCollection provideCommonNotifCollection(NotifPipeline pipeline);
/**
* Provide the object which can be used to obtain NotificationVisibility objects.
*/
@Binds
- @SysUISingleton
NotificationVisibilityProvider provideNotificationVisibilityProvider(
- NotificationVisibilityProviderImpl newProvider);
+ NotificationVisibilityProviderImpl impl);
/**
* Provide the active implementation for presenting notifications.
*/
- @Provides
- @SysUISingleton
- static NotifShadeEventSource provideNotifShadeEventSource(
- NotifPipelineFlags notifPipelineFlags,
- Lazy<ShadeEventCoordinator> shadeEventCoordinatorLazy,
- Lazy<LegacyNotificationPresenterExtensions> legacyNotificationPresenterExtensionsLazy) {
- return notifPipelineFlags.isNewPipelineEnabled()
- ? shadeEventCoordinatorLazy.get()
- : legacyNotificationPresenterExtensionsLazy.get();
- }
+ @Binds
+ NotifShadeEventSource provideNotifShadeEventSource(ShadeEventCoordinator shadeEventCoordinator);
/**
* Provide a dismissal callback that's triggered when a user manually dismissed a notification
* from the notification shade or it gets auto-cancelled by click.
*/
- @Provides
- @SysUISingleton
- static OnUserInteractionCallback provideOnUserInteractionCallback(
- NotifPipelineFlags notifPipelineFlags,
- HeadsUpManager headsUpManager,
- StatusBarStateController statusBarStateController,
- Lazy<NotifCollection> notifCollection,
- Lazy<NotificationVisibilityProvider> visibilityProvider,
- Lazy<VisualStabilityCoordinator> visualStabilityCoordinator,
- NotificationEntryManager entryManager,
- VisualStabilityManager visualStabilityManager,
- Lazy<GroupMembershipManager> groupMembershipManagerLazy) {
- return notifPipelineFlags.isNewPipelineEnabled()
- ? new OnUserInteractionCallbackImpl(
- visibilityProvider.get(),
- notifCollection.get(),
- headsUpManager,
- statusBarStateController,
- visualStabilityCoordinator.get())
- : new OnUserInteractionCallbackImplLegacy(
- entryManager,
- visibilityProvider.get(),
- headsUpManager,
- statusBarStateController,
- visualStabilityManager,
- groupMembershipManagerLazy.get());
- }
+ @Binds
+ OnUserInteractionCallback provideOnUserInteractionCallback(OnUserInteractionCallbackImpl impl);
/** */
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 11ffde625798..0e9f1cda73fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -23,31 +23,22 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.Snoo
import com.android.systemui.statusbar.NotificationListener
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.AnimatedImageNotificationManager
-import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.NotificationClicker
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.NotificationListController
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore
import com.android.systemui.statusbar.notification.collection.NotifPipeline
-import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
import com.android.systemui.statusbar.notification.collection.TargetSdkResolver
-import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
-import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
-import com.android.systemui.statusbar.notification.interruption.HeadsUpController
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.CentralSurfaces
-import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper
import com.android.systemui.statusbar.policy.DeviceProvisionedController
-import com.android.systemui.statusbar.policy.HeadsUpManager
-import com.android.systemui.statusbar.policy.RemoteInputUriController
import com.android.wm.shell.bubbles.Bubbles
import dagger.Lazy
import java.io.PrintWriter
@@ -64,30 +55,21 @@ import javax.inject.Inject
@SysUISingleton
class NotificationsControllerImpl @Inject constructor(
private val centralSurfaces: Lazy<CentralSurfaces>,
- private val notifPipelineFlags: NotifPipelineFlags,
private val notificationListener: NotificationListener,
private val entryManager: NotificationEntryManager,
- private val debugModeFilterProvider: DebugModeFilterProvider,
- private val legacyRanker: NotificationRankingManager,
private val commonNotifCollection: Lazy<CommonNotifCollection>,
private val notifPipeline: Lazy<NotifPipeline>,
private val notifLiveDataStore: NotifLiveDataStore,
private val targetSdkResolver: TargetSdkResolver,
- private val newNotifPipelineInitializer: Lazy<NotifPipelineInitializer>,
+ private val notifPipelineInitializer: Lazy<NotifPipelineInitializer>,
private val notifBindPipelineInitializer: NotifBindPipelineInitializer,
private val deviceProvisionedController: DeviceProvisionedController,
private val notificationRowBinder: NotificationRowBinderImpl,
- private val bindEventManagerImpl: BindEventManagerImpl,
- private val remoteInputUriController: RemoteInputUriController,
- private val groupManagerLegacy: Lazy<NotificationGroupManagerLegacy>,
- private val groupAlertTransferHelper: NotificationGroupAlertTransferHelper,
- private val headsUpManager: HeadsUpManager,
- private val headsUpController: HeadsUpController,
private val headsUpViewBinder: HeadsUpViewBinder,
private val clickerBuilder: NotificationClicker.Builder,
private val animatedImageNotificationManager: AnimatedImageNotificationManager,
private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager,
- private val bubblesOptional: Optional<Bubbles>
+ private val bubblesOptional: Optional<Bubbles>,
) : NotificationsController {
override fun initialize(
@@ -118,33 +100,13 @@ class NotificationsControllerImpl @Inject constructor(
notifBindPipelineInitializer.initialize()
animatedImageNotificationManager.bind()
- if (INITIALIZE_NEW_PIPELINE) {
- newNotifPipelineInitializer.get().initialize(
- notificationListener,
- notificationRowBinder,
- listContainer,
- stackController)
- }
+ notifPipelineInitializer.get().initialize(
+ notificationListener,
+ notificationRowBinder,
+ listContainer,
+ stackController)
- if (notifPipelineFlags.isNewPipelineEnabled()) {
- targetSdkResolver.initialize(notifPipeline.get())
- // TODO
- } else {
- targetSdkResolver.initialize(entryManager)
- remoteInputUriController.attach(entryManager)
- groupAlertTransferHelper.bind(entryManager, groupManagerLegacy.get())
- bindEventManagerImpl.attachToLegacyPipeline(entryManager)
- headsUpManager.addListener(groupManagerLegacy.get())
- headsUpManager.addListener(groupAlertTransferHelper)
- headsUpController.attach(entryManager, headsUpManager)
- groupManagerLegacy.get().setHeadsUpManager(headsUpManager)
- groupAlertTransferHelper.setHeadsUpManager(headsUpManager)
- debugModeFilterProvider.registerInvalidationListener {
- entryManager.updateNotifications("debug mode filter changed")
- }
-
- entryManager.initialize(notificationListener, legacyRanker)
- }
+ targetSdkResolver.initialize(notifPipeline.get())
peopleSpaceWidgetManager.attach(notificationListener)
}
@@ -185,9 +147,4 @@ class NotificationsControllerImpl @Inject constructor(
override fun getActiveNotificationsCount(): Int =
notifLiveDataStore.activeNotifCount.value
-
- companion object {
- // NOTE: The new pipeline is always active, even if the old pipeline is *rendering*.
- private const val INITIALIZE_NEW_PIPELINE = true
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
index 19cf9dc135b1..5ef2b9e55d9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinder.java
@@ -83,7 +83,7 @@ public class HeadsUpViewBinder {
params.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
CancellationSignal signal = mStage.requestRebind(entry, en -> {
- mLogger.entryBoundSuccessfully(entry.getKey());
+ mLogger.entryBoundSuccessfully(entry);
en.getRow().setUsesIncreasedHeadsUpHeight(params.useIncreasedHeadsUpHeight());
// requestRebing promises that if we called cancel before this callback would be
// invoked, then we will not enter this callback, and because we always cancel before
@@ -94,7 +94,7 @@ public class HeadsUpViewBinder {
}
});
abortBindCallback(entry);
- mLogger.startBindingHun(entry.getKey());
+ mLogger.startBindingHun(entry);
mOngoingBindCallbacks.put(entry, signal);
}
@@ -105,7 +105,7 @@ public class HeadsUpViewBinder {
public void abortBindCallback(NotificationEntry entry) {
CancellationSignal ongoingBindCallback = mOngoingBindCallbacks.remove(entry);
if (ongoingBindCallback != null) {
- mLogger.currentOngoingBindingAborted(entry.getKey());
+ mLogger.currentOngoingBindingAborted(entry);
ongoingBindCallback.cancel();
}
}
@@ -116,7 +116,7 @@ public class HeadsUpViewBinder {
public void unbindHeadsUpView(NotificationEntry entry) {
abortBindCallback(entry);
mStage.getStageParams(entry).markContentViewsFreeable(FLAG_CONTENT_VIEW_HEADS_UP);
- mLogger.entryContentViewMarkedFreeable(entry.getKey());
- mStage.requestRebind(entry, e -> mLogger.entryUnbound(e.getKey()));
+ mLogger.entryContentViewMarkedFreeable(entry);
+ mStage.requestRebind(entry, e -> mLogger.entryUnbound(e));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
index 50a6207efe0b..d1feaa05c653 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
@@ -3,44 +3,46 @@ package com.android.systemui.statusbar.notification.interruption
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel.INFO
import com.android.systemui.log.dagger.NotificationHeadsUpLog
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
class HeadsUpViewBinderLogger @Inject constructor(@NotificationHeadsUpLog val buffer: LogBuffer) {
- fun startBindingHun(key: String) {
+ fun startBindingHun(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
}, {
"start binding heads up entry $str1 "
})
}
- fun currentOngoingBindingAborted(key: String) {
+ fun currentOngoingBindingAborted(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
}, {
"aborted potential ongoing heads up entry binding $str1 "
})
}
- fun entryBoundSuccessfully(key: String) {
+ fun entryBoundSuccessfully(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
}, {
"heads up entry bound successfully $str1 "
})
}
- fun entryUnbound(key: String) {
+ fun entryUnbound(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
}, {
"heads up entry unbound successfully $str1 "
})
}
- fun entryContentViewMarkedFreeable(key: String) {
+ fun entryContentViewMarkedFreeable(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
}, {
"start unbinding heads up entry $str1 "
})
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index 6c99e3adb73e..99d320d1c7ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -16,20 +16,20 @@
package com.android.systemui.statusbar.notification.interruption
-import android.service.notification.StatusBarNotification
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel.DEBUG
import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.dagger.NotificationHeadsUpLog
-import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.dagger.NotificationInterruptLog
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
class NotificationInterruptLogger @Inject constructor(
- @NotificationLog val notifBuffer: LogBuffer,
- @NotificationHeadsUpLog val hunBuffer: LogBuffer
+ @NotificationInterruptLog val buffer: LogBuffer
) {
fun logHeadsUpFeatureChanged(useHeadsUp: Boolean) {
- hunBuffer.log(TAG, INFO, {
+ buffer.log(TAG, INFO, {
bool1 = useHeadsUp
}, {
"heads up is enabled=$bool1"
@@ -37,118 +37,118 @@ class NotificationInterruptLogger @Inject constructor(
}
fun logWillDismissAll() {
- hunBuffer.log(TAG, INFO, {
+ buffer.log(TAG, INFO, {
}, {
"dismissing any existing heads up notification on disable event"
})
}
- fun logNoBubbleNotAllowed(sbn: StatusBarNotification) {
- notifBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoBubbleNotAllowed(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No bubble up: not allowed to bubble: $str1"
})
}
- fun logNoBubbleNoMetadata(sbn: StatusBarNotification) {
- notifBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoBubbleNoMetadata(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No bubble up: notification: $str1 doesn't have valid metadata"
})
}
fun logNoHeadsUpFeatureDisabled() {
- hunBuffer.log(TAG, DEBUG, {
+ buffer.log(TAG, DEBUG, {
}, {
"No heads up: no huns"
})
}
- fun logNoHeadsUpPackageSnoozed(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoHeadsUpPackageSnoozed(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No alerting: snoozed package: $str1"
})
}
- fun logNoHeadsUpAlreadyBubbled(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoHeadsUpAlreadyBubbled(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No heads up: in unlocked shade where notification is shown as a bubble: $str1"
})
}
- fun logNoHeadsUpSuppressedByDnd(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoHeadsUpSuppressedByDnd(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No heads up: suppressed by DND: $str1"
})
}
- fun logNoHeadsUpNotImportant(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoHeadsUpNotImportant(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No heads up: unimportant notification: $str1"
})
}
- fun logNoHeadsUpNotInUse(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoHeadsUpNotInUse(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No heads up: not in use: $str1"
})
}
fun logNoHeadsUpSuppressedBy(
- sbn: StatusBarNotification,
+ entry: NotificationEntry,
suppressor: NotificationInterruptSuppressor
) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
str2 = suppressor.name
}, {
"No heads up: aborted by suppressor: $str2 sbnKey=$str1"
})
}
- fun logHeadsUp(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logHeadsUp(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"Heads up: $str1"
})
}
- fun logNoAlertingFilteredOut(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoAlertingFilteredOut(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No alerting: filtered notification: $str1"
})
}
- fun logNoAlertingGroupAlertBehavior(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoAlertingGroupAlertBehavior(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No alerting: suppressed due to group alert behavior: $str1"
})
}
fun logNoAlertingSuppressedBy(
- sbn: StatusBarNotification,
+ entry: NotificationEntry,
suppressor: NotificationInterruptSuppressor,
awake: Boolean
) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
str2 = suppressor.name
bool1 = awake
}, {
@@ -156,65 +156,92 @@ class NotificationInterruptLogger @Inject constructor(
})
}
- fun logNoAlertingRecentFullscreen(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoAlertingRecentFullscreen(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No alerting: recent fullscreen: $str1"
})
}
- fun logNoPulsingSettingDisabled(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoPulsingSettingDisabled(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No pulsing: disabled by setting: $str1"
})
}
- fun logNoPulsingBatteryDisabled(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoPulsingBatteryDisabled(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No pulsing: disabled by battery saver: $str1"
})
}
- fun logNoPulsingNoAlert(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoPulsingNoAlert(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No pulsing: notification shouldn't alert: $str1"
})
}
- fun logNoPulsingNoAmbientEffect(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoPulsingNoAmbientEffect(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No pulsing: ambient effect suppressed: $str1"
})
}
- fun logNoPulsingNotImportant(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logNoPulsingNotImportant(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"No pulsing: not important enough: $str1"
})
}
- fun logPulsing(sbn: StatusBarNotification) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = sbn.key
+ fun logPulsing(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"Pulsing: $str1"
})
}
- fun keyguardHideNotification(key: String) {
- hunBuffer.log(TAG, DEBUG, {
- str1 = key
+ fun logNoFullscreen(entry: NotificationEntry, reason: String) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
+ str2 = reason
+ }, {
+ "No FullScreenIntent: $str2: $str1"
+ })
+ }
+
+ fun logNoFullscreenWarning(entry: NotificationEntry, reason: String) {
+ buffer.log(TAG, WARNING, {
+ str1 = entry.logKey
+ str2 = reason
+ }, {
+ "No FullScreenIntent: WARNING: $str2: $str1"
+ })
+ }
+
+ fun logFullscreen(entry: NotificationEntry, reason: String) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
+ str2 = reason
+ }, {
+ "FullScreenIntent: $str2: $str1"
+ })
+ }
+
+ fun keyguardHideNotification(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
}, {
"Keyguard Hide Notification: $str1"
})
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index e210f193b0a1..a72b3814d5ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -41,6 +41,7 @@ import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.util.ArrayList;
import java.util.List;
@@ -58,6 +59,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
private final List<NotificationInterruptSuppressor> mSuppressors = new ArrayList<>();
private final StatusBarStateController mStatusBarStateController;
+ private final KeyguardStateController mKeyguardStateController;
private final NotificationFilter mNotificationFilter;
private final ContentResolver mContentResolver;
private final PowerManager mPowerManager;
@@ -82,6 +84,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
NotificationFilter notificationFilter,
BatteryController batteryController,
StatusBarStateController statusBarStateController,
+ KeyguardStateController keyguardStateController,
HeadsUpManager headsUpManager,
NotificationInterruptLogger logger,
@Main Handler mainHandler,
@@ -94,6 +97,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mNotificationFilter = notificationFilter;
mStatusBarStateController = statusBarStateController;
+ mKeyguardStateController = keyguardStateController;
mHeadsUpManager = headsUpManager;
mLogger = logger;
mFlags = flags;
@@ -147,14 +151,14 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
}
if (!entry.canBubble()) {
- mLogger.logNoBubbleNotAllowed(sbn);
+ mLogger.logNoBubbleNotAllowed(entry);
return false;
}
if (entry.getBubbleMetadata() == null
|| (entry.getBubbleMetadata().getShortcutId() == null
&& entry.getBubbleMetadata().getIntent() == null)) {
- mLogger.logNoBubbleNoMetadata(sbn);
+ mLogger.logNoBubbleNoMetadata(entry);
return false;
}
@@ -177,9 +181,91 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
*/
@Override
public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) {
- return entry.getSbn().getNotification().fullScreenIntent != null
- && (!shouldHeadsUp(entry)
- || mStatusBarStateController.getState() == StatusBarState.KEYGUARD);
+ if (entry.getSbn().getNotification().fullScreenIntent == null) {
+ return false;
+ }
+
+ // Never show FSI when suppressed by DND
+ if (entry.shouldSuppressFullScreenIntent()) {
+ mLogger.logNoFullscreen(entry, "Suppressed by DND");
+ return false;
+ }
+
+ // Never show FSI if importance is not HIGH
+ if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) {
+ mLogger.logNoFullscreen(entry, "Not important enough");
+ return false;
+ }
+
+ // If the notification has suppressive GroupAlertBehavior, block FSI and warn.
+ StatusBarNotification sbn = entry.getSbn();
+ if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
+ // b/231322873: Detect and report an event when a notification has both an FSI and a
+ // suppressive groupAlertBehavior, and now correctly block the FSI from firing.
+ final int uid = entry.getSbn().getUid();
+ android.util.EventLog.writeEvent(0x534e4554, "231322873", uid, "groupAlertBehavior");
+ mLogger.logNoFullscreenWarning(entry, "GroupAlertBehavior will prevent HUN");
+ return false;
+ }
+
+ // If the screen is off, then launch the FullScreenIntent
+ if (!mPowerManager.isInteractive()) {
+ mLogger.logFullscreen(entry, "Device is not interactive");
+ return true;
+ }
+
+ // If the device is currently dreaming, then launch the FullScreenIntent
+ if (isDreaming()) {
+ mLogger.logFullscreen(entry, "Device is dreaming");
+ return true;
+ }
+
+ // If the keyguard is showing, then launch the FullScreenIntent
+ if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
+ mLogger.logFullscreen(entry, "Keyguard is showing");
+ return true;
+ }
+
+ // If the notification should HUN, then we don't need FSI
+ if (shouldHeadsUp(entry)) {
+ mLogger.logNoFullscreen(entry, "Expected to HUN");
+ return false;
+ }
+
+ // Check whether FSI requires the keyguard to be showing.
+ if (mFlags.fullScreenIntentRequiresKeyguard()) {
+
+ // If notification won't HUN and keyguard is showing, launch the FSI.
+ if (mKeyguardStateController.isShowing()) {
+ if (mKeyguardStateController.isOccluded()) {
+ mLogger.logFullscreen(entry, "Expected not to HUN while keyguard occluded");
+ } else {
+ // Likely LOCKED_SHADE, but launch FSI anyway
+ mLogger.logFullscreen(entry, "Keyguard is showing and not occluded");
+ }
+ return true;
+ }
+
+ // Detect the case determined by b/231322873 to launch FSI while device is in use,
+ // as blocked by the correct implementation, and report the event.
+ final int uid = entry.getSbn().getUid();
+ android.util.EventLog.writeEvent(0x534e4554, "231322873", uid, "no hun or keyguard");
+ mLogger.logNoFullscreenWarning(entry, "Expected not to HUN while not on keyguard");
+ return false;
+ }
+
+ // If the notification won't HUN for some other reason (DND/snooze/etc), launch FSI.
+ mLogger.logFullscreen(entry, "Expected not to HUN");
+ return true;
+ }
+
+ private boolean isDreaming() {
+ try {
+ return mDreamManager.isDreaming();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to query dream manager.", e);
+ return false;
+ }
}
private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) {
@@ -194,51 +280,49 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
return false;
}
+ if (!canAlertHeadsUpCommon(entry)) {
+ return false;
+ }
+
if (!canAlertAwakeCommon(entry)) {
return false;
}
if (isSnoozedPackage(sbn)) {
- mLogger.logNoHeadsUpPackageSnoozed(sbn);
+ mLogger.logNoHeadsUpPackageSnoozed(entry);
return false;
}
boolean inShade = mStatusBarStateController.getState() == SHADE;
if (entry.isBubble() && inShade) {
- mLogger.logNoHeadsUpAlreadyBubbled(sbn);
+ mLogger.logNoHeadsUpAlreadyBubbled(entry);
return false;
}
if (entry.shouldSuppressPeek()) {
- mLogger.logNoHeadsUpSuppressedByDnd(sbn);
+ mLogger.logNoHeadsUpSuppressedByDnd(entry);
return false;
}
if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) {
- mLogger.logNoHeadsUpNotImportant(sbn);
+ mLogger.logNoHeadsUpNotImportant(entry);
return false;
}
- boolean isDreaming = false;
- try {
- isDreaming = mDreamManager.isDreaming();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to query dream manager.", e);
- }
- boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
+ boolean inUse = mPowerManager.isScreenOn() && !isDreaming();
if (!inUse) {
- mLogger.logNoHeadsUpNotInUse(sbn);
+ mLogger.logNoHeadsUpNotInUse(entry);
return false;
}
for (int i = 0; i < mSuppressors.size(); i++) {
if (mSuppressors.get(i).suppressAwakeHeadsUp(entry)) {
- mLogger.logNoHeadsUpSuppressedBy(sbn, mSuppressors.get(i));
+ mLogger.logNoHeadsUpSuppressedBy(entry, mSuppressors.get(i));
return false;
}
}
- mLogger.logHeadsUp(sbn);
+ mLogger.logHeadsUp(entry);
return true;
}
@@ -250,33 +334,36 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
* @return true if the entry should ambient pulse, false otherwise
*/
private boolean shouldHeadsUpWhenDozing(NotificationEntry entry) {
- StatusBarNotification sbn = entry.getSbn();
-
if (!mAmbientDisplayConfiguration.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) {
- mLogger.logNoPulsingSettingDisabled(sbn);
+ mLogger.logNoPulsingSettingDisabled(entry);
return false;
}
if (mBatteryController.isAodPowerSave()) {
- mLogger.logNoPulsingBatteryDisabled(sbn);
+ mLogger.logNoPulsingBatteryDisabled(entry);
return false;
}
if (!canAlertCommon(entry)) {
- mLogger.logNoPulsingNoAlert(sbn);
+ mLogger.logNoPulsingNoAlert(entry);
+ return false;
+ }
+
+ if (!canAlertHeadsUpCommon(entry)) {
+ mLogger.logNoPulsingNoAlert(entry);
return false;
}
if (entry.shouldSuppressAmbient()) {
- mLogger.logNoPulsingNoAmbientEffect(sbn);
+ mLogger.logNoPulsingNoAmbientEffect(entry);
return false;
}
if (entry.getImportance() < NotificationManager.IMPORTANCE_DEFAULT) {
- mLogger.logNoPulsingNotImportant(sbn);
+ mLogger.logNoPulsingNotImportant(entry);
return false;
}
- mLogger.logPulsing(sbn);
+ mLogger.logPulsing(entry);
return true;
}
@@ -287,33 +374,38 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
* @return true if these checks pass, false if the notification should not alert
*/
private boolean canAlertCommon(NotificationEntry entry) {
- StatusBarNotification sbn = entry.getSbn();
+ for (int i = 0; i < mSuppressors.size(); i++) {
+ if (mSuppressors.get(i).suppressInterruptions(entry)) {
+ mLogger.logNoAlertingSuppressedBy(entry, mSuppressors.get(i), /* awake */ false);
+ return false;
+ }
+ }
- if (!mFlags.isNewPipelineEnabled() && mNotificationFilter.shouldFilterOut(entry)) {
- mLogger.logNoAlertingFilteredOut(sbn);
+ if (mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry)) {
+ mLogger.keyguardHideNotification(entry);
return false;
}
+ return true;
+ }
+
+ /**
+ * Common checks for heads up notifications on regular and AOD displays.
+ *
+ * @param entry the entry to check
+ * @return true if these checks pass, false if the notification should not alert
+ */
+ private boolean canAlertHeadsUpCommon(NotificationEntry entry) {
+ StatusBarNotification sbn = entry.getSbn();
+
// Don't alert notifications that are suppressed due to group alert behavior
if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
- mLogger.logNoAlertingGroupAlertBehavior(sbn);
+ mLogger.logNoAlertingGroupAlertBehavior(entry);
return false;
}
- for (int i = 0; i < mSuppressors.size(); i++) {
- if (mSuppressors.get(i).suppressInterruptions(entry)) {
- mLogger.logNoAlertingSuppressedBy(sbn, mSuppressors.get(i), /* awake */ false);
- return false;
- }
- }
-
if (entry.hasJustLaunchedFullScreenIntent()) {
- mLogger.logNoAlertingRecentFullscreen(sbn);
- return false;
- }
-
- if (mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry)) {
- mLogger.keyguardHideNotification(entry.getKey());
+ mLogger.logNoAlertingRecentFullscreen(entry);
return false;
}
@@ -331,7 +423,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
for (int i = 0; i < mSuppressors.size(); i++) {
if (mSuppressors.get(i).suppressAwakeInterruptions(entry)) {
- mLogger.logNoAlertingSuppressedBy(sbn, mSuppressors.get(i), /* awake */ true);
+ mLogger.logNoAlertingSuppressedBy(entry, mSuppressors.get(i), /* awake */ true);
return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 9fbd5c39dedd..639187790ae0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -22,7 +22,6 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.Trace;
import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -39,9 +38,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -80,7 +76,6 @@ public class NotificationLogger implements StateListener {
private final Executor mUiBgExecutor;
private final NotifLiveDataStore mNotifLiveDataStore;
private final NotificationVisibilityProvider mVisibilityProvider;
- private final NotificationEntryManager mEntryManager;
private final NotifPipeline mNotifPipeline;
private final NotificationPanelLogger mNotificationPanelLogger;
private final ExpansionStateLogger mExpansionStateLogger;
@@ -220,10 +215,8 @@ public class NotificationLogger implements StateListener {
*/
public NotificationLogger(NotificationListener notificationListener,
@UiBackground Executor uiBgExecutor,
- NotifPipelineFlags notifPipelineFlags,
NotifLiveDataStore notifLiveDataStore,
NotificationVisibilityProvider visibilityProvider,
- NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
StatusBarStateController statusBarStateController,
ExpansionStateLogger expansionStateLogger,
@@ -232,7 +225,6 @@ public class NotificationLogger implements StateListener {
mUiBgExecutor = uiBgExecutor;
mNotifLiveDataStore = notifLiveDataStore;
mVisibilityProvider = visibilityProvider;
- mEntryManager = entryManager;
mNotifPipeline = notifPipeline;
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -241,36 +233,7 @@ public class NotificationLogger implements StateListener {
// Not expected to be destroyed, don't need to unsubscribe
statusBarStateController.addCallback(this);
- if (notifPipelineFlags.isNewPipelineEnabled()) {
- registerNewPipelineListener();
- } else {
- registerLegacyListener();
- }
- }
-
- private void registerLegacyListener() {
- mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
- @Override
- public void onEntryRemoved(
- NotificationEntry entry,
- NotificationVisibility visibility,
- boolean removedByUser,
- int reason) {
- mExpansionStateLogger.onEntryRemoved(entry.getKey());
- }
-
- @Override
- public void onPreEntryUpdated(NotificationEntry entry) {
- mExpansionStateLogger.onEntryUpdated(entry.getKey());
- }
-
- @Override
- public void onInflationError(
- StatusBarNotification notification,
- Exception exception) {
- logNotificationError(notification, exception);
- }
- });
+ registerNewPipelineListener();
}
private void registerNewPipelineListener() {
@@ -333,26 +296,6 @@ public class NotificationLogger implements StateListener {
}
}
- /**
- * Logs Notification inflation error
- */
- private void logNotificationError(
- StatusBarNotification notification,
- Exception exception) {
- try {
- mBarService.onNotificationError(
- notification.getPackageName(),
- notification.getTag(),
- notification.getId(),
- notification.getUid(),
- notification.getInitialPid(),
- exception.getMessage(),
- notification.getUserId());
- } catch (RemoteException ex) {
- // The end is nigh.
- }
- }
-
private void logNotificationVisibilityChanges(
Collection<NotificationVisibility> newlyVisible,
Collection<NotificationVisibility> noLongerVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 26614ff4ae58..94341ba4bb19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -143,7 +143,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
// We don't correctly track dark mode until the content views are inflated, so always update
// the background on first content update just in case it happens to be during a theme change.
- private boolean mUpdateBackgroundOnUpdate = true;
+ private boolean mUpdateSelfBackgroundOnUpdate = true;
private boolean mNotificationTranslationFinished = false;
private boolean mIsSnoozed;
private boolean mIsFaded;
@@ -555,9 +555,28 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
updateLimits();
updateShelfIconColor();
updateRippleAllowed();
- if (mUpdateBackgroundOnUpdate) {
- mUpdateBackgroundOnUpdate = false;
- updateBackgroundColors();
+ if (mUpdateSelfBackgroundOnUpdate) {
+ // Because this is triggered by UiMode change which we already propagated to children,
+ // we know that child rows will receive the same event, and will update their own
+ // backgrounds when they finish inflating, so propagating again would be redundant.
+ mUpdateSelfBackgroundOnUpdate = false;
+ updateBackgroundColorsOfSelf();
+ }
+ }
+
+ private void updateBackgroundColorsOfSelf() {
+ super.updateBackgroundColors();
+ }
+
+ @Override
+ public void updateBackgroundColors() {
+ // Because this call is made by the NSSL only on attached rows at the moment of the
+ // UiMode or Theme change, we have to propagate to our child views.
+ updateBackgroundColorsOfSelf();
+ if (mIsSummaryWithChildren) {
+ for (ExpandableNotificationRow child : mChildrenContainer.getAttachedChildren()) {
+ child.updateBackgroundColors();
+ }
}
}
@@ -1244,7 +1263,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
public void onUiModeChanged() {
- mUpdateBackgroundOnUpdate = true;
+ mUpdateSelfBackgroundOnUpdate = true;
reInflateViews();
if (mChildrenContainer != null) {
for (ExpandableNotificationRow child : mChildrenContainer.getAttachedChildren()) {
@@ -2075,6 +2094,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public void applyLaunchAnimationParams(LaunchAnimationParameters params) {
if (params == null) {
+ // `null` params indicates the animation is over, which means we can't access
+ // params.getParentStartClipTopAmount() which has the value we want to restore.
+ // Fortunately, only NotificationShelf actually uses these values for anything other
+ // than this launch animation, so we can restore the value to 0 and it's right for now.
+ if (mNotificationParent != null) {
+ mNotificationParent.setClipTopAmount(0);
+ }
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 599039d46556..a493a676e3d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
import android.util.Log;
import android.view.View;
@@ -247,7 +248,7 @@ public class ExpandableNotificationRowController implements NotifViewController
@Override
@NonNull
public String getNodeLabel() {
- return mView.getEntry().getKey();
+ return logKey(mView.getEntry());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index d71e76801b3b..8f73b802271d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -58,7 +58,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
private ArrayList<View> mMatchParentViews = new ArrayList<View>();
private static Rect mClipRect = new Rect();
private boolean mWillBeGone;
- private int mMinClipTopAmount = 0;
private boolean mClipToActualHeight = true;
private boolean mChangingPosition = false;
private ViewGroup mTransientContainer;
@@ -187,10 +186,12 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
* @param notifyListeners Whether the listener should be informed about the change.
*/
public void setActualHeight(int actualHeight, boolean notifyListeners) {
- mActualHeight = actualHeight;
- updateClipping();
- if (notifyListeners) {
- notifyHeightChanged(false /* needsAnimation */);
+ if (mActualHeight != actualHeight) {
+ mActualHeight = actualHeight;
+ updateClipping();
+ if (notifyListeners) {
+ notifyHeightChanged(false /* needsAnimation */);
+ }
}
}
@@ -477,14 +478,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
mWillBeGone = willBeGone;
}
- public int getMinClipTopAmount() {
- return mMinClipTopAmount;
- }
-
- public void setMinClipTopAmount(int minClipTopAmount) {
- mMinClipTopAmount = minClipTopAmount;
- }
-
@Override
public void setLayerType(int layerType, Paint paint) {
// Allow resetting the layerType to NONE regardless of overlappingRendering
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
index c66140822d92..99a24cb3e9ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -67,6 +67,7 @@ public class HybridConversationNotificationView extends HybridNotificationView {
mConversationIconView = requireViewById(com.android.internal.R.id.conversation_icon);
mConversationFacePile = requireViewById(com.android.internal.R.id.conversation_face_pile);
mConversationSenderName = requireViewById(R.id.conversation_notification_sender);
+ applyTextColor(mConversationSenderName, mSecondaryTextColor);
mFacePileSize = getResources()
.getDimensionPixelSize(R.dimen.conversation_single_line_face_pile_size);
mFacePileAvatarSize = getResources()
@@ -75,6 +76,9 @@ public class HybridConversationNotificationView extends HybridNotificationView {
.getDimensionPixelSize(R.dimen.conversation_single_line_avatar_size);
mFacePileProtectionWidth = getResources().getDimensionPixelSize(
R.dimen.conversation_single_line_face_pile_protection_width);
+ mTransformationHelper.setCustomTransformation(
+ new FadeOutAndDownWithTitleTransformation(mConversationSenderName),
+ mConversationSenderName.getId());
mTransformationHelper.addViewTransformingToSimilar(mConversationIconView);
mTransformationHelper.addTransformedView(mConversationSenderName);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
index 56f8e087d64d..77fd05186090 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
@@ -16,13 +16,14 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
+
import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.content.res.Resources;
import android.service.notification.StatusBarNotification;
import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -55,10 +56,8 @@ public class HybridGroupManager {
mOverflowNumberPadding = res.getDimensionPixelSize(R.dimen.group_overflow_number_padding);
}
- private HybridNotificationView inflateHybridViewWithStyle(int style,
- View contentView, ViewGroup parent) {
- LayoutInflater inflater = new ContextThemeWrapper(mContext, style)
- .getSystemService(LayoutInflater.class);
+ private HybridNotificationView inflateHybridView(View contentView, ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
int layout = contentView instanceof ConversationLayout
? R.layout.hybrid_conversation_notification
: R.layout.hybrid_notification;
@@ -91,16 +90,8 @@ public class HybridGroupManager {
public HybridNotificationView bindFromNotification(HybridNotificationView reusableView,
View contentView, StatusBarNotification notification,
ViewGroup parent) {
- return bindFromNotificationWithStyle(reusableView, contentView, notification,
- R.style.HybridNotification, parent);
- }
-
- private HybridNotificationView bindFromNotificationWithStyle(
- HybridNotificationView reusableView, View contentView,
- StatusBarNotification notification,
- int style, ViewGroup parent) {
if (reusableView == null) {
- reusableView = inflateHybridViewWithStyle(style, contentView, parent);
+ reusableView = inflateHybridView(contentView, parent);
}
CharSequence titleText = resolveTitle(notification.getNotification());
CharSequence contentText = resolveText(notification.getNotification());
@@ -136,8 +127,8 @@ public class HybridGroupManager {
if (!text.equals(reusableView.getText())) {
reusableView.setText(text);
}
- String contentDescription = String.format(mContext.getResources().getQuantityString(
- R.plurals.notification_group_overflow_description, number), number);
+ String contentDescription = icuMessageFormat(mContext.getResources(),
+ R.string.notification_group_overflow_description, number);
reusableView.setContentDescription(contentDescription);
reusableView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mOverflowNumberSize);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
index c0d85a6a16ef..fc9d9e8b736c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
@@ -16,13 +16,18 @@
package com.android.systemui.statusbar.notification.row;
+import static android.app.Notification.COLOR_INVALID;
+
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
+import androidx.annotation.ColorInt;
+
import com.android.keyguard.AlphaOptimizedLinearLayout;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
@@ -40,6 +45,8 @@ public class HybridNotificationView extends AlphaOptimizedLinearLayout
protected final ViewTransformationHelper mTransformationHelper = new ViewTransformationHelper();
protected TextView mTitleView;
protected TextView mTextView;
+ protected int mPrimaryTextColor = COLOR_INVALID;
+ protected int mSecondaryTextColor = COLOR_INVALID;
public HybridNotificationView(Context context) {
this(context, null);
@@ -69,42 +76,37 @@ public class HybridNotificationView extends AlphaOptimizedLinearLayout
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ resolveThemeTextColors();
mTitleView = findViewById(R.id.notification_title);
mTextView = findViewById(R.id.notification_text);
+ applyTextColor(mTitleView, mPrimaryTextColor);
+ applyTextColor(mTextView, mSecondaryTextColor);
mTransformationHelper.setCustomTransformation(
- new ViewTransformationHelper.CustomTransformation() {
- @Override
- public boolean transformTo(TransformState ownState, TransformableView notification,
- float transformationAmount) {
- // We want to transform to the same y location as the title
- TransformState otherState = notification.getCurrentState(
- TRANSFORMING_VIEW_TITLE);
- CrossFadeHelper.fadeOut(mTextView, transformationAmount);
- if (otherState != null) {
- ownState.transformViewVerticalTo(otherState, transformationAmount);
- otherState.recycle();
- }
- return true;
- }
-
- @Override
- public boolean transformFrom(TransformState ownState,
- TransformableView notification, float transformationAmount) {
- // We want to transform from the same y location as the title
- TransformState otherState = notification.getCurrentState(
- TRANSFORMING_VIEW_TITLE);
- CrossFadeHelper.fadeIn(mTextView, transformationAmount, true /* remap */);
- if (otherState != null) {
- ownState.transformViewVerticalFrom(otherState, transformationAmount);
- otherState.recycle();
- }
- return true;
- }
- }, TRANSFORMING_VIEW_TEXT);
+ new FadeOutAndDownWithTitleTransformation(mTextView),
+ TRANSFORMING_VIEW_TEXT);
mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_TITLE, mTitleView);
mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_TEXT, mTextView);
}
+ protected void applyTextColor(TextView textView, @ColorInt int textColor) {
+ if (textColor != COLOR_INVALID) {
+ textView.setTextColor(textColor);
+ }
+ }
+
+ private void resolveThemeTextColors() {
+ try (TypedArray ta = mContext.getTheme().obtainStyledAttributes(
+ android.R.style.Theme_DeviceDefault_DayNight, new int[]{
+ android.R.attr.textColorPrimary,
+ android.R.attr.textColorSecondary
+ })) {
+ if (ta != null) {
+ mPrimaryTextColor = ta.getColor(0, mPrimaryTextColor);
+ mSecondaryTextColor = ta.getColor(1, mSecondaryTextColor);
+ }
+ }
+ }
+
public void bind(@Nullable CharSequence title, @Nullable CharSequence text,
@Nullable View contentView) {
mTitleView.setText(title);
@@ -152,4 +154,40 @@ public class HybridNotificationView extends AlphaOptimizedLinearLayout
@Override
public void setNotificationFaded(boolean faded) {}
+
+ protected static class FadeOutAndDownWithTitleTransformation extends
+ ViewTransformationHelper.CustomTransformation {
+
+ private final View mView;
+
+ public FadeOutAndDownWithTitleTransformation(View view) {
+ mView = view;
+ }
+
+ @Override
+ public boolean transformTo(TransformState ownState, TransformableView notification,
+ float transformationAmount) {
+ // We want to transform to the same y location as the title
+ TransformState otherState = notification.getCurrentState(TRANSFORMING_VIEW_TITLE);
+ CrossFadeHelper.fadeOut(mView, transformationAmount);
+ if (otherState != null) {
+ ownState.transformViewVerticalTo(otherState, transformationAmount);
+ otherState.recycle();
+ }
+ return true;
+ }
+
+ @Override
+ public boolean transformFrom(TransformState ownState,
+ TransformableView notification, float transformationAmount) {
+ // We want to transform from the same y location as the title
+ TransformState otherState = notification.getCurrentState(TRANSFORMING_VIEW_TITLE);
+ CrossFadeHelper.fadeIn(mView, transformationAmount, true /* remap */);
+ if (otherState != null) {
+ ownState.transformViewVerticalFrom(otherState, transformationAmount);
+ otherState.recycle();
+ }
+ return true;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
index f693ebbb7830..ea564ddb9193 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
@@ -112,7 +112,8 @@ public final class NotifBindPipeline {
public void manageRow(
@NonNull NotificationEntry entry,
@NonNull ExpandableNotificationRow row) {
- mLogger.logManagedRow(entry.getKey());
+ mLogger.logManagedRow(entry);
+ mLogger.logManagedRow(entry);
final BindEntry bindEntry = getBindEntry(entry);
if (bindEntry == null) {
@@ -154,12 +155,12 @@ public final class NotifBindPipeline {
* the real work once rather than repeatedly start and cancel it.
*/
private void requestPipelineRun(NotificationEntry entry) {
- mLogger.logRequestPipelineRun(entry.getKey());
+ mLogger.logRequestPipelineRun(entry);
final BindEntry bindEntry = getBindEntry(entry);
if (bindEntry.row == null) {
// Row is not managed yet but may be soon. Stop for now.
- mLogger.logRequestPipelineRowNotSet(entry.getKey());
+ mLogger.logRequestPipelineRowNotSet(entry);
return;
}
@@ -177,7 +178,7 @@ public final class NotifBindPipeline {
* callbacks when the run finishes. If a run is already in progress, it is restarted.
*/
private void startPipeline(NotificationEntry entry) {
- mLogger.logStartPipeline(entry.getKey());
+ mLogger.logStartPipeline(entry);
if (mStage == null) {
throw new IllegalStateException("No stage was ever set on the pipeline");
@@ -193,7 +194,7 @@ public final class NotifBindPipeline {
final BindEntry bindEntry = getBindEntry(entry);
final Set<BindCallback> callbacks = bindEntry.callbacks;
- mLogger.logFinishedPipeline(entry.getKey(), callbacks.size());
+ mLogger.logFinishedPipeline(entry, callbacks.size());
bindEntry.invalidated = false;
// Move all callbacks to separate list as callbacks may themselves add/remove callbacks.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
index ec406f0524ff..ab91926d466a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
@@ -19,6 +19,8 @@ package com.android.systemui.statusbar.notification.row
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel.INFO
import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
class NotifBindPipelineLogger @Inject constructor(
@@ -32,41 +34,41 @@ class NotifBindPipelineLogger @Inject constructor(
})
}
- fun logManagedRow(notifKey: String) {
+ fun logManagedRow(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = notifKey
+ str1 = entry.logKey
}, {
"Row set for notif: $str1"
})
}
- fun logRequestPipelineRun(notifKey: String) {
+ fun logRequestPipelineRun(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = notifKey
+ str1 = entry.logKey
}, {
"Request pipeline run for notif: $str1"
})
}
- fun logRequestPipelineRowNotSet(notifKey: String) {
+ fun logRequestPipelineRowNotSet(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = notifKey
+ str1 = entry.logKey
}, {
"Row is not set so pipeline will not run. notif = $str1"
})
}
- fun logStartPipeline(notifKey: String) {
+ fun logStartPipeline(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = notifKey
+ str1 = entry.logKey
}, {
"Start pipeline for notif: $str1"
})
}
- fun logFinishedPipeline(notifKey: String, numCallbacks: Int) {
+ fun logFinishedPipeline(entry: NotificationEntry, numCallbacks: Int) {
buffer.log(TAG, INFO, {
- str1 = notifKey
+ str1 = entry.logKey
int1 = numCallbacks
}, {
"Finished pipeline for notif $str1 with $int1 callbacks"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 134f24e7e646..27aa4b38e0ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -266,9 +266,14 @@ public class NotificationConversationInfo extends LinearLayout implements
snooze.setOnClickListener(mOnSnoozeClick);
*/
- if (mAppBubble == BUBBLE_PREFERENCE_ALL) {
- ((TextView) findViewById(R.id.default_summary)).setText(getResources().getString(
+ TextView defaultSummaryTextView = findViewById(R.id.default_summary);
+ if (mAppBubble == BUBBLE_PREFERENCE_ALL
+ && BubblesManager.areBubblesEnabled(mContext, mSbn.getUser())) {
+ defaultSummaryTextView.setText(getResources().getString(
R.string.notification_channel_summary_default_with_bubbles, mAppName));
+ } else {
+ defaultSummaryTextView.setText(getResources().getString(
+ R.string.notification_channel_summary_default));
}
findViewById(R.id.priority).setOnClickListener(mOnFavoriteClick);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index 7269f5545163..512b04968166 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -243,11 +245,11 @@ public class NotificationSnooze extends LinearLayout
private SnoozeOption createOption(int minutes, int accessibilityActionId) {
Resources res = getResources();
boolean showInHours = minutes >= 60;
- int pluralResId = showInHours
- ? R.plurals.snoozeHourOptions
- : R.plurals.snoozeMinuteOptions;
+ int stringResId = showInHours
+ ? R.string.snoozeHourOptions
+ : R.string.snoozeMinuteOptions;
int count = showInHours ? (minutes / 60) : minutes;
- String description = res.getQuantityString(pluralResId, count, count);
+ String description = icuMessageFormat(res, stringResId, count);
String resultText = String.format(res.getString(R.string.snoozed_for_time), description);
AccessibilityAction action = new AccessibilityAction(accessibilityActionId, description);
final int index = resultText.indexOf(description);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
index 3616f8faee1e..81cf14646465 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
@@ -57,7 +57,7 @@ public class RowContentBindStage extends BindStage<RowContentBindParams> {
@NonNull StageCallback callback) {
RowContentBindParams params = getStageParams(entry);
- mLogger.logStageParams(entry.getKey(), params.toString());
+ mLogger.logStageParams(entry, params);
// Resolve content to bind/unbind.
@InflationFlag int inflationFlags = params.getContentViews();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
index 29cce3375c8a..f9923b2254d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
@@ -19,17 +19,19 @@ package com.android.systemui.statusbar.notification.row
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel.INFO
import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
class RowContentBindStageLogger @Inject constructor(
@NotificationLog private val buffer: LogBuffer
) {
- fun logStageParams(notifKey: String, stageParams: String) {
+ fun logStageParams(entry: NotificationEntry, stageParams: RowContentBindParams) {
buffer.log(TAG, INFO, {
- str1 = notifKey
- str2 = stageParams
+ str1 = entry.logKey
+ str2 = stageParams.toString()
}, {
- "Invalidated notif $str1 with params: \n$str2"
+ "Invalidated notif $str1 with params: $str2"
})
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index 7414bdc13672..5aaf63f8d32d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -74,11 +74,6 @@ public abstract class StackScrollerDecorView extends ExpandableView {
mSecondaryView = findSecondaryView();
setVisible(false /* nowVisible */, false /* animate */);
setSecondaryVisible(false /* nowVisible */, false /* animate */);
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
setOutlineProvider(null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index a552f999aeb4..a76f0827fc18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -1312,6 +1312,7 @@ public class NotificationChildrenContainer extends ViewGroup
}
float bottomRoundness = last ? currentBottomRoundness : 0.0f;
child.setBottomRoundness(bottomRoundness, isShown() /* animate */);
+ child.setTopRoundness(0.0f, false /* animate */);
last = false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index e2ed1d83530b..defae5b6941f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -17,8 +17,11 @@
package com.android.systemui.statusbar.notification.stack;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
+import static com.android.systemui.util.DumpUtilsKt.println;
+import static com.android.systemui.util.DumpUtilsKt.visibilityString;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -746,19 +749,20 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
- private void logHunSkippedForUnexpectedState(String key, boolean expected, boolean actual) {
+ private void logHunSkippedForUnexpectedState(ExpandableNotificationRow enr,
+ boolean expected, boolean actual) {
if (mLogger == null) return;
- mLogger.hunSkippedForUnexpectedState(key, expected, actual);
+ mLogger.hunSkippedForUnexpectedState(enr.getEntry(), expected, actual);
}
- private void logHunAnimationSkipped(String key, String reason) {
+ private void logHunAnimationSkipped(ExpandableNotificationRow enr, String reason) {
if (mLogger == null) return;
- mLogger.hunAnimationSkipped(key, reason);
+ mLogger.hunAnimationSkipped(enr.getEntry(), reason);
}
- private void logHunAnimationEventAdded(String key, int type) {
+ private void logHunAnimationEventAdded(ExpandableNotificationRow enr, int type) {
if (mLogger == null) return;
- mLogger.hunAnimationEventAdded(key, type);
+ mLogger.hunAnimationEventAdded(enr.getEntry(), type);
}
private void onDrawDebug(Canvas canvas) {
@@ -1386,12 +1390,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void setExpandedHeight(float height) {
- final float shadeBottom = getHeight() - getEmptyBottomMargin();
final boolean skipHeightUpdate = shouldSkipHeightUpdate();
- if (!skipHeightUpdate) {
- final float expansionFraction = MathUtils.saturate(height / shadeBottom);
- mAmbientState.setExpansionFraction(expansionFraction);
- }
updateStackPosition();
if (!skipHeightUpdate) {
@@ -3177,7 +3176,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (isHeadsUp != row.isHeadsUp()) {
// For cases where we have a heads up showing and appearing again we shouldn't
// do the animations at all.
- logHunSkippedForUnexpectedState(key, isHeadsUp, row.isHeadsUp());
+ logHunSkippedForUnexpectedState(row, isHeadsUp, row.isHeadsUp());
continue;
}
int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
@@ -3195,7 +3194,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (row.isChildInGroup()) {
// We can otherwise get stuck in there if it was just isolated
row.setHeadsUpAnimatingAway(false);
- logHunAnimationSkipped(key, "row is child in group");
+ logHunAnimationSkipped(row, "row is child in group");
continue;
}
} else {
@@ -3203,7 +3202,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (viewState == null) {
// A view state was never generated for this view, so we don't need to animate
// this. This may happen with notification children.
- logHunAnimationSkipped(key, "row has no viewState");
+ logHunAnimationSkipped(row, "row has no viewState");
continue;
}
if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
@@ -3227,7 +3226,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
+ " onBottom=" + onBottom
+ " row=" + row.getEntry().getKey());
}
- logHunAnimationEventAdded(key, type);
+ logHunAnimationEventAdded(row, type);
}
mHeadsUpChangeAnimations.clear();
mAddedHeadsUpChildren.clear();
@@ -4363,8 +4362,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/**
* Update colors of "dismiss" and "empty shade" views.
- *
- * @param lightTheme True if light theme should be used.
*/
@ShadeViewRefactor(RefactorComponent.DECORATOR)
void updateDecorViews() {
@@ -4558,31 +4555,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mClearAllInProgress = clearAllInProgress;
mAmbientState.setClearAllInProgress(clearAllInProgress);
mController.getNoticationRoundessManager().setClearAllInProgress(clearAllInProgress);
- handleClearAllClipping();
}
boolean getClearAllInProgress() {
return mClearAllInProgress;
}
- @ShadeViewRefactor(RefactorComponent.ADAPTER)
- private void handleClearAllClipping() {
- final int count = getChildCount();
- boolean previousChildWillBeDismissed = false;
- for (int i = 0; i < count; i++) {
- ExpandableView child = (ExpandableView) getChildAt(i);
- if (child.getVisibility() == GONE) {
- continue;
- }
- if (mClearAllInProgress && previousChildWillBeDismissed) {
- child.setMinClipTopAmount(child.getClipTopAmount());
- } else {
- child.setMinClipTopAmount(0);
- }
- previousChildWillBeDismissed = canChildBeCleared(child);
- }
- }
-
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public boolean isFooterViewNotGone() {
return mFooterView != null
@@ -4799,8 +4777,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (SPEW) {
Log.v(TAG, "generateHeadsUpAnimation: previous hun appear animation cancelled");
}
- logHunAnimationSkipped(row.getEntry().getKey(),
- "previous hun appear animation cancelled");
+ logHunAnimationSkipped(row, "previous hun appear animation cancelled");
return;
}
mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
@@ -5079,30 +5056,31 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void dump(PrintWriter pwOriginal, String[] args) {
IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
- StringBuilder sb = new StringBuilder("[")
- .append(this.getClass().getSimpleName()).append(":")
- .append(" pulsing=").append(mPulsing ? "T" : "f")
- .append(" expanded=").append(mIsExpanded ? "T" : "f")
- .append(" headsUpPinned=").append(mInHeadsUpPinnedMode ? "T" : "f")
- .append(" qsClipping=").append(mShouldUseRoundedRectClipping ? "T" : "f")
- .append(" qsClipDismiss=").append(mDismissUsingRowTranslationX ? "T" : "f")
- .append(" visibility=").append(DumpUtilsKt.visibilityString(getVisibility()))
- .append(" alpha=").append(getAlpha())
- .append(" scrollY=").append(mAmbientState.getScrollY())
- .append(" maxTopPadding=").append(mMaxTopPadding)
- .append(" showShelfOnly=").append(mShouldShowShelfOnly ? "T" : "f")
- .append(" qsExpandFraction=").append(mQsExpansionFraction)
- .append(" isCurrentUserSetup=").append(mIsCurrentUserSetup)
- .append(" hideAmount=").append(mAmbientState.getHideAmount())
- .append(" ambientStateSwipingUp=").append(mAmbientState.isSwipingUp())
- .append(" maxDisplayedNotifications=").append(mMaxDisplayedNotifications)
- .append(" intrinsicContentHeight=").append(mIntrinsicContentHeight)
- .append(" contentHeight=").append(mContentHeight)
- .append(" intrinsicPadding=").append(mIntrinsicPadding)
- .append(" topPadding=").append(mTopPadding)
- .append(" bottomPadding=").append(mBottomPadding)
- .append("]");
- pw.println(sb.toString());
+ pw.println("Internal state:");
+ DumpUtilsKt.withIncreasedIndent(pw, () -> {
+ println(pw, "pulsing", mPulsing);
+ println(pw, "expanded", mIsExpanded);
+ println(pw, "headsUpPinned", mInHeadsUpPinnedMode);
+ println(pw, "qsClipping", mShouldUseRoundedRectClipping);
+ println(pw, "qsClipDismiss", mDismissUsingRowTranslationX);
+ println(pw, "visibility", visibilityString(getVisibility()));
+ println(pw, "alpha", getAlpha());
+ println(pw, "scrollY", mAmbientState.getScrollY());
+ println(pw, "maxTopPadding", mMaxTopPadding);
+ println(pw, "showShelfOnly", mShouldShowShelfOnly);
+ println(pw, "qsExpandFraction", mQsExpansionFraction);
+ println(pw, "isCurrentUserSetup", mIsCurrentUserSetup);
+ println(pw, "hideAmount", mAmbientState.getHideAmount());
+ println(pw, "ambientStateSwipingUp", mAmbientState.isSwipingUp());
+ println(pw, "maxDisplayedNotifications", mMaxDisplayedNotifications);
+ println(pw, "intrinsicContentHeight", mIntrinsicContentHeight);
+ println(pw, "contentHeight", mContentHeight);
+ println(pw, "intrinsicPadding", mIntrinsicPadding);
+ println(pw, "topPadding", mTopPadding);
+ println(pw, "bottomPadding", mBottomPadding);
+ });
+ pw.println();
+ pw.println("Contents:");
DumpUtilsKt.withIncreasedIndent(pw, () -> {
int childCount = getChildCount();
pw.println("Number of children: " + childCount);
@@ -5269,6 +5247,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
setClearAllInProgress(true);
mShadeNeedsToClose = closeShade;
+ InteractionJankMonitor.getInstance().begin(this, CUJ_SHADE_CLEAR_ALL);
// Decrease the delay for every row we animate to give the sense of
// accelerating the swipes
final int rowDelayDecrement = 5;
@@ -6181,6 +6160,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private void onClearAllAnimationsEnd(
List<ExpandableNotificationRow> viewsToRemove,
@SelectedRows int selectedRows) {
+ InteractionJankMonitor.getInstance().end(CUJ_SHADE_CLEAR_ALL);
if (mClearAllAnimationListener != null) {
mClearAllAnimationListener.onAnimationEnd(viewsToRemove, selectedRows);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 010e6cf90817..596b767bb434 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -28,7 +28,6 @@ import static com.android.systemui.statusbar.notification.stack.NotificationStac
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeCleared;
import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
import android.content.res.Configuration;
@@ -38,12 +37,10 @@ import android.graphics.PointF;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
-import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.util.Pair;
import android.view.Display;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -53,27 +50,26 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.ExpandHelper;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
@@ -84,9 +80,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -117,7 +111,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
-import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -156,18 +149,16 @@ public class NotificationStackScrollLayoutController {
private final ConfigurationController mConfigurationController;
private final ZenModeController mZenModeController;
private final MetricsLogger mMetricsLogger;
+ private final DumpManager mDumpManager;
private final FalsingCollector mFalsingCollector;
private final FalsingManager mFalsingManager;
private final Resources mResources;
private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
private final ScrimController mScrimController;
- private final NotifPipelineFlags mNotifPipelineFlags;
private final NotifPipeline mNotifPipeline;
private final NotifCollection mNotifCollection;
private final NotificationEntryManager mNotificationEntryManager;
- private final IStatusBarService mIStatusBarService;
private final UiEventLogger mUiEventLogger;
- private final LayoutInflater mLayoutInflater;
private final NotificationRemoteInputManager mRemoteInputManager;
private final VisualStabilityManager mVisualStabilityManager;
private final ShadeController mShadeController;
@@ -204,8 +195,6 @@ public class NotificationStackScrollLayoutController {
@Nullable
private NotificationActivityStarter mNotificationActivityStarter;
- private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
-
@VisibleForTesting
final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
new View.OnAttachStateChangeListener() {
@@ -254,10 +243,7 @@ public class NotificationStackScrollLayoutController {
mView.setAnimateBottomOnLayout(true);
}
// Let's update the footer once the notifications have been updated (in the next frame)
- mView.post(() -> {
- updateFooter();
- updateSectionBoundaries("dynamic privacy changed");
- });
+ mView.post(this::updateFooter);
};
@VisibleForTesting
@@ -632,9 +618,9 @@ public class NotificationStackScrollLayoutController {
KeyguardMediaController keyguardMediaController,
KeyguardBypassController keyguardBypassController,
ZenModeController zenModeController,
- SysuiColorExtractor colorExtractor,
NotificationLockscreenUserManager lockscreenUserManager,
MetricsLogger metricsLogger,
+ DumpManager dumpManager,
FalsingCollector falsingCollector,
FalsingManager falsingManager,
@Main Resources resources,
@@ -644,15 +630,12 @@ public class NotificationStackScrollLayoutController {
NotificationGroupManagerLegacy legacyGroupManager,
GroupExpansionManager groupManager,
@SilentHeader SectionHeaderController silentHeaderController,
- NotifPipelineFlags notifPipelineFlags,
NotifPipeline notifPipeline,
NotifCollection notifCollection,
NotificationEntryManager notificationEntryManager,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
ShadeTransitionController shadeTransitionController,
- IStatusBarService iStatusBarService,
UiEventLogger uiEventLogger,
- LayoutInflater layoutInflater,
NotificationRemoteInputManager remoteInputManager,
VisualStabilityManager visualStabilityManager,
ShadeController shadeController,
@@ -677,6 +660,7 @@ public class NotificationStackScrollLayoutController {
mZenModeController = zenModeController;
mLockscreenUserManager = lockscreenUserManager;
mMetricsLogger = metricsLogger;
+ mDumpManager = dumpManager;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mShadeTransitionController = shadeTransitionController;
mFalsingCollector = falsingCollector;
@@ -694,14 +678,11 @@ public class NotificationStackScrollLayoutController {
mCentralSurfaces.requestNotificationUpdate("onGroupsChanged");
}
});
- mNotifPipelineFlags = notifPipelineFlags;
mSilentHeaderController = silentHeaderController;
mNotifPipeline = notifPipeline;
mNotifCollection = notifCollection;
mNotificationEntryManager = notificationEntryManager;
- mIStatusBarService = iStatusBarService;
mUiEventLogger = uiEventLogger;
- mLayoutInflater = layoutInflater;
mRemoteInputManager = remoteInputManager;
mVisualStabilityManager = visualStabilityManager;
mShadeController = shadeController;
@@ -728,6 +709,7 @@ public class NotificationStackScrollLayoutController {
}
});
mView.setShadeController(mShadeController);
+ mDumpManager.registerDumpable(mView);
mKeyguardBypassController.registerOnBypassStateChangedListener(
isEnabled -> mNotificationRoundnessManager.setShouldRoundPulsingViews(!isEnabled));
@@ -740,21 +722,12 @@ public class NotificationStackScrollLayoutController {
.setOnMenuEventListener(mMenuEventListener)
.build();
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
- mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
- @Override
- public void onEntryUpdated(NotificationEntry entry) {
- mView.onEntryUpdated(entry);
- }
- });
- } else {
- mNotificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
- @Override
- public void onPreEntryUpdated(NotificationEntry entry) {
- mView.onEntryUpdated(entry);
- }
- });
- }
+ mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
+ @Override
+ public void onEntryUpdated(NotificationEntry entry) {
+ mView.onEntryUpdated(entry);
+ }
+ });
mView.initView(mView.getContext(), mSwipeHelper, mNotificationStackSizeCalculator);
mView.setKeyguardBypassEnabled(mKeyguardBypassController.getBypassEnabled());
@@ -1226,10 +1199,6 @@ public class NotificationStackScrollLayoutController {
Trace.endSection();
}
- public boolean areNotificationsHiddenInShade() {
- return mZenModeController.areNotificationsHiddenInShade();
- }
-
public boolean isShowingEmptyShadeView() {
return mShowEmptyShadeView;
}
@@ -1334,15 +1303,6 @@ public class NotificationStackScrollLayoutController {
};
}
- public void updateSectionBoundaries(String reason) {
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
- return;
- }
- Trace.beginSection("NSSLC.updateSectionBoundaries");
- mView.updateSectionBoundaries(reason);
- Trace.endSection();
- }
-
public void updateFooter() {
Trace.beginSection("NSSLC.updateFooter");
mView.updateFooter();
@@ -1458,39 +1418,18 @@ public class NotificationStackScrollLayoutController {
private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove,
@SelectedRows int selectedRows) {
- if (mNotifPipelineFlags.isNewPipelineEnabled()) {
- if (selectedRows == ROWS_ALL) {
- mNotifCollection.dismissAllNotifications(
- mLockscreenUserManager.getCurrentUserId());
- } else {
- final List<Pair<NotificationEntry, DismissedByUserStats>>
- entriesWithRowsDismissedFromShade = new ArrayList<>();
- for (ExpandableNotificationRow row : viewsToRemove) {
- final NotificationEntry entry = row.getEntry();
- entriesWithRowsDismissedFromShade.add(
- new Pair<>(entry, getDismissedByUserStats(entry)));
- }
- mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade);
- }
+ if (selectedRows == ROWS_ALL) {
+ mNotifCollection.dismissAllNotifications(
+ mLockscreenUserManager.getCurrentUserId());
} else {
- for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
- if (canChildBeCleared(rowToRemove)) {
- mNotificationEntryManager.performRemoveNotification(
- rowToRemove.getEntry().getSbn(),
- getDismissedByUserStats(rowToRemove.getEntry()),
- NotificationListenerService.REASON_CANCEL_ALL);
- } else {
- rowToRemove.resetTranslation();
- }
- }
- if (selectedRows == ROWS_ALL) {
- try {
- // TODO(b/169585328): Do not clear media player notifications
- mIStatusBarService.onClearAllNotifications(
- mLockscreenUserManager.getCurrentUserId());
- } catch (Exception ignored) {
- }
+ final List<Pair<NotificationEntry, DismissedByUserStats>>
+ entriesWithRowsDismissedFromShade = new ArrayList<>();
+ for (ExpandableNotificationRow row : viewsToRemove) {
+ final NotificationEntry entry = row.getEntry();
+ entriesWithRowsDismissedFromShade.add(
+ new Pair<>(entry, getDismissedByUserStats(entry)));
}
+ mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
index 04bf62104f66..5f79c0e3913a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
@@ -3,21 +3,27 @@ package com.android.systemui.statusbar.notification.stack
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel.INFO
import com.android.systemui.log.dagger.NotificationHeadsUpLog
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.*
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.logKey
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER
import javax.inject.Inject
class NotificationStackScrollLogger @Inject constructor(
@NotificationHeadsUpLog private val buffer: LogBuffer
) {
- fun hunAnimationSkipped(key: String, reason: String) {
+ fun hunAnimationSkipped(entry: NotificationEntry, reason: String) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
str2 = reason
}, {
"heads up animation skipped: key: $str1 reason: $str2"
})
}
- fun hunAnimationEventAdded(key: String, type: Int) {
+ fun hunAnimationEventAdded(entry: NotificationEntry, type: Int) {
val reason: String
reason = if (type == ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
"HEADS_UP_DISAPPEAR"
@@ -33,16 +39,16 @@ class NotificationStackScrollLogger @Inject constructor(
type.toString()
}
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
str2 = reason
}, {
"heads up animation added: $str1 with type $str2"
})
}
- fun hunSkippedForUnexpectedState(key: String, expected: Boolean, actual: Boolean) {
+ fun hunSkippedForUnexpectedState(entry: NotificationEntry, expected: Boolean, actual: Boolean) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
bool1 = expected
bool2 = actual
}, {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 4013254c6592..6d513d0da5c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -24,8 +24,7 @@ import android.util.MathUtils;
import android.view.View;
import android.view.ViewGroup;
-import androidx.annotation.VisibleForTesting;
-
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.R;
@@ -65,6 +64,9 @@ public class StackScrollAlgorithm {
private int mPinnedZTranslationExtra;
private float mNotificationScrimPadding;
private int mMarginBottom;
+ private float mQuickQsOffsetHeight;
+ private float mSmallCornerRadius;
+ private float mLargeCornerRadius;
public StackScrollAlgorithm(
Context context,
@@ -74,10 +76,10 @@ public class StackScrollAlgorithm {
}
public void initView(Context context) {
- initConstants(context);
+ updateResources(context);
}
- private void initConstants(Context context) {
+ private void updateResources(Context context) {
Resources res = context.getResources();
mPaddingBetweenElements = res.getDimensionPixelSize(
R.dimen.notification_divider_height);
@@ -93,6 +95,9 @@ public class StackScrollAlgorithm {
R.dimen.notification_section_divider_height_lockscreen);
mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
mMarginBottom = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
+ mQuickQsOffsetHeight = SystemBarUtils.getQuickQsOffsetHeight(context);
+ mSmallCornerRadius = res.getDimension(R.dimen.notification_corner_radius_small);
+ mLargeCornerRadius = res.getDimension(R.dimen.notification_corner_radius);
}
/**
@@ -441,6 +446,15 @@ public class StackScrollAlgorithm {
return false;
}
+ @VisibleForTesting
+ void maybeUpdateHeadsUpIsVisible(ExpandableViewState viewState, boolean isShadeExpanded,
+ boolean mustStayOnScreen, boolean topVisible, float viewEnd, float hunMax) {
+
+ if (isShadeExpanded && mustStayOnScreen && topVisible) {
+ viewState.headsUpIsVisible = viewEnd < hunMax;
+ }
+ }
+
// TODO(b/172289889) polish shade open from HUN
/**
* Populates the {@link ExpandableViewState} for a single child.
@@ -474,14 +488,6 @@ public class StackScrollAlgorithm {
: ShadeInterpolation.getContentAlpha(expansion);
}
- if (ambientState.isShadeExpanded() && view.mustStayOnScreen()
- && viewState.yTranslation >= 0) {
- // Even if we're not scrolled away we're in view and we're also not in the
- // shelf. We can relax the constraints and let us scroll off the top!
- float end = viewState.yTranslation + viewState.height + ambientState.getStackY();
- viewState.headsUpIsVisible = end < ambientState.getMaxHeadsUpTranslation();
- }
-
final float expansionFraction = getExpansionFractionWithoutShelf(
algorithmState, ambientState);
@@ -497,8 +503,15 @@ public class StackScrollAlgorithm {
algorithmState.mCurrentExpandedYPosition += gap;
}
+ // Must set viewState.yTranslation _before_ use.
+ // Incoming views have yTranslation=0 by default.
viewState.yTranslation = algorithmState.mCurrentYPosition;
+ maybeUpdateHeadsUpIsVisible(viewState, ambientState.isShadeExpanded(),
+ view.mustStayOnScreen(), /* topVisible */ viewState.yTranslation >= 0,
+ /* viewEnd */ viewState.yTranslation + viewState.height + ambientState.getStackY(),
+ /* hunMax */ ambientState.getMaxHeadsUpTranslation()
+ );
if (view instanceof FooterView) {
final boolean shadeClosed = !ambientState.isShadeExpanded();
final boolean isShelfShowing = algorithmState.firstViewInShelf != null;
@@ -682,7 +695,8 @@ public class StackScrollAlgorithm {
if (row.mustStayOnScreen() && !childState.headsUpIsVisible
&& !row.showingPulsing()) {
// Ensure that the heads up is always visible even when scrolled off
- clampHunToTop(ambientState, row, childState);
+ clampHunToTop(mQuickQsOffsetHeight, ambientState.getStackTranslation(),
+ row.getCollapsedHeight(), childState);
if (isTopEntry && row.isAboveShelf()) {
// the first hun can't get off screen.
clampHunToMaxTranslation(ambientState, row, childState);
@@ -719,27 +733,62 @@ public class StackScrollAlgorithm {
}
}
- private void clampHunToTop(AmbientState ambientState, ExpandableNotificationRow row,
- ExpandableViewState childState) {
- float newTranslation = Math.max(ambientState.getTopPadding()
- + ambientState.getStackTranslation(), childState.yTranslation);
- childState.height = (int) Math.max(childState.height - (newTranslation
- - childState.yTranslation), row.getCollapsedHeight());
- childState.yTranslation = newTranslation;
+ /**
+ * When shade is open and we are scrolled to the bottom of notifications,
+ * clamp incoming HUN in its collapsed form, right below qs offset.
+ * Transition pinned collapsed HUN to full height when scrolling back up.
+ */
+ @VisibleForTesting
+ void clampHunToTop(float quickQsOffsetHeight, float stackTranslation, float collapsedHeight,
+ ExpandableViewState viewState) {
+
+ final float newTranslation = Math.max(quickQsOffsetHeight + stackTranslation,
+ viewState.yTranslation);
+
+ // Transition from collapsed pinned state to fully expanded state
+ // when the pinned HUN approaches its actual location (when scrolling back to top).
+ final float distToRealY = newTranslation - viewState.yTranslation;
+ viewState.height = (int) Math.max(viewState.height - distToRealY, collapsedHeight);
+ viewState.yTranslation = newTranslation;
}
+ // Pin HUN to bottom of expanded QS
+ // while the rest of notifications are scrolled offscreen.
private void clampHunToMaxTranslation(AmbientState ambientState, ExpandableNotificationRow row,
ExpandableViewState childState) {
- float newTranslation;
float maxHeadsUpTranslation = ambientState.getMaxHeadsUpTranslation();
- float maxShelfPosition = ambientState.getInnerHeight() + ambientState.getTopPadding()
+ final float maxShelfPosition = ambientState.getInnerHeight() + ambientState.getTopPadding()
+ ambientState.getStackTranslation();
maxHeadsUpTranslation = Math.min(maxHeadsUpTranslation, maxShelfPosition);
- float bottomPosition = maxHeadsUpTranslation - row.getCollapsedHeight();
- newTranslation = Math.min(childState.yTranslation, bottomPosition);
+
+ final float bottomPosition = maxHeadsUpTranslation - row.getCollapsedHeight();
+ final float newTranslation = Math.min(childState.yTranslation, bottomPosition);
childState.height = (int) Math.min(childState.height, maxHeadsUpTranslation
- newTranslation);
childState.yTranslation = newTranslation;
+
+ // Animate pinned HUN bottom corners to and from original roundness.
+ final float originalCornerRadius =
+ row.isLastInSection() ? 1f : (mSmallCornerRadius / mLargeCornerRadius);
+ final float roundness = computeCornerRoundnessForPinnedHun(mHostView.getHeight(),
+ ambientState.getStackY(), getMaxAllowedChildHeight(row), originalCornerRadius);
+ row.setBottomRoundness(roundness, /* animate= */ false);
+ }
+
+ @VisibleForTesting
+ float computeCornerRoundnessForPinnedHun(float hostViewHeight, float stackY,
+ float viewMaxHeight, float originalCornerRadius) {
+
+ // Compute y where corner roundness should be in its original unpinned state.
+ // We use view max height because the pinned collapsed HUN expands to max height
+ // when it becomes unpinned.
+ final float originalRoundnessY = hostViewHeight - viewMaxHeight;
+
+ final float distToOriginalRoundness = Math.max(0f, stackY - originalRoundnessY);
+ final float progressToPinnedRoundness = Math.min(1f,
+ distToOriginalRoundness / viewMaxHeight);
+
+ return MathUtils.lerp(originalCornerRadius, 1f, progressToPinnedRoundness);
}
protected int getMaxAllowedChildHeight(View child) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
index 77377af9ddfb..cb4a0884fea4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
@@ -3,6 +3,7 @@ package com.android.systemui.statusbar.notification.stack
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.NotificationHeadsUpLog
+import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
class StackStateLogger @Inject constructor(
@@ -10,7 +11,7 @@ class StackStateLogger @Inject constructor(
) {
fun logHUNViewDisappearing(key: String) {
buffer.log(TAG, LogLevel.INFO, {
- str1 = key
+ str1 = logKey(key)
}, {
"Heads up view disappearing $str1 "
})
@@ -18,7 +19,7 @@ class StackStateLogger @Inject constructor(
fun logHUNViewAppearing(key: String) {
buffer.log(TAG, LogLevel.INFO, {
- str1 = key
+ str1 = logKey(key)
}, {
"Heads up notification view appearing $str1 "
})
@@ -26,7 +27,7 @@ class StackStateLogger @Inject constructor(
fun logHUNViewDisappearingWithRemoveEvent(key: String) {
buffer.log(TAG, LogLevel.ERROR, {
- str1 = key
+ str1 = logKey(key)
}, {
"Heads up view disappearing $str1 for ANIMATION_TYPE_REMOVE"
})
@@ -34,7 +35,7 @@ class StackStateLogger @Inject constructor(
fun logHUNViewAppearingWithAddEvent(key: String) {
buffer.log(TAG, LogLevel.ERROR, {
- str1 = key
+ str1 = logKey(key)
}, {
"Heads up view disappearing $str1 for ANIMATION_TYPE_ADD"
})
@@ -42,7 +43,7 @@ class StackStateLogger @Inject constructor(
fun disappearAnimationEnded(key: String) {
buffer.log(TAG, LogLevel.INFO, {
- str1 = key
+ str1 = logKey(key)
}, {
"Heads up notification disappear animation ended $str1 "
})
@@ -50,7 +51,7 @@ class StackStateLogger @Inject constructor(
fun appearAnimationEnded(key: String) {
buffer.log(TAG, LogLevel.INFO, {
- str1 = key
+ str1 = logKey(key)
}, {
"Heads up notification appear animation ended $str1 "
})
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 39620ac23117..a0f386ff0b5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -448,7 +448,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
}
// During wake and unlock, we need to draw black before waking up to avoid abrupt
// brightness changes due to display state transitions.
- boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
Runnable wakeUp = ()-> {
if (!wasDeviceInteractive) {
if (DEBUG_BIO_WAKELOCK) {
@@ -659,7 +658,10 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mLatencyTracker.onActionCancel(action);
}
- if (biometricSourceType == BiometricSourceType.FINGERPRINT
+ if (!mVibratorHelper.hasVibrator()
+ && (!mUpdateMonitor.isDeviceInteractive() || mUpdateMonitor.isDreaming())) {
+ startWakeAndUnlock(MODE_SHOW_BOUNCER);
+ } else if (biometricSourceType == BiometricSourceType.FINGERPRINT
&& mUpdateMonitor.isUdfpsSupported()) {
long currUptimeMillis = SystemClock.uptimeMillis();
if (currUptimeMillis - mLastFpFailureUptimeMillis < mConsecutiveFpFailureThreshold) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index fc043b114479..5a8050814c8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -47,6 +47,9 @@ import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.NotificationShadeWindowView;
+import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.LightRevealScrim;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -265,9 +268,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
boolean isPulsing();
- @Nullable
- View getAmbientIndicationContainer();
-
boolean isOccluded();
//TODO: These can / should probably be moved to NotificationPresenter or ShadeController
@@ -427,12 +427,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void onHintFinished();
- void onCameraHintStarted();
-
- void onVoiceAssistHintStarted();
-
- void onPhoneHintStarted();
-
void onTrackingStopped(boolean expand);
// TODO: Figure out way to remove these.
@@ -448,6 +442,8 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void setBouncerShowing(boolean bouncerShowing);
+ void setBouncerShowingOverDream(boolean bouncerShowingOverDream);
+
void collapseShade();
int getWakefulnessState();
@@ -564,8 +560,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void setLaunchEmergencyActionOnFinishedWaking(boolean launch);
- void setTopHidesStatusBar(boolean hides);
-
QSPanelController getQSPanelController();
boolean areNotificationAlertsDisabled();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 9060d5f67913..53b5b0caf257 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -45,6 +45,7 @@ import android.view.WindowInsetsController.Behavior;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.view.AppearanceRegion;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
@@ -54,6 +55,9 @@ import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.shade.NotificationPanelView;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -388,8 +392,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.reset(true /* hide */);
}
- mNotificationPanelViewController.launchCamera(
- mCentralSurfaces.isDeviceInteractive() /* animate */, source);
+ mNotificationPanelViewController.launchCamera(source);
mCentralSurfaces.updateScrimController();
} else {
// We need to defer the camera launch until the screen comes on, since otherwise
@@ -464,7 +467,8 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName) {
+ @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName,
+ LetterboxDetails[] letterboxDetails) {
if (displayId != mDisplayId) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index aa38d8a69b80..2d6d846bf2a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -139,6 +139,7 @@ import com.android.systemui.assist.AssistManager;
import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.camera.CameraIntents;
+import com.android.systemui.charging.WiredChargingRippleController;
import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -147,7 +148,6 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.emergency.EmergencyGesture;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -174,6 +174,9 @@ import com.android.systemui.qs.QSPanelController;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.brightness.BrightnessSliderController;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.NotificationShadeWindowView;
+import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.BackDropView;
@@ -197,8 +200,6 @@ import com.android.systemui.statusbar.PowerButtonReveal;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.charging.WiredChargingRippleController;
-import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.core.StatusBarInitializer;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
@@ -207,7 +208,6 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -238,7 +238,6 @@ import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;
import com.android.systemui.volume.VolumeComponent;
-import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
import com.android.wm.shell.startingsurface.StartingSurface;
@@ -307,7 +306,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
}
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- private final DreamOverlayStateController mDreamOverlayStateController;
private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks;
private float mTransitionToFullShadeProgress = 0f;
private NotificationListContainer mNotifListContainer;
@@ -405,11 +403,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
}
@Override
- public void setTopHidesStatusBar(boolean hides) {
- mTopHidesStatusBar = hides;
- }
-
- @Override
public QSPanelController getQSPanelController() {
return mQSPanelController;
}
@@ -442,6 +435,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
*/
protected int mState; // TODO: remove this. Just use StatusBarStateController
protected boolean mBouncerShowing;
+ private boolean mBouncerShowingOverDream;
private final PhoneStatusBarPolicy mIconPolicy;
@@ -451,7 +445,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
private BiometricUnlockController mBiometricUnlockController;
private final LightBarController mLightBarController;
private final Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
- private final LockscreenGestureLogger mLockscreenGestureLogger;
@Nullable
protected LockscreenWallpaper mLockscreenWallpaper;
private final AutoHideController mAutoHideController;
@@ -518,9 +511,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
private boolean mExpandedVisible;
- private final int[] mAbsPos = new int[2];
-
- private final NotifShadeEventSource mNotifShadeEventSource;
protected final NotificationEntryManager mEntryManager;
private final NotificationGutsManager mGutsManager;
private final NotificationLogger mNotificationLogger;
@@ -602,7 +592,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
}
}
- private Handler mMainHandler;
private final DelayableExecutor mMainExecutor;
private int mInteractingWindows;
@@ -636,12 +625,9 @@ public class CentralSurfacesImpl extends CoreStartable implements
// Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
private int mLastLoggedStateFingerprint;
- private boolean mTopHidesStatusBar;
- private boolean mStatusBarWindowHidden;
private boolean mIsLaunchingActivityOverLockscreen;
private final UserSwitcherController mUserSwitcherController;
- private final NetworkController mNetworkController;
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
protected final BatteryController mBatteryController;
protected boolean mPanelExpanded;
@@ -661,7 +647,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
protected NotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
- private final Optional<BubblesManager> mBubblesManagerOptional;
private final Optional<Bubbles> mBubblesOptional;
private final Bubbles.BubbleExpandListener mBubbleExpandListener;
private final Optional<StartingSurface> mStartingSurfaceOptional;
@@ -703,7 +688,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
FalsingManager falsingManager,
FalsingCollector falsingCollector,
BroadcastDispatcher broadcastDispatcher,
- NotifShadeEventSource notifShadeEventSource,
NotificationEntryManager notificationEntryManager,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
@@ -718,13 +702,11 @@ public class CentralSurfacesImpl extends CoreStartable implements
NotificationLockscreenUserManager lockScreenUserManager,
NotificationRemoteInputManager remoteInputManager,
UserSwitcherController userSwitcherController,
- NetworkController networkController,
BatteryController batteryController,
SysuiColorExtractor colorExtractor,
ScreenLifecycle screenLifecycle,
WakefulnessLifecycle wakefulnessLifecycle,
SysuiStatusBarStateController statusBarStateController,
- Optional<BubblesManager> bubblesManagerOptional,
Optional<Bubbles> bubblesOptional,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
@@ -736,7 +718,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
DozeParameters dozeParameters,
ScrimController scrimController,
Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
- LockscreenGestureLogger lockscreenGestureLogger,
Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
DozeServiceHost dozeServiceHost,
PowerManager powerManager,
@@ -769,7 +750,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
LockscreenShadeTransitionController lockscreenShadeTransitionController,
FeatureFlags featureFlags,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
- @Main Handler mainHandler,
@Main DelayableExecutor delayableExecutor,
@Main MessageRouter messageRouter,
WallpaperManager wallpaperManager,
@@ -778,7 +758,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
NotifPipelineFlags notifPipelineFlags,
InteractionJankMonitor jankMonitor,
DeviceStateManager deviceStateManager,
- DreamOverlayStateController dreamOverlayStateController,
WiredChargingRippleController wiredChargingRippleController,
IDreamManager dreamManager) {
super(context);
@@ -799,7 +778,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
mFalsingCollector = falsingCollector;
mFalsingManager = falsingManager;
mBroadcastDispatcher = broadcastDispatcher;
- mNotifShadeEventSource = notifShadeEventSource;
mEntryManager = notificationEntryManager;
mGutsManager = notificationGutsManager;
mNotificationLogger = notificationLogger;
@@ -814,13 +792,11 @@ public class CentralSurfacesImpl extends CoreStartable implements
mLockscreenUserManager = lockScreenUserManager;
mRemoteInputManager = remoteInputManager;
mUserSwitcherController = userSwitcherController;
- mNetworkController = networkController;
mBatteryController = batteryController;
mColorExtractor = colorExtractor;
mScreenLifecycle = screenLifecycle;
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
- mBubblesManagerOptional = bubblesManagerOptional;
mBubblesOptional = bubblesOptional;
mVisualStabilityManager = visualStabilityManager;
mDeviceProvisionedController = deviceProvisionedController;
@@ -834,7 +810,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
mDozeParameters = dozeParameters;
mScrimController = scrimController;
mLockscreenWallpaperLazy = lockscreenWallpaperLazy;
- mLockscreenGestureLogger = lockscreenGestureLogger;
mScreenPinningRequest = screenPinningRequest;
mDozeScrimController = dozeScrimController;
mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
@@ -861,12 +836,10 @@ public class CentralSurfacesImpl extends CoreStartable implements
mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
mFeatureFlags = featureFlags;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
- mMainHandler = mainHandler;
mMainExecutor = delayableExecutor;
mMessageRouter = messageRouter;
mWallpaperManager = wallpaperManager;
mJankMonitor = jankMonitor;
- mDreamOverlayStateController = dreamOverlayStateController;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
@@ -973,7 +946,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
}
mCommandQueueCallbacks.onSystemBarAttributesChanged(mDisplayId, result.mAppearance,
result.mAppearanceRegions, result.mNavbarColorManagedByIme, result.mBehavior,
- result.mRequestedVisibilities, result.mPackageName);
+ result.mRequestedVisibilities, result.mPackageName, result.mLetterboxDetails);
// StatusBarManagerService has a back up of IME token and it's restored here.
mCommandQueueCallbacks.setImeWindowStatus(mDisplayId, result.mImeToken,
@@ -1197,9 +1170,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
mStatusBarTouchableRegionManager.setup(this, mNotificationShadeWindowView);
mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
- if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
- mHeadsUpManager.addListener(mVisualStabilityManager);
- }
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
createNavigationBar(result);
@@ -1284,8 +1254,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
backdrop.setScaleY(scale);
});
- mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
-
// Set up the quick settings tile panel
final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame);
if (container != null) {
@@ -1485,12 +1453,16 @@ public class CentralSurfacesImpl extends CoreStartable implements
mPowerManager.wakeUp(
time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why);
mWakeUpComingFromTouch = true;
- where.getLocationInWindow(mTmpInt2);
// NOTE, the incoming view can sometimes be the entire container... unsure if
// this location is valuable enough
- mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2,
- mTmpInt2[1] + where.getHeight() / 2);
+ if (where != null) {
+ where.getLocationInWindow(mTmpInt2);
+ mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2,
+ mTmpInt2[1] + where.getHeight() / 2);
+ } else {
+ mWakeUpTouchLocation = new PointF(-1, -1);
+ }
mFalsingCollector.onScreenOnFromTouch();
}
}
@@ -1737,7 +1709,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
// Wrap the animation controller to dismiss the shade and set
// mIsLaunchingActivityOverLockscreen during the animation.
ActivityLaunchAnimator.Controller delegate = wrapAnimationController(
- animationController, dismissShade);
+ animationController, dismissShade, /* isLaunchForActivity= */ true);
controller = new DelegateLaunchAnimatorController(delegate) {
@Override
public void onIntentStarted(boolean willAnimate) {
@@ -1889,12 +1861,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
return mDozeServiceHost.isPulsing();
}
- @androidx.annotation.Nullable
- @Override
- public View getAmbientIndicationContainer() {
- return mAmbientIndicationContainer;
- }
-
/**
* When the keyguard is showing and covered by a "showWhenLocked" activity it
* is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager}
@@ -2274,8 +2240,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
public void updateBubblesVisibility() {
mBubblesOptional.ifPresent(bubbles -> bubbles.onStatusBarVisibilityChanged(
mStatusBarMode != MODE_LIGHTS_OUT
- && mStatusBarMode != MODE_LIGHTS_OUT_TRANSPARENT
- && !mStatusBarWindowHidden));
+ && mStatusBarMode != MODE_LIGHTS_OUT_TRANSPARENT));
}
void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState,
@@ -2354,15 +2319,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
pw.print (" ");
mNotificationPanelViewController.dump(pw, args);
}
- pw.println(" mStackScroller: ");
- if (mStackScroller != null) {
- // Double indent until we rewrite the rest of this dump()
- pw.increaseIndent();
- pw.increaseIndent();
- mStackScroller.dump(pw, args);
- pw.decreaseIndent();
- pw.decreaseIndent();
- }
+ pw.println(" mStackScroller: " + mStackScroller + " (dump moved)");
pw.println(" Theme:");
String nightMode = mUiModeManager == null ? "null" : mUiModeManager.getNightMode() + "";
pw.println(" dark theme: " + nightMode +
@@ -2500,7 +2457,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
true /* isActivityIntent */);
ActivityLaunchAnimator.Controller animController =
animationController != null ? wrapAnimationController(animationController,
- dismissShade) : null;
+ dismissShade, /* isLaunchForActivity= */ true) : null;
// If we animate, we will dismiss the shade only once the animation is done. This is taken
// care of by the StatusBarLaunchAnimationController.
@@ -2517,6 +2474,12 @@ public class CentralSurfacesImpl extends CoreStartable implements
animate, intent.getPackage(), (adapter) -> {
ActivityOptions options = new ActivityOptions(
CentralSurfaces.getActivityOptions(mDisplayId, adapter));
+
+ // We know that the intent of the caller is to dismiss the keyguard and
+ // this runnable is called right after the keyguard is solved, so we tell
+ // WM that we should dismiss it to avoid flickers when opening an activity
+ // that can also be shown over the keyguard.
+ options.setDismissKeyguard();
options.setDisallowEnterPictureInPictureWhileLaunching(
disallowEnterPictureInPictureWhileLaunching);
if (CameraIntents.isInsecureCameraIntent(intent)) {
@@ -2563,13 +2526,34 @@ public class CentralSurfacesImpl extends CoreStartable implements
callback.onActivityStarted(ActivityManager.START_CANCELED);
}
};
+ // Do not deferKeyguard when occluded because, when keyguard is occluded,
+ // we do not launch the activity until keyguard is done.
+ boolean occluded = mStatusBarKeyguardViewManager.isShowing()
+ && mStatusBarKeyguardViewManager.isOccluded();
+ boolean deferred = !occluded;
executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShadeDirectly,
- willLaunchResolverActivity, true /* deferred */, animate);
+ willLaunchResolverActivity, deferred /* deferred */, animate);
}
+ /**
+ * Return a {@link ActivityLaunchAnimator.Controller} wrapping {@code animationController} so
+ * that:
+ * - if it launches in the notification shade window and {@code dismissShade} is true, then
+ * the shade will be instantly dismissed at the end of the animation.
+ * - if it launches in status bar window, it will make the status bar window match the device
+ * size during the animation (that way, the animation won't be clipped by the status bar
+ * size).
+ *
+ * @param animationController the controller that is wrapped and will drive the main animation.
+ * @param dismissShade whether the notification shade will be dismissed at the end of the
+ * animation. This is ignored if {@code animationController} is not
+ * animating in the shade window.
+ * @param isLaunchForActivity whether the launch is for an activity.
+ */
@Nullable
private ActivityLaunchAnimator.Controller wrapAnimationController(
- ActivityLaunchAnimator.Controller animationController, boolean dismissShade) {
+ ActivityLaunchAnimator.Controller animationController, boolean dismissShade,
+ boolean isLaunchForActivity) {
View rootView = animationController.getLaunchContainer().getRootView();
Optional<ActivityLaunchAnimator.Controller> controllerFromStatusBar =
@@ -2583,7 +2567,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
// If the view is not in the status bar, then we are animating a view in the shade.
// We have to make sure that we collapse it when the animation ends or is cancelled.
return new StatusBarLaunchAnimatorController(animationController, this,
- true /* isLaunchForActivity */);
+ isLaunchForActivity);
}
return animationController;
@@ -2724,7 +2708,8 @@ public class CentralSurfacesImpl extends CoreStartable implements
@Override
public void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction,
boolean afterKeyguardGone) {
- if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP
+ if (!action.willRunAnimationOnKeyguard()
+ && mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP
&& mKeyguardStateController.canDismissLockScreen()
&& !mStatusBarStateController.leaveOpenOnKeyguardHide()
&& mDozeServiceHost.isPulsing()) {
@@ -3026,8 +3011,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
@Override
public boolean isInLaunchTransition() {
- return mNotificationPanelViewController.isLaunchTransitionRunning()
- || mNotificationPanelViewController.isLaunchTransitionFinished();
+ return mNotificationPanelViewController.isLaunchTransitionFinished();
}
/**
@@ -3059,11 +3043,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
mCommandQueue.appTransitionStarting(mDisplayId, SystemClock.uptimeMillis(),
LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
};
- if (mNotificationPanelViewController.isLaunchTransitionRunning()) {
- mNotificationPanelViewController.setLaunchTransitionEndRunnable(hideRunnable);
- } else {
- hideRunnable.run();
- }
+ hideRunnable.run();
}
private void cancelAfterLaunchTransitionRunnables() {
@@ -3072,7 +3052,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
}
mLaunchTransitionEndRunnable = null;
mLaunchTransitionCancelRunnable = null;
- mNotificationPanelViewController.setLaunchTransitionEndRunnable(null);
}
/**
@@ -3328,9 +3307,13 @@ public class CentralSurfacesImpl extends CoreStartable implements
@Override
public boolean onBackPressed() {
- boolean isScrimmedBouncer = mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
- if (mStatusBarKeyguardViewManager.onBackPressed(isScrimmedBouncer /* hideImmediately */)) {
- if (isScrimmedBouncer) {
+ final boolean isScrimmedBouncer =
+ mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
+ final boolean isBouncerOverDream = isBouncerShowingOverDream();
+
+ if (mStatusBarKeyguardViewManager.onBackPressed(
+ isScrimmedBouncer || isBouncerOverDream /* hideImmediately */)) {
+ if (isScrimmedBouncer || isBouncerOverDream) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
} else {
mNotificationPanelViewController.expandWithoutQs();
@@ -3352,7 +3335,8 @@ public class CentralSurfacesImpl extends CoreStartable implements
if (mNotificationPanelViewController.closeUserSwitcherIfOpen()) {
return true;
}
- if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
+ if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED
+ && !isBouncerOverDream) {
if (mNotificationPanelViewController.canPanelBeCollapsed()) {
mShadeController.animateCollapsePanels();
}
@@ -3496,24 +3480,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
}
@Override
- public void onCameraHintStarted() {
- mFalsingCollector.onCameraHintStarted();
- mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
- }
-
- @Override
- public void onVoiceAssistHintStarted() {
- mFalsingCollector.onLeftAffordanceHintStarted();
- mKeyguardIndicationController.showTransientIndication(R.string.voice_hint);
- }
-
- @Override
- public void onPhoneHintStarted() {
- mFalsingCollector.onLeftAffordanceHintStarted();
- mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
- }
-
- @Override
public void onTrackingStopped(boolean expand) {
}
@@ -3558,6 +3524,9 @@ public class CentralSurfacesImpl extends CoreStartable implements
setBouncerShowingForStatusBarComponents(bouncerShowing);
mStatusBarHideIconsForBouncerManager.setBouncerShowingAndTriggerUpdate(bouncerShowing);
mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
+ if (mBouncerShowing) {
+ wakeUpIfDozing(SystemClock.uptimeMillis(), null, "BOUNCER_VISIBLE");
+ }
updateScrimController();
if (!mBouncerShowing) {
updatePanelExpansionForKeyguard();
@@ -3565,6 +3534,17 @@ public class CentralSurfacesImpl extends CoreStartable implements
}
/**
+ * Sets whether the bouncer over dream is showing. Note that the bouncer over dream is handled
+ * independently of the rest of the notification panel. As a result, setting this state via
+ * {@link #setBouncerShowing(boolean)} leads to unintended side effects from states modified
+ * behind the dream.
+ */
+ @Override
+ public void setBouncerShowingOverDream(boolean bouncerShowingOverDream) {
+ mBouncerShowingOverDream = bouncerShowingOverDream;
+ }
+
+ /**
* Propagate the bouncer state to status bar components.
*
* Separate from {@link #setBouncerShowing} because we sometimes re-create the status bar and
@@ -3686,8 +3666,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
mWakeUpCoordinator.setFullyAwake(true);
mWakeUpCoordinator.setWakingUp(false);
if (mLaunchCameraWhenFinishedWaking) {
- mNotificationPanelViewController.launchCamera(
- false /* animate */, mLastCameraLaunchSource);
+ mNotificationPanelViewController.launchCamera(mLastCameraLaunchSource);
mLaunchCameraWhenFinishedWaking = false;
}
if (mLaunchEmergencyActionWhenFinishedWaking) {
@@ -4120,8 +4099,9 @@ public class CentralSurfacesImpl extends CoreStartable implements
// We wrap animationCallback with a StatusBarLaunchAnimatorController so that the
// shade is collapsed after the animation (or when it is cancelled, aborted, etc).
ActivityLaunchAnimator.Controller controller =
- animationController != null ? new StatusBarLaunchAnimatorController(
- animationController, this, intent.isActivity()) : null;
+ animationController != null ? wrapAnimationController(
+ animationController, /* dismissShade= */ true, intent.isActivity())
+ : null;
mActivityLaunchAnimator.startPendingIntentWithAnimation(
controller, animate, intent.getCreatorPackage(),
@@ -4208,7 +4188,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
@Override
public boolean isBouncerShowingOverDream() {
- return isBouncerShowing() && mDreamOverlayStateController.isOverlayActive();
+ return mBouncerShowingOverDream;
}
/**
@@ -4329,9 +4309,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
if (!mUserSetup) {
animateCollapseQuickSettings();
}
- if (mNotificationPanelViewController != null) {
- mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
- }
updateQsExpansionEnabled();
}
}
@@ -4370,9 +4347,6 @@ public class CentralSurfacesImpl extends CoreStartable implements
Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
}
- if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
- mViewHierarchyManager.updateRowStates();
- }
mScreenPinningRequest.onConfigurationChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 55b310ff986d..80c3e6ce989d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -40,6 +40,8 @@ import com.android.systemui.doze.DozeLog;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 9863a0ed1ce0..484441a1e76b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -26,6 +26,7 @@ import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
@@ -40,8 +41,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.util.ViewController;
-import java.util.Optional;
import java.util.ArrayList;
+import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 415bd90ed23a..80432dbd277c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -217,8 +217,9 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
* Notify that the status bar panel gets expanded or collapsed.
*
* @param isExpanded True to notify expanded, false to notify collapsed.
+ * TODO(b/237811427) replace with a listener
*/
- void setIsPanelExpanded(boolean isExpanded) {
+ public void setIsPanelExpanded(boolean isExpanded) {
if (isExpanded != mIsExpanded) {
mIsExpanded = isExpanded;
if (isExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 42f301d2f222..6bfb0dad28f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -21,6 +21,7 @@ import android.view.MotionEvent;
import android.view.ViewConfiguration;
import com.android.systemui.Gefingerpoken;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
deleted file mode 100644
index 2922b4c98d34..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ /dev/null
@@ -1,587 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
-import com.android.systemui.classifier.Classifier;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.statusbar.KeyguardAffordanceView;
-import com.android.wm.shell.animation.FlingAnimationUtils;
-
-/**
- * A touch handler of the keyguard which is responsible for launching phone and camera affordances.
- */
-public class KeyguardAffordanceHelper {
-
- public static final long HINT_PHASE1_DURATION = 200;
- private static final long HINT_PHASE2_DURATION = 350;
- private static final float BACKGROUND_RADIUS_SCALE_FACTOR = 0.25f;
- private static final int HINT_CIRCLE_OPEN_DURATION = 500;
-
- private final Context mContext;
- private final Callback mCallback;
-
- private FlingAnimationUtils mFlingAnimationUtils;
- private VelocityTracker mVelocityTracker;
- private boolean mSwipingInProgress;
- private float mInitialTouchX;
- private float mInitialTouchY;
- private float mTranslation;
- private float mTranslationOnDown;
- private int mTouchSlop;
- private int mMinTranslationAmount;
- private int mMinFlingVelocity;
- private int mHintGrowAmount;
- private KeyguardAffordanceView mLeftIcon;
- private KeyguardAffordanceView mRightIcon;
- private Animator mSwipeAnimator;
- private final FalsingManager mFalsingManager;
- private int mMinBackgroundRadius;
- private boolean mMotionCancelled;
- private int mTouchTargetSize;
- private View mTargetedView;
- private boolean mTouchSlopExeeded;
- private AnimatorListenerAdapter mFlingEndListener = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mSwipeAnimator = null;
- mSwipingInProgress = false;
- mTargetedView = null;
- }
- };
- private Runnable mAnimationEndRunnable = new Runnable() {
- @Override
- public void run() {
- mCallback.onAnimationToSideEnded();
- }
- };
-
- KeyguardAffordanceHelper(Callback callback, Context context, FalsingManager falsingManager) {
- mContext = context;
- mCallback = callback;
- initIcons();
- updateIcon(mLeftIcon, 0.0f, mLeftIcon.getRestingAlpha(), false, false, true, false);
- updateIcon(mRightIcon, 0.0f, mRightIcon.getRestingAlpha(), false, false, true, false);
- mFalsingManager = falsingManager;
- initDimens();
- }
-
- private void initDimens() {
- final ViewConfiguration configuration = ViewConfiguration.get(mContext);
- mTouchSlop = configuration.getScaledPagingTouchSlop();
- mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
- mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
- R.dimen.keyguard_min_swipe_amount);
- mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize(
- R.dimen.keyguard_affordance_min_background_radius);
- mTouchTargetSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.keyguard_affordance_touch_target_size);
- mHintGrowAmount =
- mContext.getResources().getDimensionPixelSize(R.dimen.hint_grow_amount_sideways);
- mFlingAnimationUtils = new FlingAnimationUtils(mContext.getResources().getDisplayMetrics(),
- 0.4f);
- }
-
- private void initIcons() {
- mLeftIcon = mCallback.getLeftIcon();
- mRightIcon = mCallback.getRightIcon();
- updatePreviews();
- }
-
- public void updatePreviews() {
- mLeftIcon.setPreviewView(mCallback.getLeftPreview());
- mRightIcon.setPreviewView(mCallback.getRightPreview());
- }
-
- public boolean onTouchEvent(MotionEvent event) {
- int action = event.getActionMasked();
- if (mMotionCancelled && action != MotionEvent.ACTION_DOWN) {
- return false;
- }
- final float y = event.getY();
- final float x = event.getX();
-
- boolean isUp = false;
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- View targetView = getIconAtPosition(x, y);
- if (targetView == null || (mTargetedView != null && mTargetedView != targetView)) {
- mMotionCancelled = true;
- return false;
- }
- if (mTargetedView != null) {
- cancelAnimation();
- } else {
- mTouchSlopExeeded = false;
- }
- startSwiping(targetView);
- mInitialTouchX = x;
- mInitialTouchY = y;
- mTranslationOnDown = mTranslation;
- initVelocityTracker();
- trackMovement(event);
- mMotionCancelled = false;
- break;
- case MotionEvent.ACTION_POINTER_DOWN:
- mMotionCancelled = true;
- endMotion(true /* forceSnapBack */, x, y);
- break;
- case MotionEvent.ACTION_MOVE:
- trackMovement(event);
- float xDist = x - mInitialTouchX;
- float yDist = y - mInitialTouchY;
- float distance = (float) Math.hypot(xDist, yDist);
- if (!mTouchSlopExeeded && distance > mTouchSlop) {
- mTouchSlopExeeded = true;
- }
- if (mSwipingInProgress) {
- if (mTargetedView == mRightIcon) {
- distance = mTranslationOnDown - distance;
- distance = Math.min(0, distance);
- } else {
- distance = mTranslationOnDown + distance;
- distance = Math.max(0, distance);
- }
- setTranslation(distance, false /* isReset */, false /* animateReset */);
- }
- break;
-
- case MotionEvent.ACTION_UP:
- isUp = true;
- case MotionEvent.ACTION_CANCEL:
- boolean hintOnTheRight = mTargetedView == mRightIcon;
- trackMovement(event);
- endMotion(!isUp, x, y);
- if (!mTouchSlopExeeded && isUp) {
- mCallback.onIconClicked(hintOnTheRight);
- }
- break;
- }
- return true;
- }
-
- private void startSwiping(View targetView) {
- mCallback.onSwipingStarted(targetView == mRightIcon);
- mSwipingInProgress = true;
- mTargetedView = targetView;
- }
-
- private View getIconAtPosition(float x, float y) {
- if (leftSwipePossible() && isOnIcon(mLeftIcon, x, y)) {
- return mLeftIcon;
- }
- if (rightSwipePossible() && isOnIcon(mRightIcon, x, y)) {
- return mRightIcon;
- }
- return null;
- }
-
- public boolean isOnAffordanceIcon(float x, float y) {
- return isOnIcon(mLeftIcon, x, y) || isOnIcon(mRightIcon, x, y);
- }
-
- private boolean isOnIcon(View icon, float x, float y) {
- float iconX = icon.getX() + icon.getWidth() / 2.0f;
- float iconY = icon.getY() + icon.getHeight() / 2.0f;
- double distance = Math.hypot(x - iconX, y - iconY);
- return distance <= mTouchTargetSize / 2;
- }
-
- private void endMotion(boolean forceSnapBack, float lastX, float lastY) {
- if (mSwipingInProgress) {
- flingWithCurrentVelocity(forceSnapBack, lastX, lastY);
- } else {
- mTargetedView = null;
- }
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- }
-
- private boolean rightSwipePossible() {
- return mRightIcon.getVisibility() == View.VISIBLE;
- }
-
- private boolean leftSwipePossible() {
- return mLeftIcon.getVisibility() == View.VISIBLE;
- }
-
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- return false;
- }
-
- public void startHintAnimation(boolean right,
- Runnable onFinishedListener) {
- cancelAnimation();
- startHintAnimationPhase1(right, onFinishedListener);
- }
-
- private void startHintAnimationPhase1(final boolean right, final Runnable onFinishedListener) {
- final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
- ValueAnimator animator = getAnimatorToRadius(right, mHintGrowAmount);
- animator.addListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mCancelled) {
- mSwipeAnimator = null;
- mTargetedView = null;
- onFinishedListener.run();
- } else {
- startUnlockHintAnimationPhase2(right, onFinishedListener);
- }
- }
- });
- animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- animator.setDuration(HINT_PHASE1_DURATION);
- animator.start();
- mSwipeAnimator = animator;
- mTargetedView = targetView;
- }
-
- /**
- * Phase 2: Move back.
- */
- private void startUnlockHintAnimationPhase2(boolean right, final Runnable onFinishedListener) {
- ValueAnimator animator = getAnimatorToRadius(right, 0);
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mSwipeAnimator = null;
- mTargetedView = null;
- onFinishedListener.run();
- }
- });
- animator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- animator.setDuration(HINT_PHASE2_DURATION);
- animator.setStartDelay(HINT_CIRCLE_OPEN_DURATION);
- animator.start();
- mSwipeAnimator = animator;
- }
-
- private ValueAnimator getAnimatorToRadius(final boolean right, int radius) {
- final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
- ValueAnimator animator = ValueAnimator.ofFloat(targetView.getCircleRadius(), radius);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float newRadius = (float) animation.getAnimatedValue();
- targetView.setCircleRadiusWithoutAnimation(newRadius);
- float translation = getTranslationFromRadius(newRadius);
- mTranslation = right ? -translation : translation;
- updateIconsFromTranslation(targetView);
- }
- });
- return animator;
- }
-
- private void cancelAnimation() {
- if (mSwipeAnimator != null) {
- mSwipeAnimator.cancel();
- }
- }
-
- private void flingWithCurrentVelocity(boolean forceSnapBack, float lastX, float lastY) {
- float vel = getCurrentVelocity(lastX, lastY);
-
- // We snap back if the current translation is not far enough
- boolean snapBack = false;
- if (mCallback.needsAntiFalsing()) {
- snapBack = snapBack || mFalsingManager.isFalseTouch(
- mTargetedView == mRightIcon
- ? Classifier.RIGHT_AFFORDANCE : Classifier.LEFT_AFFORDANCE);
- }
- snapBack = snapBack || isBelowFalsingThreshold();
-
- // or if the velocity is in the opposite direction.
- boolean velIsInWrongDirection = vel * mTranslation < 0;
- snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
- vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
- fling(vel, snapBack || forceSnapBack, mTranslation < 0);
- }
-
- private boolean isBelowFalsingThreshold() {
- return Math.abs(mTranslation) < Math.abs(mTranslationOnDown) + getMinTranslationAmount();
- }
-
- private int getMinTranslationAmount() {
- float factor = mCallback.getAffordanceFalsingFactor();
- return (int) (mMinTranslationAmount * factor);
- }
-
- private void fling(float vel, final boolean snapBack, boolean right) {
- float target = right ? -mCallback.getMaxTranslationDistance()
- : mCallback.getMaxTranslationDistance();
- target = snapBack ? 0 : target;
-
- ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
- mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mTranslation = (float) animation.getAnimatedValue();
- }
- });
- animator.addListener(mFlingEndListener);
- if (!snapBack) {
- startFinishingCircleAnimation(vel * 0.375f, mAnimationEndRunnable, right);
- mCallback.onAnimationToSideStarted(right, mTranslation, vel);
- } else {
- reset(true);
- }
- animator.start();
- mSwipeAnimator = animator;
- if (snapBack) {
- mCallback.onSwipingAborted();
- }
- }
-
- private void startFinishingCircleAnimation(float velocity, Runnable animationEndRunnable,
- boolean right) {
- KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
- targetView.finishAnimation(velocity, animationEndRunnable);
- }
-
- private void setTranslation(float translation, boolean isReset, boolean animateReset) {
- translation = rightSwipePossible() ? translation : Math.max(0, translation);
- translation = leftSwipePossible() ? translation : Math.min(0, translation);
- float absTranslation = Math.abs(translation);
- if (translation != mTranslation || isReset) {
- KeyguardAffordanceView targetView = translation > 0 ? mLeftIcon : mRightIcon;
- KeyguardAffordanceView otherView = translation > 0 ? mRightIcon : mLeftIcon;
- float alpha = absTranslation / getMinTranslationAmount();
-
- // We interpolate the alpha of the other icons to 0
- float fadeOutAlpha = 1.0f - alpha;
- fadeOutAlpha = Math.max(fadeOutAlpha, 0.0f);
-
- boolean animateIcons = isReset && animateReset;
- boolean forceNoCircleAnimation = isReset && !animateReset;
- float radius = getRadiusFromTranslation(absTranslation);
- boolean slowAnimation = isReset && isBelowFalsingThreshold();
- if (!isReset) {
- updateIcon(targetView, radius, alpha + fadeOutAlpha * targetView.getRestingAlpha(),
- false, false, false, false);
- } else {
- updateIcon(targetView, 0.0f, fadeOutAlpha * targetView.getRestingAlpha(),
- animateIcons, slowAnimation, true /* isReset */, forceNoCircleAnimation);
- }
- updateIcon(otherView, 0.0f, fadeOutAlpha * otherView.getRestingAlpha(),
- animateIcons, slowAnimation, isReset, forceNoCircleAnimation);
-
- mTranslation = translation;
- }
- }
-
- private void updateIconsFromTranslation(KeyguardAffordanceView targetView) {
- float absTranslation = Math.abs(mTranslation);
- float alpha = absTranslation / getMinTranslationAmount();
-
- // We interpolate the alpha of the other icons to 0
- float fadeOutAlpha = 1.0f - alpha;
- fadeOutAlpha = Math.max(0.0f, fadeOutAlpha);
-
- // We interpolate the alpha of the targetView to 1
- KeyguardAffordanceView otherView = targetView == mRightIcon ? mLeftIcon : mRightIcon;
- updateIconAlpha(targetView, alpha + fadeOutAlpha * targetView.getRestingAlpha(), false);
- updateIconAlpha(otherView, fadeOutAlpha * otherView.getRestingAlpha(), false);
- }
-
- private float getTranslationFromRadius(float circleSize) {
- float translation = (circleSize - mMinBackgroundRadius)
- / BACKGROUND_RADIUS_SCALE_FACTOR;
- return translation > 0.0f ? translation + mTouchSlop : 0.0f;
- }
-
- private float getRadiusFromTranslation(float translation) {
- if (translation <= mTouchSlop) {
- return 0.0f;
- }
- return (translation - mTouchSlop) * BACKGROUND_RADIUS_SCALE_FACTOR + mMinBackgroundRadius;
- }
-
- public void animateHideLeftRightIcon() {
- cancelAnimation();
- updateIcon(mRightIcon, 0f, 0f, true, false, false, false);
- updateIcon(mLeftIcon, 0f, 0f, true, false, false, false);
- }
-
- private void updateIcon(KeyguardAffordanceView view, float circleRadius, float alpha,
- boolean animate, boolean slowRadiusAnimation, boolean force,
- boolean forceNoCircleAnimation) {
- if (view.getVisibility() != View.VISIBLE && !force) {
- return;
- }
- if (forceNoCircleAnimation) {
- view.setCircleRadiusWithoutAnimation(circleRadius);
- } else {
- view.setCircleRadius(circleRadius, slowRadiusAnimation);
- }
- updateIconAlpha(view, alpha, animate);
- }
-
- private void updateIconAlpha(KeyguardAffordanceView view, float alpha, boolean animate) {
- float scale = getScale(alpha, view);
- alpha = Math.min(1.0f, alpha);
- view.setImageAlpha(alpha, animate);
- view.setImageScale(scale, animate);
- }
-
- private float getScale(float alpha, KeyguardAffordanceView icon) {
- float scale = alpha / icon.getRestingAlpha() * 0.2f +
- KeyguardAffordanceView.MIN_ICON_SCALE_AMOUNT;
- return Math.min(scale, KeyguardAffordanceView.MAX_ICON_SCALE_AMOUNT);
- }
-
- private void trackMovement(MotionEvent event) {
- if (mVelocityTracker != null) {
- mVelocityTracker.addMovement(event);
- }
- }
-
- private void initVelocityTracker() {
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- }
- mVelocityTracker = VelocityTracker.obtain();
- }
-
- private float getCurrentVelocity(float lastX, float lastY) {
- if (mVelocityTracker == null) {
- return 0;
- }
- mVelocityTracker.computeCurrentVelocity(1000);
- float aX = mVelocityTracker.getXVelocity();
- float aY = mVelocityTracker.getYVelocity();
- float bX = lastX - mInitialTouchX;
- float bY = lastY - mInitialTouchY;
- float bLen = (float) Math.hypot(bX, bY);
- // Project the velocity onto the distance vector: a * b / |b|
- float projectedVelocity = (aX * bX + aY * bY) / bLen;
- if (mTargetedView == mRightIcon) {
- projectedVelocity = -projectedVelocity;
- }
- return projectedVelocity;
- }
-
- public void onConfigurationChanged() {
- initDimens();
- initIcons();
- }
-
- public void onRtlPropertiesChanged() {
- initIcons();
- }
-
- public void reset(boolean animate) {
- cancelAnimation();
- setTranslation(0.0f, true /* isReset */, animate);
- mMotionCancelled = true;
- if (mSwipingInProgress) {
- mCallback.onSwipingAborted();
- mSwipingInProgress = false;
- }
- }
-
- public boolean isSwipingInProgress() {
- return mSwipingInProgress;
- }
-
- public void launchAffordance(boolean animate, boolean left) {
- if (mSwipingInProgress) {
- // We don't want to mess with the state if the user is actually swiping already.
- return;
- }
- KeyguardAffordanceView targetView = left ? mLeftIcon : mRightIcon;
- KeyguardAffordanceView otherView = left ? mRightIcon : mLeftIcon;
- startSwiping(targetView);
-
- // Do not animate the circle expanding if the affordance isn't visible,
- // otherwise the circle will be meaningless.
- if (targetView.getVisibility() != View.VISIBLE) {
- animate = false;
- }
-
- if (animate) {
- fling(0, false, !left);
- updateIcon(otherView, 0.0f, 0, true, false, true, false);
- } else {
- mCallback.onAnimationToSideStarted(!left, mTranslation, 0);
- mTranslation = left ? mCallback.getMaxTranslationDistance()
- : mCallback.getMaxTranslationDistance();
- updateIcon(otherView, 0.0f, 0.0f, false, false, true, false);
- targetView.instantFinishAnimation();
- mFlingEndListener.onAnimationEnd(null);
- mAnimationEndRunnable.run();
- }
- }
-
- public interface Callback {
-
- /**
- * Notifies the callback when an animation to a side page was started.
- *
- * @param rightPage Is the page animated to the right page?
- */
- void onAnimationToSideStarted(boolean rightPage, float translation, float vel);
-
- /**
- * Notifies the callback the animation to a side page has ended.
- */
- void onAnimationToSideEnded();
-
- float getMaxTranslationDistance();
-
- void onSwipingStarted(boolean rightIcon);
-
- void onSwipingAborted();
-
- void onIconClicked(boolean rightIcon);
-
- KeyguardAffordanceView getLeftIcon();
-
- KeyguardAffordanceView getRightIcon();
-
- View getLeftPreview();
-
- View getRightPreview();
-
- /**
- * @return The factor the minimum swipe amount should be multiplied with.
- */
- float getAffordanceFalsingFactor();
-
- boolean needsAntiFalsing();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 414bc84fcd85..43a5451f4bb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -16,56 +16,31 @@
package com.android.systemui.statusbar.phone;
-import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
-import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-
import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
-import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON;
-import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK;
-import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON;
-import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_UNLOCK;
import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE;
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.MediaStore;
-import android.service.media.CameraPrewarmService;
import android.service.quickaccesswallet.GetWalletCardsError;
import android.service.quickaccesswallet.GetWalletCardsResponse;
import android.service.quickaccesswallet.QuickAccessWalletClient;
-import android.telecom.TelecomManager;
-import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -73,81 +48,38 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
-import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.Interpolators;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.camera.CameraIntents;
-import com.android.systemui.controls.ControlsServiceInfo;
import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.controls.management.ControlsListingController;
import com.android.systemui.controls.ui.ControlsActivity;
import com.android.systemui.controls.ui.ControlsUiController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.IntentButtonProvider;
-import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
-import com.android.systemui.plugins.IntentButtonProvider.IntentButton.IconState;
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
-import com.android.systemui.statusbar.KeyguardAffordanceView;
-import com.android.systemui.statusbar.policy.AccessibilityController;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.ExtensionController.Extension;
-import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.PreviewInflater;
-import com.android.systemui.tuner.LockscreenFragment.LockButtonFactory;
-import com.android.systemui.tuner.TunerService;
import com.android.systemui.wallet.controller.QuickAccessWalletController;
+import java.util.ArrayList;
import java.util.List;
/**
* Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
* text.
*/
-public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
- KeyguardStateController.Callback,
- AccessibilityController.AccessibilityStateChangedCallback {
-
- final static String TAG = "CentralSurfaces/KeyguardBottomAreaView";
-
- public static final String CAMERA_LAUNCH_SOURCE_AFFORDANCE = "lockscreen_affordance";
- public static final String CAMERA_LAUNCH_SOURCE_WIGGLE = "wiggle_gesture";
- public static final String CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = "power_double_tap";
- public static final String CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER = "lift_to_launch_ml";
-
- public static final String EXTRA_CAMERA_LAUNCH_SOURCE
- = "com.android.systemui.camera_launch_source";
-
- private static final String LEFT_BUTTON_PLUGIN
- = "com.android.systemui.action.PLUGIN_LOCKSCREEN_LEFT_BUTTON";
- private static final String RIGHT_BUTTON_PLUGIN
- = "com.android.systemui.action.PLUGIN_LOCKSCREEN_RIGHT_BUTTON";
+public class KeyguardBottomAreaView extends FrameLayout {
- private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
- private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
+ private static final String TAG = "CentralSurfaces/KeyguardBottomAreaView";
private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
- // TODO(b/179494051): May no longer be needed
- private final boolean mShowLeftAffordance;
- private final boolean mShowCameraAffordance;
-
- private KeyguardAffordanceView mRightAffordanceView;
- private KeyguardAffordanceView mLeftAffordanceView;
-
private ImageView mWalletButton;
private ImageView mQRCodeScannerButton;
private ImageView mControlsButton;
private boolean mHasCard = false;
- private WalletCardRetriever mCardRetriever = new WalletCardRetriever();
+ private final WalletCardRetriever mCardRetriever = new WalletCardRetriever();
private QuickAccessWalletController mQuickAccessWalletController;
private QRCodeScannerController mQRCodeScannerController;
private ControlsComponent mControlsComponent;
@@ -157,68 +89,41 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private ViewGroup mIndicationArea;
private TextView mIndicationText;
private TextView mIndicationTextBottom;
- private ViewGroup mPreviewContainer;
private ViewGroup mOverlayContainer;
- private View mLeftPreview;
- private View mCameraPreview;
-
private ActivityStarter mActivityStarter;
private KeyguardStateController mKeyguardStateController;
- private FlashlightController mFlashlightController;
- private PreviewInflater mPreviewInflater;
- private AccessibilityController mAccessibilityController;
- private CentralSurfaces mCentralSurfaces;
- private KeyguardAffordanceHelper mAffordanceHelper;
private FalsingManager mFalsingManager;
- private boolean mUserSetupComplete;
- private boolean mPrewarmBound;
- private Messenger mPrewarmMessenger;
- private final ServiceConnection mPrewarmConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mPrewarmMessenger = new Messenger(service);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mPrewarmMessenger = null;
- }
- };
-
- private boolean mLeftIsVoiceAssist;
- private Drawable mLeftAssistIcon;
-
- private IntentButton mRightButton = new DefaultRightButton();
- private Extension<IntentButton> mRightExtension;
- private String mRightButtonStr;
- private IntentButton mLeftButton = new DefaultLeftButton();
- private Extension<IntentButton> mLeftExtension;
- private String mLeftButtonStr;
private boolean mDozing;
private int mIndicationBottomMargin;
private int mIndicationPadding;
private float mDarkAmount;
private int mBurnInXOffset;
private int mBurnInYOffset;
- private ActivityIntentHelper mActivityIntentHelper;
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-
- private ControlsListingController.ControlsListingCallback mListingCallback =
- new ControlsListingController.ControlsListingCallback() {
- public void onServicesUpdated(List<ControlsServiceInfo> serviceInfos) {
- post(() -> {
- boolean available = !serviceInfos.isEmpty();
-
- if (available != mControlServicesAvailable) {
- mControlServicesAvailable = available;
- updateControlsVisibility();
- updateAffordanceColors();
- }
- });
+
+ private final ControlsListingController.ControlsListingCallback mListingCallback =
+ serviceInfos -> post(() -> {
+ boolean available = !serviceInfos.isEmpty();
+
+ if (available != mControlServicesAvailable) {
+ mControlServicesAvailable = available;
+ updateControlsVisibility();
+ updateAffordanceColors();
+ }
+ });
+
+ private final KeyguardStateController.Callback mKeyguardStateCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
+ if (mKeyguardStateController.isShowing()) {
+ if (mQuickAccessWalletController != null) {
+ mQuickAccessWalletController.queryWalletCards(mCardRetriever);
}
- };
+ }
+ }
+ };
public KeyguardBottomAreaView(Context context) {
this(context, null);
@@ -235,46 +140,40 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mShowLeftAffordance = getResources().getBoolean(R.bool.config_keyguardShowLeftAffordance);
- mShowCameraAffordance = getResources()
- .getBoolean(R.bool.config_keyguardShowCameraAffordance);
}
- private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
- @Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- String label = null;
- if (host == mRightAffordanceView) {
- label = getResources().getString(R.string.camera_label);
- } else if (host == mLeftAffordanceView) {
- if (mLeftIsVoiceAssist) {
- label = getResources().getString(R.string.voice_assist_label);
- } else {
- label = getResources().getString(R.string.phone_label);
- }
- }
- info.addAction(new AccessibilityAction(ACTION_CLICK, label));
- }
+ /** Initializes the {@link KeyguardBottomAreaView} with the given dependencies */
+ public void init(
+ FalsingManager falsingManager,
+ QuickAccessWalletController controller,
+ ControlsComponent controlsComponent,
+ QRCodeScannerController qrCodeScannerController) {
+ mFalsingManager = falsingManager;
+ mQuickAccessWalletController = controller;
+ mQuickAccessWalletController.setupWalletChangeObservers(
+ mCardRetriever, WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE);
+ mQuickAccessWalletController.updateWalletPreference();
+ mQuickAccessWalletController.queryWalletCards(mCardRetriever);
+ updateWalletVisibility();
- @Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
- if (action == ACTION_CLICK) {
- if (host == mRightAffordanceView) {
- launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE);
- return true;
- } else if (host == mLeftAffordanceView) {
- launchLeftAffordance();
- return true;
- }
- }
- return super.performAccessibilityAction(host, action, args);
- }
- };
+ mControlsComponent = controlsComponent;
+ mControlsComponent.getControlsListingController().ifPresent(
+ c -> c.addCallback(mListingCallback));
- public void initFrom(KeyguardBottomAreaView oldBottomArea) {
- setCentralSurfaces(oldBottomArea.mCentralSurfaces);
+ mQRCodeScannerController = qrCodeScannerController;
+ mQRCodeScannerController.registerQRCodeScannerChangeObservers(
+ QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE,
+ QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE);
+ updateQRCodeButtonVisibility();
+ updateAffordanceColors();
+ }
+
+ /**
+ * Initializes this instance of {@link KeyguardBottomAreaView} based on the given instance of
+ * another {@link KeyguardBottomAreaView}
+ */
+ public void initFrom(KeyguardBottomAreaView oldBottomArea) {
// if it exists, continue to use the original ambient indication container
// instead of the newly inflated one
if (mAmbientIndicationArea != null) {
@@ -302,11 +201,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext),
- new ActivityIntentHelper(mContext));
mOverlayContainer = findViewById(R.id.overlay_container);
- mRightAffordanceView = findViewById(R.id.camera_button);
- mLeftAffordanceView = findViewById(R.id.left_button);
mWalletButton = findViewById(R.id.wallet_button);
mQRCodeScannerButton = findViewById(R.id.qr_code_scanner_button);
mControlsButton = findViewById(R.id.controls_button);
@@ -318,18 +213,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
R.dimen.keyguard_indication_margin_bottom);
mBurnInYOffset = getResources().getDimensionPixelSize(
R.dimen.default_burn_in_prevention_offset);
- updateCameraVisibility();
mKeyguardStateController = Dependency.get(KeyguardStateController.class);
- mKeyguardStateController.addCallback(this);
+ mKeyguardStateController.addCallback(mKeyguardStateCallback);
setClipChildren(false);
setClipToPadding(false);
- mRightAffordanceView.setOnClickListener(this);
- mLeftAffordanceView.setOnClickListener(this);
- initAccessibility();
mActivityStarter = Dependency.get(ActivityStarter.class);
- mFlashlightController = Dependency.get(FlashlightController.class);
- mAccessibilityController = Dependency.get(AccessibilityController.class);
- mActivityIntentHelper = new ActivityIntentHelper(getContext());
mIndicationPadding = getResources().getDimensionPixelSize(
R.dimen.keyguard_indication_area_padding);
@@ -338,51 +226,18 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
updateControlsVisibility();
}
- /**
- * Set the container where the previews are rendered.
- */
- public void setPreviewContainer(ViewGroup previewContainer) {
- mPreviewContainer = previewContainer;
- inflateCameraPreview();
- updateLeftAffordance();
- }
-
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mAccessibilityController.addStateChangedCallback(this);
- mRightExtension = Dependency.get(ExtensionController.class).newExtension(IntentButton.class)
- .withPlugin(IntentButtonProvider.class, RIGHT_BUTTON_PLUGIN,
- p -> p.getIntentButton())
- .withTunerFactory(new LockButtonFactory(mContext, LOCKSCREEN_RIGHT_BUTTON))
- .withDefault(() -> new DefaultRightButton())
- .withCallback(button -> setRightButton(button))
- .build();
- mLeftExtension = Dependency.get(ExtensionController.class).newExtension(IntentButton.class)
- .withPlugin(IntentButtonProvider.class, LEFT_BUTTON_PLUGIN,
- p -> p.getIntentButton())
- .withTunerFactory(new LockButtonFactory(mContext, LOCKSCREEN_LEFT_BUTTON))
- .withDefault(() -> new DefaultLeftButton())
- .withCallback(button -> setLeftButton(button))
- .build();
final IntentFilter filter = new IntentFilter();
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
- getContext().registerReceiverAsUser(mDevicePolicyReceiver,
- UserHandle.ALL, filter, null, null);
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
- mKeyguardStateController.addCallback(this);
+ mKeyguardStateController.addCallback(mKeyguardStateCallback);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mKeyguardStateController.removeCallback(this);
- mAccessibilityController.removeStateChangedCallback(this);
- mRightExtension.destroy();
- mLeftExtension.destroy();
- getContext().unregisterReceiver(mDevicePolicyReceiver);
- mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
+ mKeyguardStateController.removeCallback(mKeyguardStateCallback);
if (mQuickAccessWalletController != null) {
mQuickAccessWalletController.unregisterWalletChangeObservers(
@@ -401,11 +256,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
}
- private void initAccessibility() {
- mLeftAffordanceView.setAccessibilityDelegate(mAccessibilityDelegate);
- mRightAffordanceView.setAccessibilityDelegate(mAccessibilityDelegate);
- }
-
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -427,19 +277,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
getResources().getDimensionPixelSize(
com.android.internal.R.dimen.text_size_small_material));
- ViewGroup.LayoutParams lp = mRightAffordanceView.getLayoutParams();
- lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width);
- lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
- mRightAffordanceView.setLayoutParams(lp);
- updateRightAffordanceIcon();
-
- lp = mLeftAffordanceView.getLayoutParams();
- lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width);
- lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
- mLeftAffordanceView.setLayoutParams(lp);
- updateLeftAffordanceIcon();
-
- lp = mWalletButton.getLayoutParams();
+ ViewGroup.LayoutParams lp = mWalletButton.getLayoutParams();
lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width);
lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height);
mWalletButton.setLayoutParams(lp);
@@ -462,76 +300,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
updateAffordanceColors();
}
- private void updateRightAffordanceIcon() {
- IconState state = mRightButton.getIcon();
- mRightAffordanceView.setVisibility(!mDozing && state.isVisible ? View.VISIBLE : View.GONE);
- if (state.drawable != mRightAffordanceView.getDrawable()
- || state.tint != mRightAffordanceView.shouldTint()) {
- mRightAffordanceView.setImageDrawable(state.drawable, state.tint);
- }
- mRightAffordanceView.setContentDescription(state.contentDescription);
- }
-
- public void setCentralSurfaces(CentralSurfaces centralSurfaces) {
- mCentralSurfaces = centralSurfaces;
- updateCameraVisibility(); // in case onFinishInflate() was called too early
- }
-
- public void setAffordanceHelper(KeyguardAffordanceHelper affordanceHelper) {
- mAffordanceHelper = affordanceHelper;
- }
-
- public void setUserSetupComplete(boolean userSetupComplete) {
- mUserSetupComplete = userSetupComplete;
- updateCameraVisibility();
- updateLeftAffordanceIcon();
- }
-
- private Intent getCameraIntent() {
- return mRightButton.getIntent();
- }
-
- /**
- * Resolves the intent to launch the camera application.
- */
- public ResolveInfo resolveCameraIntent() {
- return mContext.getPackageManager().resolveActivityAsUser(getCameraIntent(),
- PackageManager.MATCH_DEFAULT_ONLY,
- KeyguardUpdateMonitor.getCurrentUser());
- }
-
- private void updateCameraVisibility() {
- if (mRightAffordanceView == null) {
- // Things are not set up yet; reply hazy, ask again later
- return;
- }
- mRightAffordanceView.setVisibility(!mDozing && mShowCameraAffordance
- && mRightButton.getIcon().isVisible ? View.VISIBLE : View.GONE);
- }
-
- /**
- * Set an alternate icon for the left assist affordance (replace the mic icon)
- */
- public void setLeftAssistIcon(Drawable drawable) {
- mLeftAssistIcon = drawable;
- updateLeftAffordanceIcon();
- }
-
- private void updateLeftAffordanceIcon() {
- if (!mShowLeftAffordance || mDozing) {
- mLeftAffordanceView.setVisibility(GONE);
- return;
- }
-
- IconState state = mLeftButton.getIcon();
- mLeftAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE);
- if (state.drawable != mLeftAffordanceView.getDrawable()
- || state.tint != mLeftAffordanceView.shouldTint()) {
- mLeftAffordanceView.setImageDrawable(state.drawable, state.tint);
- }
- mLeftAffordanceView.setContentDescription(state.contentDescription);
- }
-
private void updateWalletVisibility() {
if (mDozing
|| mQuickAccessWalletController == null
@@ -575,128 +343,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
}
- public boolean isLeftVoiceAssist() {
- return mLeftIsVoiceAssist;
- }
-
- private boolean isPhoneVisible() {
- PackageManager pm = mContext.getPackageManager();
- return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
- && pm.resolveActivity(PHONE_INTENT, 0) != null;
- }
-
- @Override
- public void onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled) {
- mRightAffordanceView.setClickable(touchExplorationEnabled);
- mLeftAffordanceView.setClickable(touchExplorationEnabled);
- mRightAffordanceView.setFocusable(accessibilityEnabled);
- mLeftAffordanceView.setFocusable(accessibilityEnabled);
- }
-
- @Override
- public void onClick(View v) {
- if (v == mRightAffordanceView) {
- launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE);
- } else if (v == mLeftAffordanceView) {
- launchLeftAffordance();
- }
- }
-
- public void bindCameraPrewarmService() {
- Intent intent = getCameraIntent();
- ActivityInfo targetInfo = mActivityIntentHelper.getTargetActivityInfo(intent,
- KeyguardUpdateMonitor.getCurrentUser(), true /* onlyDirectBootAware */);
- if (targetInfo != null && targetInfo.metaData != null) {
- String clazz = targetInfo.metaData.getString(
- MediaStore.META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE);
- if (clazz != null) {
- Intent serviceIntent = new Intent();
- serviceIntent.setClassName(targetInfo.packageName, clazz);
- serviceIntent.setAction(CameraPrewarmService.ACTION_PREWARM);
- try {
- if (getContext().bindServiceAsUser(serviceIntent, mPrewarmConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
- new UserHandle(UserHandle.USER_CURRENT))) {
- mPrewarmBound = true;
- }
- } catch (SecurityException e) {
- Log.w(TAG, "Unable to bind to prewarm service package=" + targetInfo.packageName
- + " class=" + clazz, e);
- }
- }
- }
- }
-
- public void unbindCameraPrewarmService(boolean launched) {
- if (mPrewarmBound) {
- if (mPrewarmMessenger != null && launched) {
- try {
- mPrewarmMessenger.send(Message.obtain(null /* handler */,
- CameraPrewarmService.MSG_CAMERA_FIRED));
- } catch (RemoteException e) {
- Log.w(TAG, "Error sending camera fired message", e);
- }
- }
- mContext.unbindService(mPrewarmConnection);
- mPrewarmBound = false;
- }
- }
-
- public void launchCamera(String source) {
- final Intent intent = getCameraIntent();
- intent.putExtra(EXTRA_CAMERA_LAUNCH_SOURCE, source);
- boolean wouldLaunchResolverActivity = mActivityIntentHelper.wouldLaunchResolverActivity(
- intent, KeyguardUpdateMonitor.getCurrentUser());
- if (CameraIntents.isSecureCameraIntent(intent) && !wouldLaunchResolverActivity) {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- int result = ActivityManager.START_CANCELED;
-
- // Normally an activity will set it's requested rotation
- // animation on its window. However when launching an activity
- // causes the orientation to change this is too late. In these cases
- // the default animation is used. This doesn't look good for
- // the camera (as it rotates the camera contents out of sync
- // with physical reality). So, we ask the WindowManager to
- // force the crossfade animation if an orientation change
- // happens to occur during the launch.
- ActivityOptions o = ActivityOptions.makeBasic();
- o.setDisallowEnterPictureInPictureWhileLaunching(true);
- o.setRotationAnimationHint(
- WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
- try {
- result = ActivityTaskManager.getService().startActivityAsUser(
- null, getContext().getBasePackageName(),
- getContext().getAttributionTag(), intent,
- intent.resolveTypeIfNeeded(getContext().getContentResolver()),
- null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, o.toBundle(),
- UserHandle.CURRENT.getIdentifier());
- } catch (RemoteException e) {
- Log.w(TAG, "Unable to start camera activity", e);
- }
- final boolean launched = isSuccessfulLaunch(result);
- post(new Runnable() {
- @Override
- public void run() {
- unbindCameraPrewarmService(launched);
- }
- });
- }
- });
- } else {
- // We need to delay starting the activity because ResolverActivity finishes itself if
- // launched behind lockscreen.
- mActivityStarter.startActivity(intent, false /* dismissShade */,
- new ActivityStarter.Callback() {
- @Override
- public void onActivityStarted(int resultCode) {
- unbindCameraPrewarmService(isSuccessfulLaunch(resultCode));
- }
- });
- }
- }
-
public void setDarkAmount(float darkAmount) {
if (darkAmount == mDarkAmount) {
return;
@@ -705,85 +351,17 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
dozeTimeTick();
}
- private static boolean isSuccessfulLaunch(int result) {
- return result == ActivityManager.START_SUCCESS
- || result == ActivityManager.START_DELIVERED_TO_TOP
- || result == ActivityManager.START_TASK_TO_FRONT;
- }
-
- public void launchLeftAffordance() {
- if (mLeftIsVoiceAssist) {
- launchVoiceAssist();
- } else {
- launchPhone();
- }
- }
-
- @VisibleForTesting
- void launchVoiceAssist() {
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- Dependency.get(AssistManager.class).launchVoiceAssistFromKeyguard();
- }
- };
- if (!mKeyguardStateController.canDismissLockScreen()) {
- Dependency.get(Dependency.BACKGROUND_EXECUTOR).execute(runnable);
- } else {
- boolean dismissShade = !TextUtils.isEmpty(mRightButtonStr)
- && Dependency.get(TunerService.class).getValue(LOCKSCREEN_RIGHT_UNLOCK, 1) != 0;
- mCentralSurfaces.executeRunnableDismissingKeyguard(runnable, null /* cancelAction */,
- dismissShade, false /* afterKeyguardGone */, true /* deferred */);
- }
- }
-
- private boolean canLaunchVoiceAssist() {
- return Dependency.get(AssistManager.class).canVoiceAssistBeLaunchedFromKeyguard();
- }
-
- private void launchPhone() {
- final TelecomManager tm = TelecomManager.from(mContext);
- if (tm.isInCall()) {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- tm.showInCallScreen(false /* showDialpad */);
- }
- });
- } else {
- boolean dismissShade = !TextUtils.isEmpty(mLeftButtonStr)
- && Dependency.get(TunerService.class).getValue(LOCKSCREEN_LEFT_UNLOCK, 1) != 0;
- mActivityStarter.startActivity(mLeftButton.getIntent(), dismissShade);
+ /**
+ * Returns a list of animators to use to animate the indication areas.
+ */
+ public List<ViewPropertyAnimator> getIndicationAreaAnimators() {
+ List<ViewPropertyAnimator> animators =
+ new ArrayList<>(mAmbientIndicationArea != null ? 2 : 1);
+ animators.add(mIndicationArea.animate());
+ if (mAmbientIndicationArea != null) {
+ animators.add(mAmbientIndicationArea.animate());
}
- }
-
-
- @Override
- protected void onVisibilityChanged(View changedView, int visibility) {
- super.onVisibilityChanged(changedView, visibility);
- if (changedView == this && visibility == VISIBLE) {
- updateCameraVisibility();
- }
- }
-
- public KeyguardAffordanceView getLeftView() {
- return mLeftAffordanceView;
- }
-
- public KeyguardAffordanceView getRightView() {
- return mRightAffordanceView;
- }
-
- public View getLeftPreview() {
- return mLeftPreview;
- }
-
- public View getRightPreview() {
- return mCameraPreview;
- }
-
- public View getIndicationArea() {
- return mIndicationArea;
+ return animators;
}
@Override
@@ -791,67 +369,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
return false;
}
- @Override
- public void onUnlockedChanged() {
- updateCameraVisibility();
- }
-
- @Override
- public void onKeyguardShowingChanged() {
- if (mKeyguardStateController.isShowing()) {
- if (mQuickAccessWalletController != null) {
- mQuickAccessWalletController.queryWalletCards(mCardRetriever);
- }
- }
- }
-
- private void inflateCameraPreview() {
- if (mPreviewContainer == null) {
- return;
- }
- View previewBefore = mCameraPreview;
- boolean visibleBefore = false;
- if (previewBefore != null) {
- mPreviewContainer.removeView(previewBefore);
- visibleBefore = previewBefore.getVisibility() == View.VISIBLE;
- }
- mCameraPreview = mPreviewInflater.inflatePreview(getCameraIntent());
- if (mCameraPreview != null) {
- mPreviewContainer.addView(mCameraPreview);
- mCameraPreview.setVisibility(visibleBefore ? View.VISIBLE : View.INVISIBLE);
- }
- if (mAffordanceHelper != null) {
- mAffordanceHelper.updatePreviews();
- }
- }
-
- private void updateLeftPreview() {
- if (mPreviewContainer == null) {
- return;
- }
- View previewBefore = mLeftPreview;
- if (previewBefore != null) {
- mPreviewContainer.removeView(previewBefore);
- }
-
- if (mLeftIsVoiceAssist) {
- if (Dependency.get(AssistManager.class).getVoiceInteractorComponentName() != null) {
- mLeftPreview = mPreviewInflater.inflatePreviewFromService(
- Dependency.get(AssistManager.class).getVoiceInteractorComponentName());
- }
- } else {
- mLeftPreview = mPreviewInflater.inflatePreview(mLeftButton.getIntent());
- }
- if (mLeftPreview != null) {
- mPreviewContainer.addView(mLeftPreview);
- mLeftPreview.setVisibility(View.INVISIBLE);
- }
- if (mAffordanceHelper != null) {
- mAffordanceHelper.updatePreviews();
- }
- }
-
- public void startFinishDozeAnimation() {
+ private void startFinishDozeAnimation() {
long delay = 0;
if (mWalletButton.getVisibility() == View.VISIBLE) {
startFinishDozeAnimationElement(mWalletButton, delay);
@@ -862,13 +380,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
if (mControlsButton.getVisibility() == View.VISIBLE) {
startFinishDozeAnimationElement(mControlsButton, delay);
}
- if (mLeftAffordanceView.getVisibility() == View.VISIBLE) {
- startFinishDozeAnimationElement(mLeftAffordanceView, delay);
- delay += DOZE_ANIMATION_STAGGER_DELAY;
- }
- if (mRightAffordanceView.getVisibility() == View.VISIBLE) {
- startFinishDozeAnimationElement(mRightAffordanceView, delay);
- }
}
private void startFinishDozeAnimationElement(View element, long delay) {
@@ -882,58 +393,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
.setDuration(DOZE_ANIMATION_ELEMENT_DURATION);
}
- private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- post(new Runnable() {
- @Override
- public void run() {
- updateCameraVisibility();
- }
- });
- }
- };
-
- private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
- new KeyguardUpdateMonitorCallback() {
- @Override
- public void onUserSwitchComplete(int userId) {
- updateCameraVisibility();
- }
-
- @Override
- public void onUserUnlocked() {
- inflateCameraPreview();
- updateCameraVisibility();
- updateLeftAffordance();
- }
- };
-
- public void updateLeftAffordance() {
- updateLeftAffordanceIcon();
- updateLeftPreview();
- }
-
- private void setRightButton(IntentButton button) {
- mRightButton = button;
- updateRightAffordanceIcon();
- updateCameraVisibility();
- inflateCameraPreview();
- }
-
- private void setLeftButton(IntentButton button) {
- mLeftButton = button;
- if (!(mLeftButton instanceof DefaultLeftButton)) {
- mLeftIsVoiceAssist = false;
- }
- updateLeftAffordance();
- }
-
public void setDozing(boolean dozing, boolean animate) {
mDozing = dozing;
- updateCameraVisibility();
- updateLeftAffordanceIcon();
updateWalletVisibility();
updateControlsVisibility();
updateQRCodeButtonVisibility();
@@ -969,80 +431,23 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
/**
- * Sets the alpha of the indication areas and affordances, excluding the lock icon.
+ * Sets the alpha of various sub-components, for example the indication areas and bottom quick
+ * action buttons. Does not set the alpha of the lock icon.
*/
- public void setAffordanceAlpha(float alpha) {
- mLeftAffordanceView.setAlpha(alpha);
- mRightAffordanceView.setAlpha(alpha);
+ public void setComponentAlphas(float alpha) {
+ setImportantForAccessibility(
+ alpha == 0f
+ ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ if (mAmbientIndicationArea != null) {
+ mAmbientIndicationArea.setAlpha(alpha);
+ }
mIndicationArea.setAlpha(alpha);
mWalletButton.setAlpha(alpha);
mQRCodeScannerButton.setAlpha(alpha);
mControlsButton.setAlpha(alpha);
}
- private class DefaultLeftButton implements IntentButton {
-
- private IconState mIconState = new IconState();
-
- @Override
- public IconState getIcon() {
- mLeftIsVoiceAssist = canLaunchVoiceAssist();
- if (mLeftIsVoiceAssist) {
- mIconState.isVisible = mUserSetupComplete && mShowLeftAffordance;
- if (mLeftAssistIcon == null) {
- mIconState.drawable = mContext.getDrawable(R.drawable.ic_mic_26dp);
- } else {
- mIconState.drawable = mLeftAssistIcon;
- }
- mIconState.contentDescription = mContext.getString(
- R.string.accessibility_voice_assist_button);
- } else {
- mIconState.isVisible = mUserSetupComplete && mShowLeftAffordance
- && isPhoneVisible();
- mIconState.drawable = mContext.getDrawable(
- com.android.internal.R.drawable.ic_phone);
- mIconState.contentDescription = mContext.getString(
- R.string.accessibility_phone_button);
- }
- return mIconState;
- }
-
- @Override
- public Intent getIntent() {
- return PHONE_INTENT;
- }
- }
-
- private class DefaultRightButton implements IntentButton {
-
- private IconState mIconState = new IconState();
-
- @Override
- public IconState getIcon() {
- boolean isCameraDisabled = (mCentralSurfaces != null)
- && !mCentralSurfaces.isCameraAllowedByAdmin();
- mIconState.isVisible = !isCameraDisabled
- && mShowCameraAffordance
- && mUserSetupComplete
- && resolveCameraIntent() != null;
- mIconState.drawable = mContext.getDrawable(R.drawable.ic_camera_alt_24dp);
- mIconState.contentDescription =
- mContext.getString(R.string.accessibility_camera_button);
- return mIconState;
- }
-
- @Override
- public Intent getIntent() {
- boolean canDismissLs = mKeyguardStateController.canDismissLockScreen();
- boolean secure = mKeyguardStateController.isMethodSecure();
- if (secure && !canDismissLs) {
- return CameraIntents.getSecureCameraIntent(getContext());
- } else {
- return CameraIntents.getInsecureCameraIntent(getContext());
- }
- }
- }
-
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
int bottom = insets.getDisplayCutout() != null
@@ -1055,38 +460,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
return insets;
}
- /** Set the falsing manager */
- public void setFalsingManager(FalsingManager falsingManager) {
- mFalsingManager = falsingManager;
- }
-
- /**
- * Initialize the wallet feature, only enabling if the feature is enabled within the platform.
- */
- public void initWallet(
- QuickAccessWalletController controller) {
- mQuickAccessWalletController = controller;
- mQuickAccessWalletController.setupWalletChangeObservers(
- mCardRetriever, WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE);
- mQuickAccessWalletController.updateWalletPreference();
- mQuickAccessWalletController.queryWalletCards(mCardRetriever);
-
- updateWalletVisibility();
- updateAffordanceColors();
- }
-
- /**
- * Initialize the qr code scanner feature, controlled by QRCodeScannerController.
- */
- public void initQRCodeScanner(QRCodeScannerController qrCodeScannerController) {
- mQRCodeScannerController = qrCodeScannerController;
- mQRCodeScannerController.registerQRCodeScannerChangeObservers(
- QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE,
- QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE);
- updateQRCodeButtonVisibility();
- updateAffordanceColors();
- }
-
private void updateQRCodeButtonVisibility() {
if (mQuickAccessWalletController != null
&& mQuickAccessWalletController.isWalletEnabled()) {
@@ -1142,17 +515,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mQRCodeScannerButton.setBackgroundTintList(bgColor);
}
- /**
- * Initialize controls via the ControlsComponent
- */
- public void initControls(ControlsComponent controlsComponent) {
- mControlsComponent = controlsComponent;
- mControlsComponent.getControlsListingController().ifPresent(
- c -> c.addCallback(mListingCallback));
-
- updateAffordanceColors();
- }
-
private void onWalletClick(View v) {
// More coming here; need to inform the user about how to proceed
if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
@@ -1195,10 +557,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
public void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response) {
mHasCard = !response.getWalletCards().isEmpty();
Drawable tileIcon = mQuickAccessWalletController.getWalletClient().getTileIcon();
+ if (tileIcon != null) {
+ mWalletButton.setImageDrawable(tileIcon);
+ }
post(() -> {
- if (tileIcon != null) {
- mWalletButton.setImageDrawable(tileIcon);
- }
updateWalletVisibility();
updateAffordanceColors();
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
new file mode 100644
index 000000000000..3942dae7e0c3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 com.android.systemui.util.ViewController
+import javax.inject.Inject
+
+class KeyguardBottomAreaViewController @Inject constructor(view: KeyguardBottomAreaView) :
+ ViewController<KeyguardBottomAreaView> (view) {
+ override fun onViewAttached() {
+ }
+
+ override fun onViewDetached() {
+ }
+
+ fun getView(): KeyguardBottomAreaView {
+ // TODO: remove this method.
+ return mView
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index a9048002fb44..0001cd0a2798 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -266,6 +266,9 @@ public class KeyguardBouncer {
private void setVisibility(@View.Visibility int visibility) {
mContainer.setVisibility(visibility);
+ if (mKeyguardViewController != null) {
+ mKeyguardViewController.onBouncerVisibilityChanged(visibility);
+ }
dispatchVisibilityChanged();
}
@@ -640,6 +643,10 @@ public class KeyguardBouncer {
public interface BouncerExpansionCallback {
/**
* Invoked when the bouncer expansion reaches {@link KeyguardBouncer#EXPANSION_VISIBLE}.
+ * This is NOT called each time the bouncer is shown, but rather only when the fully
+ * shown amount has changed based on the panel expansion. The bouncer's visibility
+ * can still change when the expansion amount hasn't changed.
+ * See {@link KeyguardBouncer#isShowing()} for the checks for the bouncer showing state.
*/
default void onFullyShown() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 629aa0317d24..01af48616b65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -27,6 +27,7 @@ import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView;
/**
@@ -309,9 +310,12 @@ public class KeyguardClockPositionAlgorithm {
*/
private float getClockAlpha(int y) {
float alphaKeyguard = Math.max(0, y / Math.max(1f, getClockY(1f, mDarkAmount)));
- float qsAlphaFactor = MathUtils.saturate(mQsExpansion / 0.3f);
- qsAlphaFactor = 1f - qsAlphaFactor;
- alphaKeyguard *= qsAlphaFactor;
+ if (!mIsSplitShade) {
+ // in split shade QS are always expanded so this factor shouldn't apply
+ float qsAlphaFactor = MathUtils.saturate(mQsExpansion / 0.3f);
+ qsAlphaFactor = 1f - qsAlphaFactor;
+ alphaKeyguard *= qsAlphaFactor;
+ }
alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard);
return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index def574c1890f..f06b346780da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -42,6 +42,7 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LargeScreenShadeHeaderController.kt
deleted file mode 100644
index 178c17dd5694..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LargeScreenShadeHeaderController.kt
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.app.StatusBarManager
-import android.view.View
-import android.widget.TextView
-import androidx.constraintlayout.motion.widget.MotionLayout
-import com.android.settingslib.Utils
-import com.android.systemui.Dumpable
-import com.android.systemui.FontSizeUtils
-import com.android.systemui.R
-import com.android.systemui.animation.ShadeInterpolation
-import com.android.systemui.battery.BatteryMeterView
-import com.android.systemui.battery.BatteryMeterViewController
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.qs.ChipVisibilityListener
-import com.android.systemui.qs.HeaderPrivacyIconsController
-import com.android.systemui.qs.carrier.QSCarrierGroup
-import com.android.systemui.qs.carrier.QSCarrierGroupController
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
-import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.LARGE_SCREEN_BATTERY_CONTROLLER
-import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.LARGE_SCREEN_SHADE_HEADER
-import com.android.systemui.statusbar.policy.ConfigurationController
-import java.io.PrintWriter
-import javax.inject.Inject
-import javax.inject.Named
-
-@CentralSurfacesScope
-class LargeScreenShadeHeaderController @Inject constructor(
- @Named(LARGE_SCREEN_SHADE_HEADER) private val header: View,
- private val statusBarIconController: StatusBarIconController,
- private val privacyIconsController: HeaderPrivacyIconsController,
- private val configurationController: ConfigurationController,
- qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
- featureFlags: FeatureFlags,
- @Named(LARGE_SCREEN_BATTERY_CONTROLLER) batteryMeterViewController: BatteryMeterViewController,
- dumpManager: DumpManager
-) : Dumpable {
-
- companion object {
- private val HEADER_TRANSITION_ID = R.id.header_transition
- private val LARGE_SCREEN_HEADER_TRANSITION_ID = R.id.large_screen_header_transition
- private val QQS_HEADER_CONSTRAINT = R.id.qqs_header_constraint
- private val QS_HEADER_CONSTRAINT = R.id.qs_header_constraint
- private val LARGE_SCREEN_HEADER_CONSTRAINT = R.id.large_screen_header_constraint
-
- private fun Int.stateToString() = when (this) {
- QQS_HEADER_CONSTRAINT -> "QQS Header"
- QS_HEADER_CONSTRAINT -> "QS Header"
- LARGE_SCREEN_HEADER_CONSTRAINT -> "Large Screen Header"
- else -> "Unknown state"
- }
- }
-
- private val combinedHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)
- private val iconManager: StatusBarIconController.TintedIconManager
- private val iconContainer: StatusIconContainer
- private val carrierIconSlots: List<String>
- private val qsCarrierGroupController: QSCarrierGroupController
- private val clock: TextView = header.findViewById(R.id.clock)
- private val date: TextView = header.findViewById(R.id.date)
- private val qsCarrierGroup: QSCarrierGroup = header.findViewById(R.id.carrier_group)
-
- private var qsDisabled = false
-
- private var visible = false
- set(value) {
- if (field == value) {
- return
- }
- field = value
- updateListeners()
- }
-
- var shadeExpanded = false
- set(value) {
- if (field == value) {
- return
- }
- field = value
- onShadeExpandedChanged()
- }
-
- var active = false
- set(value) {
- if (field == value) {
- return
- }
- field = value
- onHeaderStateChanged()
- }
-
- var shadeExpandedFraction = -1f
- set(value) {
- if (visible && field != value) {
- header.alpha = ShadeInterpolation.getContentAlpha(value)
- field = value
- }
- }
-
- var qsExpandedFraction = -1f
- set(value) {
- if (visible && field != value) {
- field = value
- updateVisibility()
- updatePosition()
- }
- }
-
- var qsScrollY = 0
- set(value) {
- if (field != value) {
- field = value
- updateScrollY()
- }
- }
-
- private val chipVisibilityListener: ChipVisibilityListener = object : ChipVisibilityListener {
- override fun onChipVisibilityRefreshed(visible: Boolean) {
- if (header is MotionLayout) {
- val state = header.getConstraintSet(QQS_HEADER_CONSTRAINT).apply {
- setAlpha(R.id.statusIcons, if (visible) 0f else 1f)
- setAlpha(R.id.batteryRemainingIcon, if (visible) 0f else 1f)
- }
- header.updateState(QQS_HEADER_CONSTRAINT, state)
- }
- }
- }
-
- init {
- if (header is MotionLayout) {
- val context = header.context
- val resources = header.resources
- header.getConstraintSet(QQS_HEADER_CONSTRAINT)
- .load(context, resources.getXml(R.xml.qqs_header))
- header.getConstraintSet(QS_HEADER_CONSTRAINT)
- .load(context, resources.getXml(R.xml.qs_header))
- header.getConstraintSet(LARGE_SCREEN_HEADER_CONSTRAINT)
- .load(context, resources.getXml(R.xml.large_screen_shade_header))
- privacyIconsController.chipVisibilityListener = chipVisibilityListener
- }
-
- bindConfigurationListener()
-
- batteryMeterViewController.init()
- val batteryIcon: BatteryMeterView = header.findViewById(R.id.batteryRemainingIcon)
-
- // battery settings same as in QS icons
- batteryMeterViewController.ignoreTunerUpdates()
- batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
-
- iconContainer = header.findViewById(R.id.statusIcons)
- iconManager = StatusBarIconController.TintedIconManager(iconContainer, featureFlags)
- iconManager.setTint(Utils.getColorAttrDefaultColor(header.context,
- android.R.attr.textColorPrimary))
-
- carrierIconSlots = if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
- listOf(
- header.context.getString(com.android.internal.R.string.status_bar_no_calling),
- header.context.getString(com.android.internal.R.string.status_bar_call_strength)
- )
- } else {
- listOf(header.context.getString(com.android.internal.R.string.status_bar_mobile))
- }
- qsCarrierGroupController = qsCarrierGroupControllerBuilder
- .setQSCarrierGroup(header.findViewById(R.id.carrier_group))
- .build()
-
- dumpManager.registerDumpable(this)
-
- updateVisibility()
- updateConstraints()
- }
-
- fun disable(state1: Int, state2: Int, animate: Boolean) {
- val disabled = state2 and StatusBarManager.DISABLE2_QUICK_SETTINGS != 0
- if (disabled == qsDisabled) return
- qsDisabled = disabled
- updateVisibility()
- }
-
- private fun updateScrollY() {
- if (!active && combinedHeaders) {
- header.scrollY = qsScrollY
- }
- }
-
- private fun bindConfigurationListener() {
- val listener = object : ConfigurationController.ConfigurationListener {
- override fun onDensityOrFontScaleChanged() {
- val qsStatusStyle = R.style.TextAppearance_QS_Status
- FontSizeUtils.updateFontSizeFromStyle(clock, qsStatusStyle)
- FontSizeUtils.updateFontSizeFromStyle(date, qsStatusStyle)
- qsCarrierGroup.updateTextAppearance(qsStatusStyle)
- }
- }
- configurationController.addCallback(listener)
- }
-
- private fun onShadeExpandedChanged() {
- if (shadeExpanded) {
- privacyIconsController.startListening()
- } else {
- privacyIconsController.stopListening()
- }
- updateVisibility()
- updatePosition()
- }
-
- private fun onHeaderStateChanged() {
- if (active || combinedHeaders) {
- privacyIconsController.onParentVisible()
- } else {
- privacyIconsController.onParentInvisible()
- }
- updateVisibility()
- updateConstraints()
- }
-
- private fun updateVisibility() {
- val visibility = if (!active && !combinedHeaders || qsDisabled) {
- View.GONE
- } else if (shadeExpanded) {
- View.VISIBLE
- } else {
- View.INVISIBLE
- }
- if (header.visibility != visibility) {
- header.visibility = visibility
- visible = visibility == View.VISIBLE
- }
- }
-
- private fun updateConstraints() {
- if (!combinedHeaders) {
- return
- }
- header as MotionLayout
- if (active) {
- header.setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID)
- } else {
- header.setTransition(HEADER_TRANSITION_ID)
- header.transitionToStart()
- updatePosition()
- updateScrollY()
- }
- }
-
- private fun updatePosition() {
- if (header is MotionLayout && !active && visible) {
- header.setProgress(qsExpandedFraction)
- }
- }
-
- private fun updateListeners() {
- qsCarrierGroupController.setListening(visible)
- if (visible) {
- updateSingleCarrier(qsCarrierGroupController.isSingleCarrier)
- qsCarrierGroupController.setOnSingleCarrierChangedListener { updateSingleCarrier(it) }
- statusBarIconController.addIconGroup(iconManager)
- } else {
- qsCarrierGroupController.setOnSingleCarrierChangedListener(null)
- statusBarIconController.removeIconGroup(iconManager)
- }
- }
-
- private fun updateSingleCarrier(singleCarrier: Boolean) {
- if (singleCarrier) {
- iconContainer.removeIgnoredSlots(carrierIconSlots)
- } else {
- iconContainer.addIgnoredSlots(carrierIconSlots)
- }
- }
-
- override fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println("visible: $visible")
- pw.println("shadeExpanded: $shadeExpanded")
- pw.println("shadeExpandedFraction: $shadeExpandedFraction")
- pw.println("active: $active")
- pw.println("qsExpandedFraction: $qsExpandedFraction")
- pw.println("qsScrollY: $qsScrollY")
- if (combinedHeaders) {
- header as MotionLayout
- pw.println("currentState: ${header.currentState.stateToString()}")
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index c61510cce10e..6e98c49e6d43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -32,6 +32,7 @@ import android.view.animation.AccelerateInterpolator;
import androidx.lifecycle.Observer;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
@@ -144,7 +145,7 @@ public class LightsOutNotifController extends ViewController<View> {
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
@Behavior int behavior, InsetsVisibilities requestedVisibilities,
- String packageName) {
+ String packageName, LetterboxDetails[] letterboxDetails) {
if (displayId != mDisplayId) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 2dc3261eb886..a2140c6ab6b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License
*/
-
package com.android.systemui.statusbar.phone;
import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DELAY;
@@ -31,6 +30,7 @@ import android.util.MathUtils;
import android.util.Property;
import android.view.ContextThemeWrapper;
import android.view.View;
+import android.view.ViewGroup;
import android.view.animation.Interpolator;
import androidx.annotation.VisibleForTesting;
@@ -40,7 +40,6 @@ import com.android.internal.statusbar.StatusBarIcon;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
-import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.stack.AnimationFilter;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -54,7 +53,7 @@ import java.util.function.Consumer;
* A container for notification icons. It handles overflowing icons properly and positions them
* correctly on the screen.
*/
-public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
+public class NotificationIconContainer extends ViewGroup {
/**
* A float value indicating how much before the overflow start the icons should transform into
* a dot. A value of 0 means that they are exactly at the end and a value of 1 means it starts
@@ -232,6 +231,31 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
@Override
+ public boolean hasOverlappingRendering() {
+ // Does the same as "AlphaOptimizedFrameLayout".
+ return false;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int childCount = getChildCount();
+ final int maxVisibleIcons = getMaxVisibleIcons(childCount);
+ final int width = MeasureSpec.getSize(widthMeasureSpec);
+ final int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED);
+ int totalWidth = (int) (getActualPaddingStart() + getActualPaddingEnd());
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ measureChild(child, childWidthSpec, heightMeasureSpec);
+ if (i <= maxVisibleIcons) {
+ totalWidth += child.getMeasuredWidth();
+ }
+ }
+ final int measuredWidth = resolveSize(totalWidth, widthMeasureSpec);
+ final int measuredHeight = MeasureSpec.getSize(heightMeasureSpec);
+ setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
float centerY = getHeight() / 2.0f;
// we layout all our children on the left at the top
@@ -408,8 +432,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
float translationX = getActualPaddingStart();
int firstOverflowIndex = -1;
int childCount = getChildCount();
- int maxVisibleIcons = mOnLockScreen ? MAX_ICONS_ON_AOD :
- mIsStaticLayout ? MAX_STATIC_ICONS : childCount;
+ int maxVisibleIcons = getMaxVisibleIcons(childCount);
float layoutEnd = getLayoutEnd();
mVisualOverflowStart = 0;
mFirstVisibleIconState = null;
@@ -493,6 +516,11 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
}
}
+ private int getMaxVisibleIcons(int childCount) {
+ return mOnLockScreen ? MAX_ICONS_ON_AOD :
+ mIsStaticLayout ? MAX_STATIC_ICONS : childCount;
+ }
+
private float getLayoutEnd() {
return getActualWidth() - getActualPaddingEnd();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index faae4bbbafd0..905a5f943f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -44,6 +44,8 @@ import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;
+import androidx.lifecycle.ViewTreeLifecycleOwner;
+
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -52,6 +54,7 @@ import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.lifecycle.WindowAddedViewLifecycleOwner;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -241,6 +244,16 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
mWindowManager.addView(mNotificationShadeView, mLp);
+
+ // Set up and "inject" a LifecycleOwner bound to the Window-View relationship such that all
+ // views in the sub-tree rooted under this view can access the LifecycleOwner using
+ // ViewTreeLifecycleOwner.get(...).
+ if (ViewTreeLifecycleOwner.get(mNotificationShadeView) == null) {
+ ViewTreeLifecycleOwner.set(
+ mNotificationShadeView,
+ new WindowAddedViewLifecycleOwner(mNotificationShadeView));
+ }
+
mLpChanged.copyFrom(mLp);
onThemeChanged();
@@ -502,7 +515,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
cb.onStateChanged(mCurrentState.mKeyguardShowing,
mCurrentState.mKeyguardOccluded,
mCurrentState.mBouncerShowing,
- mCurrentState.mDozing);
+ mCurrentState.mDozing,
+ mCurrentState.mPanelExpanded);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS
new file mode 100644
index 000000000000..4657e9b84550
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS
@@ -0,0 +1,6 @@
+per-file *Notification* = set noparent
+per-file *Notification* = file:../notification/OWNERS
+
+per-file NotificationIcon* = ccassidy@google.com, evanlaird@google.com, pixel@google.com
+
+per-file NotificationShadeWindowControllerImpl.java = dupin@google.com, cinek@google.com, beverlyt@google.com, pixel@google.com, juliacr@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 5d417e0b59e2..3a85a3ea6391 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -41,6 +41,7 @@ import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
@@ -66,6 +67,7 @@ import com.android.systemui.util.time.SystemClock;
import com.android.wm.shell.animation.FlingAnimationUtils;
import java.io.PrintWriter;
+import java.util.List;
public abstract class PanelViewController {
public static final boolean DEBUG = PanelView.DEBUG;
@@ -430,6 +432,8 @@ public abstract class PanelViewController {
// situations, such keeping your finger down while swiping to unlock to an app
// that is locked in landscape (the rotation will cancel the touch event).
expand = false;
+ } else if (mCentralSurfaces.isBouncerShowingOverDream()) {
+ expand = false;
} else {
// If we get a cancel, put the shade back to the state it was in when the
// gesture started
@@ -482,8 +486,6 @@ public abstract class PanelViewController {
protected abstract boolean shouldGestureWaitForTouchSlop();
- protected abstract boolean shouldGestureIgnoreXTouchSlop(float x, float y);
-
protected void onTrackingStopped(boolean expand) {
mTracking = false;
mCentralSurfaces.onTrackingStopped(expand);
@@ -715,7 +717,7 @@ public abstract class PanelViewController {
animator.start();
}
- void onFlingEnd(boolean cancelled) {
+ protected void onFlingEnd(boolean cancelled) {
mIsFlinging = false;
// No overshoot when the animation ends
setOverExpansionInternal(0, false /* isFromGesture */);
@@ -804,6 +806,7 @@ public abstract class PanelViewController {
mExpansionDragDownAmountPx = h;
mExpandedFraction = Math.min(1f,
maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight);
+ mAmbientState.setExpansionFraction(mExpandedFraction);
onHeightUpdated(mExpandedHeight);
updatePanelExpansionAndVisibility();
});
@@ -1031,16 +1034,19 @@ public abstract class PanelViewController {
animator.start();
setAnimator(animator);
- View[] viewsToAnimate = {
- mKeyguardBottomArea.getIndicationArea(),
- mCentralSurfaces.getAmbientIndicationContainer()};
- for (View v : viewsToAnimate) {
- if (v == null) {
- continue;
- }
- v.animate().translationY(-mHintDistance).setDuration(250).setInterpolator(
- Interpolators.FAST_OUT_SLOW_IN).withEndAction(() -> v.animate().translationY(
- 0).setDuration(450).setInterpolator(mBounceInterpolator).start()).start();
+ final List<ViewPropertyAnimator> indicationAnimators =
+ mKeyguardBottomArea.getIndicationAreaAnimators();
+ for (final ViewPropertyAnimator indicationAreaAnimator : indicationAnimators) {
+ indicationAreaAnimator
+ .translationY(-mHintDistance)
+ .setDuration(250)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .withEndAction(() -> indicationAreaAnimator
+ .translationY(0)
+ .setDuration(450)
+ .setInterpolator(mBounceInterpolator)
+ .start())
+ .start();
}
}
@@ -1107,7 +1113,7 @@ public abstract class PanelViewController {
}
/** Returns true if {@link PanelView} should be visible. */
- abstract boolean shouldPanelBeVisible();
+ abstract protected boolean shouldPanelBeVisible();
/**
* Updates the panel expansion and {@link PanelView} visibility if necessary.
@@ -1158,8 +1164,6 @@ public abstract class PanelViewController {
mTouchDisabled ? "T" : "f"));
}
- public abstract void resetViews(boolean animate);
-
public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
}
@@ -1330,7 +1334,7 @@ public abstract class PanelViewController {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
- mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
+ mIgnoreXTouchSlop = true;
}
switch (event.getActionMasked()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
index 2052ee6cdac5..15c6dcfd7889 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
@@ -31,7 +31,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions {
private final float mIconAlphaWhenOpaque;
- private View mLeftSide, mStatusIcons, mBattery;
+ private View mStartSide, mStatusIcons, mBattery;
private Animator mCurrentAnimation;
/**
@@ -41,7 +41,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions {
super(backgroundView, R.drawable.status_background);
final Resources res = statusBarView.getContext().getResources();
mIconAlphaWhenOpaque = res.getFraction(R.dimen.status_bar_icon_drawing_alpha, 1, 1);
- mLeftSide = statusBarView.findViewById(R.id.status_bar_left_side);
+ mStartSide = statusBarView.findViewById(R.id.status_bar_start_side_except_heads_up);
mStatusIcons = statusBarView.findViewById(R.id.statusIcons);
mBattery = statusBarView.findViewById(R.id.battery);
applyModeBackground(-1, getMode(), false /*animate*/);
@@ -75,7 +75,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions {
}
private void applyMode(int mode, boolean animate) {
- if (mLeftSide == null) return; // pre-init
+ if (mStartSide == null) return; // pre-init
float newAlpha = getNonBatteryClockAlphaFor(mode);
float newAlphaBC = getBatteryClockAlpha(mode);
if (mCurrentAnimation != null) {
@@ -84,7 +84,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions {
if (animate) {
AnimatorSet anims = new AnimatorSet();
anims.playTogether(
- animateTransitionTo(mLeftSide, newAlpha),
+ animateTransitionTo(mStartSide, newAlpha),
animateTransitionTo(mStatusIcons, newAlpha),
animateTransitionTo(mBattery, newAlphaBC)
);
@@ -94,7 +94,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions {
anims.start();
mCurrentAnimation = anims;
} else {
- mLeftSide.setAlpha(newAlpha);
+ mStartSide.setAlpha(newAlpha);
mStatusIcons.setAlpha(newAlpha);
mBattery.setAlpha(newAlphaBC);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 9da2ef734be8..f9c4c8f3b4fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -21,7 +21,6 @@ import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
-
import com.android.systemui.R
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController
@@ -32,9 +31,7 @@ import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.util.ViewController
import com.android.systemui.util.kotlin.getOrNull
import com.android.systemui.util.view.ViewUtil
-
import java.util.Optional
-
import javax.inject.Inject
import javax.inject.Named
@@ -58,8 +55,8 @@ class PhoneStatusBarViewController private constructor(
override fun onViewAttached() {
if (moveFromCenterAnimationController == null) return
- val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side)
- val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area)
+ val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_start_side_except_heads_up)
+ val systemIconArea: ViewGroup = mView.findViewById(R.id.status_bar_end_side_content)
val viewsToAnimate = arrayOf(
statusBarLeftSide,
@@ -126,11 +123,11 @@ class PhoneStatusBarViewController private constructor(
class StatusBarViewsCenterProvider : UnfoldMoveFromCenterAnimator.ViewCenterProvider {
override fun getViewCenter(view: View, outPoint: Point) =
when (view.id) {
- R.id.status_bar_left_side -> {
+ R.id.status_bar_start_side_except_heads_up -> {
// items aligned to the start, return start center point
getViewEdgeCenter(view, outPoint, isStart = true)
}
- R.id.system_icon_area -> {
+ R.id.status_bar_end_side_content -> {
// items aligned to the end, return end center point
getViewEdgeCenter(view, outPoint, isStart = false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 5e7dc115d7f9..cb0a1480c233 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -54,8 +54,8 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.scrim.ScrimView;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.notification.stack.ViewState;
-import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.AlarmTimeout;
@@ -265,7 +265,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager,
ConfigurationController configurationController, @Main Executor mainExecutor,
ScreenOffAnimationController screenOffAnimationController,
- PanelExpansionStateManager panelExpansionStateManager,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
mScrimStateListener = lightBarController::setScrimState;
@@ -305,10 +304,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
ScrimController.this.onThemeChanged();
}
});
- panelExpansionStateManager.addExpansionListener(
- event -> setRawPanelExpansionFraction(event.getFraction())
- );
-
mColors = new GradientColors();
}
@@ -553,13 +548,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
*
* The expansion fraction is tied to the scrim opacity.
*
- * See {@link PanelExpansionListener#onPanelExpansionChanged}.
+ * See {@link ScrimShadeTransitionController#onPanelExpansionChanged}.
*
* @param rawPanelExpansionFraction From 0 to 1 where 0 means collapsed and 1 expanded.
*/
- @VisibleForTesting
- void setRawPanelExpansionFraction(
- @FloatRange(from = 0.0, to = 1.0) float rawPanelExpansionFraction) {
+ public void setRawPanelExpansionFraction(
+ @FloatRange(from = 0.0, to = 1.0) float rawPanelExpansionFraction) {
if (isNaN(rawPanelExpansionFraction)) {
throw new IllegalArgumentException("rawPanelExpansionFraction should not be NaN");
}
@@ -790,7 +784,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mInFrontAlpha = 0;
}
- if (mBouncerHiddenFraction != KeyguardBouncer.EXPANSION_HIDDEN) {
+ if (mState == ScrimState.DREAMING
+ && mBouncerHiddenFraction != KeyguardBouncer.EXPANSION_HIDDEN) {
final float interpolatedFraction =
BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(
mBouncerHiddenFraction);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index cee8b335f380..d37ecbc42168 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -23,6 +23,8 @@ import android.view.WindowManager;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationShadeWindowController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt
new file mode 100644
index 000000000000..f5ba399fe51a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.graphics.Rect
+import android.view.View
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.END_SIDE_CONTENT
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.START_SIDE_CONTENT
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope
+import com.android.systemui.util.boundsOnScreen
+import javax.inject.Inject
+import javax.inject.Named
+
+/** Provides various bounds within the status bar. */
+@StatusBarFragmentScope
+class StatusBarBoundsProvider
+@Inject
+constructor(
+ private val changeListeners: Set<@JvmSuppressWildcards BoundsChangeListener>,
+ @Named(START_SIDE_CONTENT) private val startSideContent: View,
+ @Named(END_SIDE_CONTENT) private val endSideContent: View,
+) : StatusBarFragmentComponent.Startable {
+
+ interface BoundsChangeListener {
+ fun onStatusBarBoundsChanged()
+ }
+
+ private var previousBounds =
+ BoundsPair(start = startSideContent.boundsOnScreen, end = endSideContent.boundsOnScreen)
+
+ private val layoutListener =
+ View.OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
+ val newBounds = BoundsPair(start = visibleStartSideBounds, end = visibleEndSideBounds)
+ if (previousBounds != newBounds) {
+ previousBounds = newBounds
+ changeListeners.forEach { it.onStatusBarBoundsChanged() }
+ }
+ }
+
+ override fun start() {
+ startSideContent.addOnLayoutChangeListener(layoutListener)
+ endSideContent.addOnLayoutChangeListener(layoutListener)
+ }
+
+ override fun stop() {
+ startSideContent.removeOnLayoutChangeListener(layoutListener)
+ endSideContent.removeOnLayoutChangeListener(layoutListener)
+ }
+
+ /**
+ * Returns the bounds of the end side of the status bar that are visible to the user. The end
+ * side is right when in LTR and is left when in RTL.
+ *
+ * Note that even though the layout might be larger, here we only return the bounds for what is
+ * visible to the user.
+ *
+ * The end side of the status bar contains the multi-user switcher and status icons such as
+ * wi-fi, battery, etc
+ */
+ val visibleEndSideBounds: Rect
+ get() = endSideContent.boundsOnScreen
+
+ /**
+ * Returns the bounds of the start side of the status bar that are visible to the user. The
+ * start side is left when in LTR and is right when in RTL.
+ *
+ * Note that even though the layout might be larger, here we only return the bounds for what is
+ * visible to the user.
+ *
+ * The start side of the status bar contains the operator name, clock, on-going call chip, and
+ * notifications.
+ */
+ val visibleStartSideBounds: Rect
+ get() = startSideContent.boundsOnScreen
+}
+
+private data class BoundsPair(val start: Rect, val end: Rect)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index f5462bc0fba5..c850d4f9c56b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -123,7 +123,7 @@ class StatusBarContentInsetsProvider @Inject constructor(
val point = Point()
context.display.getRealSize(point)
- return topBounds.left <= 0 || topBounds.right >= point.y
+ return topBounds.left <= 0 || topBounds.right >= point.x
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index 50f21691b044..ebfbf54abe1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index a94c2b73d1f6..7c31366ba4f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.phone.StatusBarIconList.Slot;
+
import android.annotation.NonNull;
import android.content.Context;
import android.graphics.drawable.Icon;
@@ -56,11 +58,12 @@ import javax.inject.Inject;
* registered with it.
*/
@SysUISingleton
-public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,
+public class StatusBarIconControllerImpl implements Tunable,
ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController, DemoMode {
private static final String TAG = "StatusBarIconController";
+ private final StatusBarIconList mStatusBarIconList;
private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
private final ArraySet<String> mIconHideList = new ArraySet<>();
@@ -74,15 +77,12 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
DemoModeController demoModeController,
ConfigurationController configurationController,
TunerService tunerService,
- DumpManager dumpManager) {
- super(context.getResources().getStringArray(
- com.android.internal.R.array.config_statusBarIcons));
- configurationController.addCallback(this);
-
+ DumpManager dumpManager,
+ StatusBarIconList statusBarIconList) {
+ mStatusBarIconList = statusBarIconList;
mContext = context;
- loadDimens();
-
+ configurationController.addCallback(this);
commandQueue.addCallback(this);
tunerService.addTunable(this, ICON_HIDE_LIST);
demoModeController.addCallback(this);
@@ -101,15 +101,14 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
group.setController(this);
mIconGroups.add(group);
- List<Slot> allSlots = getSlots();
+ List<Slot> allSlots = mStatusBarIconList.getSlots();
for (int i = 0; i < allSlots.size(); i++) {
Slot slot = allSlots.get(i);
List<StatusBarIconHolder> holders = slot.getHolderListInViewOrder();
boolean hidden = mIconHideList.contains(slot.getName());
for (StatusBarIconHolder holder : holders) {
- int tag = holder.getTag();
- int viewIndex = getViewIndex(getSlotIndex(slot.getName()), holder.getTag());
+ int viewIndex = mStatusBarIconList.getViewIndex(slot.getName(), holder.getTag());
group.onIconAdded(viewIndex, slot.getName(), hidden, holder);
}
}
@@ -144,7 +143,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
}
mIconHideList.clear();
mIconHideList.addAll(StatusBarIconController.getIconHideList(mContext, newValue));
- ArrayList<Slot> currentSlots = getSlots();
+ List<Slot> currentSlots = mStatusBarIconList.getSlots();
ArrayMap<Slot, List<StatusBarIconHolder>> slotsToReAdd = new ArrayMap<>();
// This is a little hacky... Peel off all of the holders on all of the slots
@@ -163,17 +162,13 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
List<StatusBarIconHolder> iconsForSlot = slotsToReAdd.get(item);
if (iconsForSlot == null) continue;
for (StatusBarIconHolder holder : iconsForSlot) {
- setIcon(getSlotIndex(item.getName()), holder);
+ setIcon(item.getName(), holder);
}
}
}
- private void loadDimens() {
- }
-
- private void addSystemIcon(int index, StatusBarIconHolder holder) {
- String slot = getSlotName(index);
- int viewIndex = getViewIndex(index, holder.getTag());
+ private void addSystemIcon(String slot, StatusBarIconHolder holder) {
+ int viewIndex = mStatusBarIconList.getViewIndex(slot, holder.getTag());
boolean hidden = mIconHideList.contains(slot);
mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, hidden, holder));
@@ -182,18 +177,17 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
/** */
@Override
public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
- int index = getSlotIndex(slot);
- StatusBarIconHolder holder = getIcon(index, 0);
+ StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, 0);
if (holder == null) {
StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
Icon.createWithResource(
mContext, resourceId), 0, 0, contentDescription);
holder = StatusBarIconHolder.fromIcon(icon);
- setIcon(index, holder);
+ setIcon(slot, holder);
} else {
holder.getIcon().icon = Icon.createWithResource(mContext, resourceId);
holder.getIcon().contentDescription = contentDescription;
- handleSet(index, holder);
+ handleSet(slot, holder);
}
}
@@ -203,21 +197,18 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
*/
@Override
public void setSignalIcon(String slot, WifiIconState state) {
-
- int index = getSlotIndex(slot);
-
if (state == null) {
- removeIcon(index, 0);
+ removeIcon(slot, 0);
return;
}
- StatusBarIconHolder holder = getIcon(index, 0);
+ StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, 0);
if (holder == null) {
holder = StatusBarIconHolder.fromWifiIconState(state);
- setIcon(index, holder);
+ setIcon(slot, holder);
} else {
holder.setWifiState(state);
- handleSet(index, holder);
+ handleSet(slot, holder);
}
}
@@ -229,8 +220,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
*/
@Override
public void setMobileIcons(String slot, List<MobileIconState> iconStates) {
- Slot mobileSlot = getSlot(slot);
- int slotIndex = getSlotIndex(slot);
+ Slot mobileSlot = mStatusBarIconList.getSlot(slot);
// Reverse the sort order to show icons with left to right([Slot1][Slot2]..).
// StatusBarIconList has UI design that first items go to the right of second items.
@@ -241,10 +231,10 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
StatusBarIconHolder holder = mobileSlot.getHolderForTag(state.subId);
if (holder == null) {
holder = StatusBarIconHolder.fromMobileIconState(state);
- setIcon(slotIndex, holder);
+ setIcon(slot, holder);
} else {
holder.setMobileState(state);
- handleSet(slotIndex, holder);
+ handleSet(slot, holder);
}
}
}
@@ -256,21 +246,19 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
*/
@Override
public void setCallStrengthIcons(String slot, List<CallIndicatorIconState> states) {
- Slot callStrengthSlot = getSlot(slot);
- int callStrengthSlotIndex = getSlotIndex(slot);
+ Slot callStrengthSlot = mStatusBarIconList.getSlot(slot);
Collections.reverse(states);
for (CallIndicatorIconState state : states) {
if (!state.isNoCalling) {
StatusBarIconHolder holder = callStrengthSlot.getHolderForTag(state.subId);
if (holder == null) {
holder = StatusBarIconHolder.fromCallIndicatorState(mContext, state);
- setIcon(callStrengthSlotIndex, holder);
} else {
holder.setIcon(new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
Icon.createWithResource(mContext, state.callStrengthResId), 0, 0,
state.callStrengthDescription));
- setIcon(callStrengthSlotIndex, holder);
}
+ setIcon(slot, holder);
}
setIconVisibility(slot, !state.isNoCalling, state.subId);
}
@@ -283,21 +271,19 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
*/
@Override
public void setNoCallingIcons(String slot, List<CallIndicatorIconState> states) {
- Slot noCallingSlot = getSlot(slot);
- int noCallingSlotIndex = getSlotIndex(slot);
+ Slot noCallingSlot = mStatusBarIconList.getSlot(slot);
Collections.reverse(states);
for (CallIndicatorIconState state : states) {
if (state.isNoCalling) {
StatusBarIconHolder holder = noCallingSlot.getHolderForTag(state.subId);
if (holder == null) {
holder = StatusBarIconHolder.fromCallIndicatorState(mContext, state);
- setIcon(noCallingSlotIndex, holder);
} else {
holder.setIcon(new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
Icon.createWithResource(mContext, state.noCallingResId), 0, 0,
state.noCallingDescription));
- setIcon(noCallingSlotIndex, holder);
}
+ setIcon(slot, holder);
}
setIconVisibility(slot, state.isNoCalling, state.subId);
}
@@ -305,42 +291,31 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
@Override
public void setExternalIcon(String slot) {
- int viewIndex = getViewIndex(getSlotIndex(slot), 0);
+ int viewIndex = mStatusBarIconList.getViewIndex(slot, 0);
int height = mContext.getResources().getDimensionPixelSize(
R.dimen.status_bar_icon_drawing_size);
mIconGroups.forEach(l -> l.onIconExternal(viewIndex, height));
}
//TODO: remove this (used in command queue and for 3rd party tiles?)
- @Override
public void setIcon(String slot, StatusBarIcon icon) {
- setIcon(getSlotIndex(slot), icon);
- }
-
- /**
- * For backwards compatibility, in the event that someone gives us a slot and a status bar icon
- */
- private void setIcon(int index, StatusBarIcon icon) {
- String slot = getSlotName(index);
if (icon == null) {
removeAllIconsForSlot(slot);
return;
}
StatusBarIconHolder holder = StatusBarIconHolder.fromIcon(icon);
- setIcon(index, holder);
+ setIcon(slot, holder);
}
- /** */
- @Override
- public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
- boolean isNew = getIcon(index, holder.getTag()) == null;
- super.setIcon(index, holder);
+ private void setIcon(String slot, @NonNull StatusBarIconHolder holder) {
+ boolean isNew = mStatusBarIconList.getIconHolder(slot, holder.getTag()) == null;
+ mStatusBarIconList.setIcon(slot, holder);
if (isNew) {
- addSystemIcon(index, holder);
+ addSystemIcon(slot, holder);
} else {
- handleSet(index, holder);
+ handleSet(slot, holder);
}
}
@@ -351,34 +326,33 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
/** */
public void setIconVisibility(String slot, boolean visibility, int tag) {
- int index = getSlotIndex(slot);
- StatusBarIconHolder holder = getIcon(index, tag);
+ StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, tag);
if (holder == null || holder.isVisible() == visibility) {
return;
}
holder.setVisible(visibility);
- handleSet(index, holder);
+ handleSet(slot, holder);
}
/** */
@Override
public void setIconAccessibilityLiveRegion(String slotName, int accessibilityLiveRegion) {
- Slot slot = getSlot(slotName);
+ Slot slot = mStatusBarIconList.getSlot(slotName);
if (!slot.hasIconsInSlot()) {
return;
}
- int slotIndex = getSlotIndex(slotName);
List<StatusBarIconHolder> iconsToUpdate = slot.getHolderListInViewOrder();
for (StatusBarIconHolder holder : iconsToUpdate) {
- int viewIndex = getViewIndex(slotIndex, holder.getTag());
+ int viewIndex = mStatusBarIconList.getViewIndex(slotName, holder.getTag());
mIconGroups.forEach(l -> l.mGroup.getChildAt(viewIndex)
.setAccessibilityLiveRegion(accessibilityLiveRegion));
}
}
/** */
+ @Override
public void removeIcon(String slot) {
removeAllIconsForSlot(slot);
}
@@ -386,39 +360,34 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
/** */
@Override
public void removeIcon(String slot, int tag) {
- removeIcon(getSlotIndex(slot), tag);
+ if (mStatusBarIconList.getIconHolder(slot, tag) == null) {
+ return;
+ }
+ int viewIndex = mStatusBarIconList.getViewIndex(slot, tag);
+ mStatusBarIconList.removeIcon(slot, tag);
+ mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex));
}
/** */
@Override
public void removeAllIconsForSlot(String slotName) {
- Slot slot = getSlot(slotName);
+ Slot slot = mStatusBarIconList.getSlot(slotName);
if (!slot.hasIconsInSlot()) {
return;
}
- int slotIndex = getSlotIndex(slotName);
List<StatusBarIconHolder> iconsToRemove = slot.getHolderListInViewOrder();
for (StatusBarIconHolder holder : iconsToRemove) {
- int viewIndex = getViewIndex(slotIndex, holder.getTag());
+ int viewIndex = mStatusBarIconList.getViewIndex(slotName, holder.getTag());
slot.removeForTag(holder.getTag());
mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex));
}
}
- /** */
- @Override
- public void removeIcon(int index, int tag) {
- if (getIcon(index, tag) == null) {
- return;
- }
- super.removeIcon(index, tag);
- int viewIndex = getViewIndex(index, 0);
- mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex));
- }
- private void handleSet(int index, StatusBarIconHolder holder) {
- int viewIndex = getViewIndex(index, holder.getTag());
+
+ private void handleSet(String slotName, StatusBarIconHolder holder) {
+ int viewIndex = mStatusBarIconList.getViewIndex(slotName, holder.getTag());
mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
}
@@ -438,7 +407,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
}
}
- super.dump(pw);
+ mStatusBarIconList.dump(pw);
}
/** */
@@ -482,7 +451,6 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
/** */
@Override
public void onDensityOrFontScaleChanged() {
- loadDimens();
refreshIconGroups();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
index c876c3228eb8..8800b05fadb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
@@ -25,60 +25,72 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+/** A class holding the list of all the system icons that could be shown in the status bar. */
public class StatusBarIconList {
- private ArrayList<Slot> mSlots = new ArrayList<>();
+ private final ArrayList<Slot> mSlots = new ArrayList<>();
+ private final List<Slot> mViewOnlySlots = Collections.unmodifiableList(mSlots);
public StatusBarIconList(String[] slots) {
final int N = slots.length;
- for (int i=0; i < N; i++) {
+ for (int i = 0; i < N; i++) {
mSlots.add(new Slot(slots[i], null));
}
}
- public int getSlotIndex(String slot) {
- final int N = mSlots.size();
- for (int i=0; i<N; i++) {
- Slot item = mSlots.get(i);
- if (item.getName().equals(slot)) {
- return i;
- }
- }
- // Auto insert new items at the beginning.
- mSlots.add(0, new Slot(slot, null));
- return 0;
- }
-
- protected ArrayList<Slot> getSlots() {
- return new ArrayList<>(mSlots);
- }
-
- protected Slot getSlot(String name) {
- return mSlots.get(getSlotIndex(name));
+ /** Returns the list of current slots. */
+ public List<Slot> getSlots() {
+ return mViewOnlySlots;
}
- public int size() {
- return mSlots.size();
+ /**
+ * Gets the slot with the given {@code name}, or creates a new slot if we don't already have a
+ * slot by that name.
+ *
+ * If a new slot is created, that slot will be inserted at the front of the list.
+ *
+ * TODO(b/237533036): Rename this to getOrCreateSlot to make it more clear that it could create
+ * a new slot. Other methods in this class will also create a new slot if we don't have one,
+ * should those be re-named too?
+ */
+ public Slot getSlot(String name) {
+ return mSlots.get(findOrInsertSlot(name));
}
- public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
- mSlots.get(index).addHolder(holder);
+ /**
+ * Sets the icon in {@code holder} to be associated with the slot with the given
+ * {@code slotName}.
+ */
+ public void setIcon(String slotName, @NonNull StatusBarIconHolder holder) {
+ mSlots.get(findOrInsertSlot(slotName)).addHolder(holder);
}
- public void removeIcon(int index, int tag) {
- mSlots.get(index).removeForTag(tag);
+ /**
+ * Removes the icon holder that we had associated with {@code slotName}'s slot at the given
+ * {@code tag}.
+ */
+ public void removeIcon(String slotName, int tag) {
+ mSlots.get(findOrInsertSlot(slotName)).removeForTag(tag);
}
- public String getSlotName(int index) {
- return mSlots.get(index).getName();
+ /**
+ * Returns the icon holder currently associated with {@code slotName}'s slot at the given
+ * {@code tag}, or null if we don't have one.
+ */
+ @Nullable
+ public StatusBarIconHolder getIconHolder(String slotName, int tag) {
+ return mSlots.get(findOrInsertSlot(slotName)).getHolderForTag(tag);
}
- public StatusBarIconHolder getIcon(int index, int tag) {
- return mSlots.get(index).getHolderForTag(tag);
- }
-
- public int getViewIndex(int slotIndex, int tag) {
+ /**
+ * Returns the index of the icon in {@code slotName}'s slot at the given {@code tag}.
+ *
+ * Note that a single slot can have multiple icons, and this function takes that into account.
+ */
+ public int getViewIndex(String slotName, int tag) {
+ int slotIndex = findOrInsertSlot(slotName);
int count = 0;
for (int i = 0; i < slotIndex; i++) {
Slot item = mSlots.get(i);
@@ -100,6 +112,25 @@ public class StatusBarIconList {
}
}
+ private int findOrInsertSlot(String slot) {
+ final int N = mSlots.size();
+ for (int i = 0; i < N; i++) {
+ Slot item = mSlots.get(i);
+ if (item.getName().equals(slot)) {
+ return i;
+ }
+ }
+ // Auto insert new items at the beginning.
+ mSlots.add(0, new Slot(slot, null));
+ return 0;
+ }
+
+
+ /**
+ * A class representing one slot in the status bar system icons view.
+ *
+ * Note that one slot can have multiple icons associated with it.
+ */
public static class Slot {
private final String mName;
private StatusBarIconHolder mHolder;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 63c5e61ac4e5..f128a4124c94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -55,6 +55,7 @@ import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -129,8 +130,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void onFullyShown() {
mBouncerAnimating = false;
updateStates();
- mCentralSurfaces.wakeUpIfDozing(SystemClock.uptimeMillis(),
- mCentralSurfaces.getBouncerContainer(), "BOUNCER_VISIBLE");
}
@Override
@@ -163,6 +162,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void onVisibilityChanged(boolean isVisible) {
+ mCentralSurfaces
+ .setBouncerShowingOverDream(
+ isVisible && mDreamOverlayStateController.isOverlayActive());
+
if (!isVisible) {
mCentralSurfaces.setBouncerHiddenFraction(KeyguardBouncer.EXPANSION_HIDDEN);
}
@@ -1039,7 +1042,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
if (occluded != mLastOccluded || mFirstUpdate) {
- mKeyguardUpdateManager.onKeyguardOccludedChanged(occluded);
mKeyguardStateController.notifyKeyguardState(showing, occluded);
}
if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) {
@@ -1068,11 +1070,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
}
- private View getCurrentNavBarView() {
- final NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView();
- return navBarView != null ? navBarView.getCurrentView() : null;
- }
-
/**
* Updates the visibility of the nav bar window (which will cause insets changes).
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index cf776e3b60d1..451612ad4bb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -52,21 +52,16 @@ import com.android.systemui.EventLogTags;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.NotificationClickNotifier;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -92,24 +87,19 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
private final Context mContext;
- private final CommandQueue mCommandQueue;
private final Handler mMainThreadHandler;
private final Executor mUiBgExecutor;
- private final NotificationEntryManager mEntryManager;
- private final NotifPipeline mNotifPipeline;
private final NotificationVisibilityProvider mVisibilityProvider;
private final HeadsUpManagerPhone mHeadsUpManager;
private final ActivityStarter mActivityStarter;
private final NotificationClickNotifier mClickNotifier;
- private final StatusBarStateController mStatusBarStateController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardManager mKeyguardManager;
private final IDreamManager mDreamManager;
private final Optional<BubblesManager> mBubblesManagerOptional;
private final Lazy<AssistManager> mAssistManagerLazy;
private final NotificationRemoteInputManager mRemoteInputManager;
- private final GroupMembershipManager mGroupMembershipManager;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final ShadeController mShadeController;
private final KeyguardStateController mKeyguardStateController;
@@ -118,7 +108,6 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback;
private final ActivityIntentHelper mActivityIntentHelper;
- private final NotifPipelineFlags mNotifPipelineFlags;
private final MetricsLogger mMetricsLogger;
private final StatusBarNotificationActivityStarterLogger mLogger;
@@ -134,23 +123,19 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
@Inject
StatusBarNotificationActivityStarter(
Context context,
- CommandQueue commandQueue,
Handler mainThreadHandler,
Executor uiBgExecutor,
- NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
NotificationVisibilityProvider visibilityProvider,
HeadsUpManagerPhone headsUpManager,
ActivityStarter activityStarter,
NotificationClickNotifier clickNotifier,
- StatusBarStateController statusBarStateController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
KeyguardManager keyguardManager,
IDreamManager dreamManager,
Optional<BubblesManager> bubblesManagerOptional,
Lazy<AssistManager> assistManagerLazy,
NotificationRemoteInputManager remoteInputManager,
- GroupMembershipManager groupMembershipManager,
NotificationLockscreenUserManager lockscreenUserManager,
ShadeController shadeController,
KeyguardStateController keyguardStateController,
@@ -158,7 +143,6 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
LockPatternUtils lockPatternUtils,
StatusBarRemoteInputCallback remoteInputCallback,
ActivityIntentHelper activityIntentHelper,
- NotifPipelineFlags notifPipelineFlags,
MetricsLogger metricsLogger,
StatusBarNotificationActivityStarterLogger logger,
OnUserInteractionCallback onUserInteractionCallback,
@@ -168,23 +152,18 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
ActivityLaunchAnimator activityLaunchAnimator,
NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) {
mContext = context;
- mCommandQueue = commandQueue;
mMainThreadHandler = mainThreadHandler;
mUiBgExecutor = uiBgExecutor;
- mEntryManager = entryManager;
- mNotifPipeline = notifPipeline;
mVisibilityProvider = visibilityProvider;
mHeadsUpManager = headsUpManager;
mActivityStarter = activityStarter;
mClickNotifier = clickNotifier;
- mStatusBarStateController = statusBarStateController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardManager = keyguardManager;
mDreamManager = dreamManager;
mBubblesManagerOptional = bubblesManagerOptional;
mAssistManagerLazy = assistManagerLazy;
mRemoteInputManager = remoteInputManager;
- mGroupMembershipManager = groupMembershipManager;
mLockscreenUserManager = lockscreenUserManager;
mShadeController = shadeController;
mKeyguardStateController = keyguardStateController;
@@ -192,7 +171,6 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
mLockPatternUtils = lockPatternUtils;
mStatusBarRemoteInputCallback = remoteInputCallback;
mActivityIntentHelper = activityIntentHelper;
- mNotifPipelineFlags = notifPipelineFlags;
mMetricsLogger = metricsLogger;
mLogger = logger;
mOnUserInteractionCallback = onUserInteractionCallback;
@@ -203,21 +181,12 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte
mActivityLaunchAnimator = activityLaunchAnimator;
mNotificationAnimationProvider = notificationAnimationProvider;
- if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
- mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
- @Override
- public void onPendingEntryAdded(NotificationEntry entry) {
- handleFullScreenIntent(entry);
- }
- });
- } else {
- mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
- @Override
- public void onEntryAdded(NotificationEntry entry) {
- handleFullScreenIntent(entry);
- }
- });
- }
+ notifPipeline.addCollectionListener(new NotifCollectionListener() {
+ @Override
+ public void onEntryAdded(NotificationEntry entry) {
+ handleFullScreenIntent(entry);
+ }
+ });
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index aa061d74f6c6..4c239314f70f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -30,19 +30,17 @@ import android.util.Log;
import android.util.Slog;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
-import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.widget.MessagingGroup;
-import com.android.internal.widget.MessagingMessage;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceNotificationListener;
import com.android.systemui.InitController;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -57,7 +55,6 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
@@ -72,16 +69,12 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import java.util.List;
-
import javax.inject.Inject;
@CentralSurfacesComponent.CentralSurfacesScope
class StatusBarNotificationPresenter implements NotificationPresenter,
- ConfigurationController.ConfigurationListener,
NotificationRowBinderImpl.BindRowCallback,
CommandQueue.Callbacks {
private static final String TAG = "StatusBarNotificationPresenter";
@@ -92,10 +85,8 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final SysuiStatusBarStateController mStatusBarStateController;
private final NotifShadeEventSource mNotifShadeEventSource;
- private final NotificationEntryManager mEntryManager;
private final NotificationMediaManager mMediaManager;
private final NotificationGutsManager mGutsManager;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final LockscreenGestureLogger mLockscreenGestureLogger;
private final NotificationPanelViewController mNotificationPanel;
@@ -116,9 +107,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
private final IStatusBarService mBarService;
private final DynamicPrivacyController mDynamicPrivacyController;
private final NotificationListContainer mNotifListContainer;
- private boolean mReinflateNotificationsOnUserSwitched;
- private boolean mDispatchUiModeChangeOnUserSwitched;
- private TextView mNotificationPanelDebugText;
protected boolean mVrMode;
@@ -143,15 +131,12 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
NotificationLockscreenUserManager lockscreenUserManager,
SysuiStatusBarStateController sysuiStatusBarStateController,
NotifShadeEventSource notifShadeEventSource,
- NotificationEntryManager notificationEntryManager,
NotificationMediaManager notificationMediaManager,
NotificationGutsManager notificationGutsManager,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
LockscreenGestureLogger lockscreenGestureLogger,
InitController initController,
NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationRemoteInputManager remoteInputManager,
- ConfigurationController configurationController,
NotifPipelineFlags notifPipelineFlags,
NotificationRemoteInputManager.Callback remoteInputManagerCallback,
NotificationListContainer notificationListContainer) {
@@ -170,10 +155,8 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
mLockscreenUserManager = lockscreenUserManager;
mStatusBarStateController = sysuiStatusBarStateController;
mNotifShadeEventSource = notifShadeEventSource;
- mEntryManager = notificationEntryManager;
mMediaManager = notificationMediaManager;
mGutsManager = notificationGutsManager;
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockscreenGestureLogger = lockscreenGestureLogger;
mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView());
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -208,13 +191,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
mNotifListContainer);
mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied);
mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse);
- if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
- mEntryManager.setUpWithPresenter(this);
- mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager);
- mEntryManager.addNotificationLifetimeExtender(mGutsManager);
- mEntryManager.addNotificationLifetimeExtenders(
- remoteInputManager.getLifetimeExtenders());
- }
notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
mLockscreenUserManager.setUpWithPresenter(this);
mMediaManager.setUpWithPresenter(this);
@@ -227,7 +203,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
onUserSwitched(mLockscreenUserManager.getCurrentUserId());
});
- configurationController.addCallback(this);
}
/** Called when the shade has been emptied to attempt to close the shade */
@@ -242,65 +217,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
}
@Override
- public void onDensityOrFontScaleChanged() {
- // TODO(b/145659174): Remove legacy pipeline code
- if (mNotifPipelineFlags.isNewPipelineEnabled()) return;
- MessagingMessage.dropCache();
- MessagingGroup.dropCache();
- if (!mKeyguardUpdateMonitor.isSwitchingUser()) {
- updateNotificationsOnDensityOrFontScaleChanged();
- } else {
- mReinflateNotificationsOnUserSwitched = true;
- }
- }
-
- @Override
- public void onUiModeChanged() {
- // TODO(b/145659174): Remove legacy pipeline code
- if (mNotifPipelineFlags.isNewPipelineEnabled()) return;
- if (!mKeyguardUpdateMonitor.isSwitchingUser()) {
- updateNotificationsOnUiModeChanged();
- } else {
- mDispatchUiModeChangeOnUserSwitched = true;
- }
- }
-
- @Override
- public void onThemeChanged() {
- onDensityOrFontScaleChanged();
- }
-
- private void updateNotificationsOnUiModeChanged() {
- // TODO(b/145659174): Remove legacy pipeline code
- if (mNotifPipelineFlags.isNewPipelineEnabled()) return;
- List<NotificationEntry> userNotifications =
- mEntryManager.getActiveNotificationsForCurrentUser();
- for (int i = 0; i < userNotifications.size(); i++) {
- NotificationEntry entry = userNotifications.get(i);
- ExpandableNotificationRow row = entry.getRow();
- if (row != null) {
- row.onUiModeChanged();
- }
- }
- }
-
- private void updateNotificationsOnDensityOrFontScaleChanged() {
- // TODO(b/145659174): Remove legacy pipeline code
- if (mNotifPipelineFlags.isNewPipelineEnabled()) return;
- List<NotificationEntry> userNotifications =
- mEntryManager.getActiveNotificationsForCurrentUser();
- for (int i = 0; i < userNotifications.size(); i++) {
- NotificationEntry entry = userNotifications.get(i);
- entry.onDensityOrFontScaleChanged();
- boolean exposedGuts = entry.areGutsExposed();
- if (exposedGuts) {
- mGutsManager.onDensityOrFontScaleChanged(entry);
- }
- }
- }
-
-
- @Override
public boolean isCollapsing() {
return mNotificationPanel.isCollapsing()
|| mNotificationShadeWindowController.isLaunchingActivity();
@@ -340,17 +256,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
// End old BaseStatusBar.userSwitched
if (MULTIUSER_DEBUG) mNotificationPanel.setHeaderDebugInfo("USER " + newUserId);
mCommandQueue.animateCollapsePanels();
- if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
- if (mReinflateNotificationsOnUserSwitched) {
- updateNotificationsOnDensityOrFontScaleChanged();
- mReinflateNotificationsOnUserSwitched = false;
- }
- if (mDispatchUiModeChangeOnUserSwitched) {
- updateNotificationsOnUiModeChanged();
- mDispatchUiModeChangeOnUserSwitched = false;
- }
- updateNotificationViews("user switched");
- }
mMediaManager.clearCurrentMediaNotification();
mCentralSurfaces.setLockscreenUser(newUserId);
updateMediaMetaData(true, false);
@@ -494,7 +399,8 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
return true;
}
- if (sbn.getNotification().fullScreenIntent != null) {
+ if (sbn.getNotification().fullScreenIntent != null
+ && !mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()) {
// we don't allow head-up on the lockscreen (unless there's a
// "showWhenLocked" activity currently showing) if
// the potential HUN has a fullscreen intent
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index 4e9090080c99..75dac1a093dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -136,8 +136,9 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
* Notify that the status bar panel gets expanded or collapsed.
*
* @param isExpanded True to notify expanded, false to notify collapsed.
+ * TODO(b/237811427) replace with a listener
*/
- void setPanelExpanded(boolean isExpanded) {
+ public void setPanelExpanded(boolean isExpanded) {
if (isExpanded != mIsStatusBarExpanded) {
mIsStatusBarExpanded = isExpanded;
if (isExpanded) {
@@ -153,7 +154,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
* any existing display cutouts (notch)
* @return the heads up notification touch area
*/
- Region calculateTouchableRegion() {
+ public Region calculateTouchableRegion() {
// Update touchable region for HeadsUp notifications
final Region headsUpTouchableRegion = mHeadsUpManager.getTouchableRegion();
if (headsUpTouchableRegion != null) {
@@ -222,7 +223,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
}
}
- void updateRegionForNotch(Region touchableRegion) {
+ public void updateRegionForNotch(Region touchableRegion) {
WindowInsets windowInsets = mNotificationShadeWindowView.getRootWindowInsets();
if (windowInsets == null) {
Log.w(TAG, "StatusBarWindowView is not attached.");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
index ac43b679da0f..ae48c2d3b6f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
@@ -17,5 +17,5 @@ package com.android.systemui.statusbar.phone;
public interface StatusBarWindowCallback {
void onStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing,
- boolean isDozing);
+ boolean isDozing, boolean panelExpanded);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 36a045637a87..26bc3e3c0920 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -93,6 +93,17 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
}
public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock) {
+ // TODO(b/219008720): Remove those calls to Dependency.get by introducing a
+ // SystemUIDialogFactory and make all other dialogs create a SystemUIDialog to which we set
+ // the content and attach listeners.
+ this(context, theme, dismissOnDeviceLock, Dependency.get(SystemUIDialogManager.class),
+ Dependency.get(SysUiState.class), Dependency.get(BroadcastDispatcher.class),
+ Dependency.get(DialogLaunchAnimator.class));
+ }
+
+ public SystemUIDialog(Context context, int theme, boolean dismissOnDeviceLock,
+ SystemUIDialogManager dialogManager, SysUiState sysUiState,
+ BroadcastDispatcher broadcastDispatcher, DialogLaunchAnimator dialogLaunchAnimator) {
super(context, theme);
mContext = context;
@@ -101,13 +112,10 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
attrs.setTitle(getClass().getSimpleName());
getWindow().setAttributes(attrs);
- mDismissReceiver = dismissOnDeviceLock ? new DismissReceiver(this) : null;
-
- // TODO(b/219008720): Remove those calls to Dependency.get by introducing a
- // SystemUIDialogFactory and make all other dialogs create a SystemUIDialog to which we set
- // the content and attach listeners.
- mDialogManager = Dependency.get(SystemUIDialogManager.class);
- mSysUiState = Dependency.get(SysUiState.class);
+ mDismissReceiver = dismissOnDeviceLock ? new DismissReceiver(this, broadcastDispatcher,
+ dialogLaunchAnimator) : null;
+ mDialogManager = dialogManager;
+ mSysUiState = sysUiState;
}
@Override
@@ -326,7 +334,10 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
* @param dismissAction An action to run when the dialog is dismissed.
*/
public static void registerDismissListener(Dialog dialog, @Nullable Runnable dismissAction) {
- DismissReceiver dismissReceiver = new DismissReceiver(dialog);
+ // TODO(b/219008720): Remove those calls to Dependency.get.
+ DismissReceiver dismissReceiver = new DismissReceiver(dialog,
+ Dependency.get(BroadcastDispatcher.class),
+ Dependency.get(DialogLaunchAnimator.class));
dialog.setOnDismissListener(d -> {
dismissReceiver.unregister();
if (dismissAction != null) dismissAction.run();
@@ -408,11 +419,11 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh
private final BroadcastDispatcher mBroadcastDispatcher;
private final DialogLaunchAnimator mDialogLaunchAnimator;
- DismissReceiver(Dialog dialog) {
+ DismissReceiver(Dialog dialog, BroadcastDispatcher broadcastDispatcher,
+ DialogLaunchAnimator dialogLaunchAnimator) {
mDialog = dialog;
- // TODO(b/219008720): Remove those calls to Dependency.get.
- mBroadcastDispatcher = Dependency.get(BroadcastDispatcher.class);
- mDialogLaunchAnimator = Dependency.get(DialogLaunchAnimator.class);
+ mBroadcastDispatcher = broadcastDispatcher;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
}
void register() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
index c5e5297ae6ba..64b04e93e69c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
@@ -22,6 +22,10 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.android.keyguard.LockIconViewController;
import com.android.systemui.biometrics.AuthRippleController;
+import com.android.systemui.shade.LargeScreenShadeHeaderController;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.NotificationShadeWindowView;
+import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.core.StatusBarInitializer;
@@ -32,10 +36,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutListContainerModule;
import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks;
import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
-import com.android.systemui.statusbar.phone.LargeScreenShadeHeaderController;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarterModule;
import com.android.systemui.statusbar.phone.StatusBarNotificationPresenterModule;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 06532c4f9d17..b60739164a19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -30,10 +30,17 @@ import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.biometrics.AuthRippleView;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.privacy.OngoingPrivacyChip;
+import com.android.systemui.shade.CombinedShadeHeadersConstraintManager;
+import com.android.systemui.shade.CombinedShadeHeadersConstraintManagerImpl;
+import com.android.systemui.shade.NotificationPanelView;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.NotificationShadeWindowView;
+import com.android.systemui.shade.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -42,11 +49,8 @@ import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
-import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
@@ -177,6 +181,14 @@ public abstract class StatusBarViewModule {
/** */
@Provides
@CentralSurfacesComponent.CentralSurfacesScope
+ public static CombinedShadeHeadersConstraintManager
+ provideCombinedShadeHeadersConstraintManager() {
+ return CombinedShadeHeadersConstraintManagerImpl.INSTANCE;
+ }
+
+ /** */
+ @Provides
+ @CentralSurfacesComponent.CentralSurfacesScope
public static OngoingPrivacyChip getSplitShadeOngoingPrivacyChip(
@Named(LARGE_SCREEN_SHADE_HEADER) View header) {
return header.findViewById(R.id.privacy_chip);
@@ -268,7 +280,8 @@ public abstract class StatusBarViewModule {
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
OperatorNameViewController.Factory operatorNameViewControllerFactory,
SecureSettings secureSettings,
- @Main Executor mainExecutor
+ @Main Executor mainExecutor,
+ DumpManager dumpManager
) {
return new CollapsedStatusBarFragment(statusBarFragmentComponentFactory,
ongoingCallController,
@@ -288,6 +301,20 @@ public abstract class StatusBarViewModule {
collapsedStatusBarFragmentLogger,
operatorNameViewControllerFactory,
secureSettings,
- mainExecutor);
+ mainExecutor,
+ dumpManager);
+ }
+
+ /**
+ * Constructs a new, unattached {@link KeyguardBottomAreaView}.
+ *
+ * Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it
+ */
+ @Provides
+ public static KeyguardBottomAreaView providesKeyguardBottomAreaView(
+ NotificationPanelView npv, LayoutInflater layoutInflater) {
+ return (KeyguardBottomAreaView) layoutInflater.inflate(R
+ .layout.keyguard_bottom_area, npv, false);
}
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 597c949168d4..0848729781bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -34,6 +34,8 @@ import android.os.Parcelable;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
+import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
@@ -43,11 +45,14 @@ import android.widget.LinearLayout;
import androidx.annotation.VisibleForTesting;
+import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DisableFlagsLogger.DisableState;
import com.android.systemui.statusbar.OperatorNameView;
@@ -59,13 +64,13 @@ import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent.Startable;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -76,9 +81,12 @@ import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListen
import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
import com.android.systemui.util.settings.SecureSettings;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -89,7 +97,7 @@ import java.util.concurrent.Executor;
@SuppressLint("ValidFragment")
public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks,
StatusBarStateController.StateListener,
- SystemStatusAnimationCallback {
+ SystemStatusAnimationCallback, Dumpable {
public static final String TAG = "CollapsedStatusBarFragment";
private static final String EXTRA_PANEL_STATE = "panel_state";
@@ -102,7 +110,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private final KeyguardStateController mKeyguardStateController;
private final NotificationPanelViewController mNotificationPanelViewController;
private final NetworkController mNetworkController;
- private LinearLayout mSystemIconArea;
+ private LinearLayout mEndSideContent;
private View mClockView;
private View mOngoingCallChip;
private View mNotificationIconAreaInner;
@@ -124,8 +132,10 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
private final SecureSettings mSecureSettings;
private final Executor mMainExecutor;
+ private final DumpManager mDumpManager;
private List<String> mBlockedIcons = new ArrayList<>();
+ private Map<Startable, Startable.State> mStartableStates = new ArrayMap<>();
private SignalCallback mSignalCallback = new SignalCallback() {
@Override
@@ -185,7 +195,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
OperatorNameViewController.Factory operatorNameViewControllerFactory,
SecureSettings secureSettings,
- @Main Executor mainExecutor
+ @Main Executor mainExecutor,
+ DumpManager dumpManager
) {
mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
mOngoingCallController = ongoingCallController;
@@ -206,6 +217,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
mSecureSettings = secureSettings;
mMainExecutor = mainExecutor;
+ mDumpManager = dumpManager;
}
@Override
@@ -217,8 +229,15 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
+ mDumpManager.registerDumpable(getClass().getSimpleName(), this);
mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(this);
mStatusBarFragmentComponent.init();
+ mStartableStates.clear();
+ for (Startable startable : mStatusBarFragmentComponent.getStartables()) {
+ mStartableStates.put(startable, Startable.State.STARTING);
+ startable.start();
+ mStartableStates.put(startable, Startable.State.STARTED);
+ }
mStatusBar = (PhoneStatusBarView) view;
View contents = mStatusBar.findViewById(R.id.status_bar_contents);
@@ -232,16 +251,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mDarkIconManager.setShouldLog(true);
updateBlockedIcons();
mStatusBarIconController.addIconGroup(mDarkIconManager);
- mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
+ mEndSideContent = mStatusBar.findViewById(R.id.status_bar_end_side_content);
mClockView = mStatusBar.findViewById(R.id.clock);
mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip);
- showSystemIconArea(false);
+ showEndSideContent(false);
showClock(false);
initEmergencyCryptkeeperText();
initOperatorName();
initNotificationIconArea();
mSystemEventAnimator =
- new StatusBarSystemEventAnimator(mSystemIconArea, getResources());
+ new StatusBarSystemEventAnimator(mEndSideContent, getResources());
mCarrierConfigTracker.addCallback(mCarrierConfigCallback);
mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
}
@@ -321,6 +340,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
mCarrierConfigTracker.removeCallback(mCarrierConfigCallback);
mCarrierConfigTracker.removeDataSubscriptionChangedListener(mDefaultDataListener);
+
+ for (Startable startable : mStatusBarFragmentComponent.getStartables()) {
+ mStartableStates.put(startable, Startable.State.STOPPING);
+ startable.stop();
+ mStartableStates.put(startable, Startable.State.STOPPED);
+ }
+ mDumpManager.unregisterDumpable(getClass().getSimpleName());
}
/** Initializes views related to the notification icon area. */
@@ -370,10 +396,10 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mDisabled2 = state2;
if ((diff1 & DISABLE_SYSTEM_INFO) != 0 || ((diff2 & DISABLE2_SYSTEM_ICONS) != 0)) {
if ((state1 & DISABLE_SYSTEM_INFO) != 0 || ((state2 & DISABLE2_SYSTEM_ICONS) != 0)) {
- hideSystemIconArea(animate);
+ hideEndSideContent(animate);
hideOperatorName(animate);
} else {
- showSystemIconArea(animate);
+ showEndSideContent(animate);
showOperatorName(animate);
}
}
@@ -474,15 +500,15 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
return mStatusBarHideIconsForBouncerManager.getShouldHideStatusBarIconsForBouncer();
}
- private void hideSystemIconArea(boolean animate) {
- animateHide(mSystemIconArea, animate);
+ private void hideEndSideContent(boolean animate) {
+ animateHide(mEndSideContent, animate);
}
- private void showSystemIconArea(boolean animate) {
+ private void showEndSideContent(boolean animate) {
// Only show the system icon area if we are not currently animating
int state = mAnimationScheduler.getAnimationState();
if (state == IDLE || state == SHOWING_PERSISTENT_DOT) {
- animateShow(mSystemIconArea, animate);
+ animateShow(mEndSideContent, animate);
}
}
@@ -670,4 +696,23 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
updateStatusBarLocation(left, right);
}
};
+
+ @Override
+ public void dump(PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, /* singleIndent= */" ");
+ StatusBarFragmentComponent component = mStatusBarFragmentComponent;
+ if (component == null) {
+ pw.println("StatusBarFragmentComponent is null");
+ } else {
+ Set<Startable> startables = component.getStartables();
+ pw.println("Startables: " + startables.size());
+ pw.increaseIndent();
+ for (Startable startable : startables) {
+ Startable.State startableState = mStartableStates.getOrDefault(startable,
+ Startable.State.NONE);
+ pw.println(startable + ", state: " + startableState);
+ }
+ pw.decreaseIndent();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
index 6717bc768fbb..d9a58442d856 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
@@ -23,9 +23,12 @@ import com.android.systemui.statusbar.phone.LightsOutNotifController;
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
+import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
import com.android.systemui.statusbar.phone.StatusBarDemoMode;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
+import java.util.Set;
+
import dagger.BindsInstance;
import dagger.Subcomponent;
@@ -41,7 +44,10 @@ import dagger.Subcomponent;
* should be included here or in {@link StatusBarFragmentModule}.
*/
-@Subcomponent(modules = {StatusBarFragmentModule.class})
+@Subcomponent(modules = {
+ StatusBarFragmentModule.class,
+ StatusBarStartablesModule.class
+})
@StatusBarFragmentScope
public interface StatusBarFragmentComponent {
/** Simple factory. */
@@ -52,6 +58,18 @@ public interface StatusBarFragmentComponent {
}
/**
+ * Performs initialization logic after {@link StatusBarFragmentComponent} has been constructed.
+ */
+ interface Startable {
+ void start();
+ void stop();
+
+ enum State {
+ NONE, STARTING, STARTED, STOPPING, STOPPED
+ }
+ }
+
+ /**
* Initialize anything extra for the component. Must be called after the component is created.
*/
default void init() {
@@ -92,4 +110,10 @@ public interface StatusBarFragmentComponent {
/** */
@StatusBarFragmentScope
PhoneStatusBarTransitions getPhoneStatusBarTransitions();
+
+ /** */
+ Set<Startable> getStartables();
+
+ /** */
+ StatusBarBoundsProvider getBoundsProvider();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index d5f5362eaf3c..41f1f9589ce4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -21,11 +21,12 @@ import android.view.View;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
+import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
@@ -34,12 +35,14 @@ import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import java.util.Optional;
+import java.util.Set;
import javax.inject.Named;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.Multibinds;
/** Dagger module for {@link StatusBarFragmentComponent}. */
@Module
@@ -48,6 +51,8 @@ public interface StatusBarFragmentModule {
String LIGHTS_OUT_NOTIF_VIEW = "lights_out_notif_view";
String OPERATOR_NAME_VIEW = "operator_name_view";
String OPERATOR_NAME_FRAME_VIEW = "operator_name_frame_view";
+ String START_SIDE_CONTENT = "start_side_content";
+ String END_SIDE_CONTENT = "end_side_content";
/** */
@Provides
@@ -68,6 +73,22 @@ public interface StatusBarFragmentModule {
/** */
@Provides
@StatusBarFragmentScope
+ @Named(START_SIDE_CONTENT)
+ static View startSideContent(@RootView PhoneStatusBarView view) {
+ return view.findViewById(R.id.status_bar_start_side_content);
+ }
+
+ /** */
+ @Provides
+ @StatusBarFragmentScope
+ @Named(END_SIDE_CONTENT)
+ static View endSideContent(@RootView PhoneStatusBarView view) {
+ return view.findViewById(R.id.status_bar_end_side_content);
+ }
+
+ /** */
+ @Provides
+ @StatusBarFragmentScope
@Named(LIGHTS_OUT_NOTIF_VIEW)
static View provideLightsOutNotifView(@RootView PhoneStatusBarView view) {
return view.findViewById(R.id.notification_lights_out);
@@ -138,4 +159,8 @@ public interface StatusBarFragmentModule {
static HeadsUpStatusBarView providesHeasdUpStatusBarView(@RootView PhoneStatusBarView view) {
return view.findViewById(R.id.heads_up_status_bar_view);
}
+
+ /** */
+ @Multibinds
+ Set<StatusBarBoundsProvider.BoundsChangeListener> boundsChangeListeners();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt
new file mode 100644
index 000000000000..9003d13df0a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.fragment.dagger
+
+import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+internal interface StatusBarStartablesModule {
+
+ @Binds
+ @IntoSet
+ fun statusBarBoundsCalculator(
+ statusBarBoundsProvider: StatusBarBoundsProvider
+ ): StatusBarFragmentComponent.Startable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionChangeEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionChangeEvent.kt
index c0384b444511..7c61b299cff9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionChangeEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionChangeEvent.kt
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.panelstate
import android.annotation.FloatRange
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt
index 911e750f666e..a6160aaf7756 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt
@@ -20,6 +20,7 @@ import android.annotation.IntDef
import android.util.Log
import androidx.annotation.FloatRange
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.util.Compile
import javax.inject.Inject
/**
@@ -109,8 +110,8 @@ class PanelExpansionStateManager @Inject constructor() {
debugLog(
"panelExpansionChanged:" +
- "start state=${oldState.stateToString()} " +
- "end state=${state.stateToString()} " +
+ "start state=${oldState.panelStateToString()} " +
+ "end state=${state.panelStateToString()} " +
"f=$fraction " +
"expanded=$expanded " +
"tracking=$tracking" +
@@ -126,14 +127,15 @@ class PanelExpansionStateManager @Inject constructor() {
/** Updates the panel state if necessary. */
fun updateState(@PanelState state: Int) {
- debugLog("update state: ${this.state.stateToString()} -> ${state.stateToString()}")
+ debugLog(
+ "update state: ${this.state.panelStateToString()} -> ${state.panelStateToString()}")
if (this.state != state) {
updateStateInternal(state)
}
}
private fun updateStateInternal(@PanelState state: Int) {
- debugLog("go state: ${this.state.stateToString()} -> ${state.stateToString()}")
+ debugLog("go state: ${this.state.panelStateToString()} -> ${state.panelStateToString()}")
this.state = state
stateListeners.forEach { it.onPanelStateChanged(state) }
}
@@ -154,7 +156,7 @@ const val STATE_OPENING = 1
const val STATE_OPEN = 2
@PanelState
-private fun Int.stateToString(): String {
+fun Int.panelStateToString(): String {
return when (this) {
STATE_CLOSED -> "CLOSED"
STATE_OPENING -> "OPENING"
@@ -163,5 +165,5 @@ private fun Int.stateToString(): String {
}
}
-private const val DEBUG = false
private val TAG = PanelExpansionStateManager::class.simpleName
+private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index a89c128dd584..753e94015751 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -119,6 +119,17 @@ public interface BatteryController extends DemoMode, Dumpable,
}
/**
+ * Returns {@code true} if the charging source is
+ * {@link android.os.BatteryManager#BATTERY_PLUGGED_DOCK}.
+ *
+ * <P>Note that charging from dock is not considered as wireless charging. In other words,
+ * {@link BatteryController#isWirelessCharging()} and this are mutually exclusive.
+ */
+ default boolean isChargingSourceDock() {
+ return false;
+ }
+
+ /**
* A listener that will be notified whenever a change in battery level or power save mode has
* occurred.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 917a5e0b9374..33ddf7eed006 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -76,7 +76,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
protected int mLevel;
protected boolean mPluggedIn;
- private boolean mPluggedInWireless;
+ private int mPluggedChargingSource;
protected boolean mCharging;
private boolean mStateUnknown = false;
private boolean mCharged;
@@ -195,10 +195,8 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
mLevel = (int)(100f
* intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
/ intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
- mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
- mPluggedInWireless = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)
- == BatteryManager.BATTERY_PLUGGED_WIRELESS;
-
+ mPluggedChargingSource = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
+ mPluggedIn = mPluggedChargingSource != 0;
final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
@@ -284,7 +282,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
@Override
public boolean isPluggedInWireless() {
- return mPluggedInWireless;
+ return mPluggedChargingSource == BatteryManager.BATTERY_PLUGGED_WIRELESS;
}
@Override
@@ -441,4 +439,9 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
registerReceiver();
updatePowerSave();
}
+
+ @Override
+ public boolean isChargingSourceDock() {
+ return mPluggedChargingSource == BatteryManager.BATTERY_PLUGGED_DOCK;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 5bd20ff2d090..86e74564fba0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -28,9 +28,9 @@ import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.settings.brightness.ToggleSlider;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import java.util.Objects;
import java.util.function.Consumer;
@@ -68,6 +68,7 @@ public class BrightnessMirrorController
mBrightnessMirror.setVisibility(View.INVISIBLE);
});
mVisibilityCallback = visibilityCallback;
+ updateResources();
}
public void showMirror() {
@@ -154,6 +155,7 @@ public class BrightnessMirrorController
.inflate(R.layout.brightness_mirror_container, mStatusBarWindow, false);
mToggleSliderController = setMirrorLayout();
mStatusBarWindow.addView(mBrightnessMirror, index);
+ updateResources();
for (int i = 0; i < mBrightnessMirrorListeners.size(); i++) {
mBrightnessMirrorListeners.valueAt(i).onBrightnessMirrorReinflated(mBrightnessMirror);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
index 4cf1d2b3f91d..9946b4b9ecaa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightControllerImpl.java
@@ -17,15 +17,11 @@
package com.android.systemui.statusbar.policy;
import android.annotation.WorkerThread;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Process;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.text.TextUtils;
@@ -33,12 +29,19 @@ import android.util.Log;
import androidx.annotation.NonNull;
+import com.android.internal.annotations.GuardedBy;
+import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.util.settings.SecureSettings;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject;
@@ -59,65 +62,88 @@ public class FlashlightControllerImpl implements FlashlightController {
"com.android.settings.flashlight.action.FLASHLIGHT_CHANGED";
private final CameraManager mCameraManager;
- private final Context mContext;
- /** Call {@link #ensureHandler()} before using */
- private Handler mHandler;
+ private final Executor mExecutor;
+ private final SecureSettings mSecureSettings;
+ private final DumpManager mDumpManager;
+ private final BroadcastSender mBroadcastSender;
- /** Lock on mListeners when accessing */
+ private final boolean mHasFlashlight;
+
+ @GuardedBy("mListeners")
private final ArrayList<WeakReference<FlashlightListener>> mListeners = new ArrayList<>(1);
- /** Lock on {@code this} when accessing */
+ @GuardedBy("this")
private boolean mFlashlightEnabled;
-
- private String mCameraId;
+ @GuardedBy("this")
private boolean mTorchAvailable;
+ private final AtomicReference<String> mCameraId;
+ private final AtomicBoolean mInitted = new AtomicBoolean(false);
+
@Inject
- public FlashlightControllerImpl(Context context, DumpManager dumpManager) {
- mContext = context;
- mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+ public FlashlightControllerImpl(
+ DumpManager dumpManager,
+ CameraManager cameraManager,
+ @Background Executor bgExecutor,
+ SecureSettings secureSettings,
+ BroadcastSender broadcastSender,
+ PackageManager packageManager
+ ) {
+ mCameraManager = cameraManager;
+ mExecutor = bgExecutor;
+ mCameraId = new AtomicReference<>(null);
+ mSecureSettings = secureSettings;
+ mDumpManager = dumpManager;
+ mBroadcastSender = broadcastSender;
+
+ mHasFlashlight = packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
+ init();
+ }
- dumpManager.registerDumpable(getClass().getSimpleName(), this);
- tryInitCamera();
+ private void init() {
+ if (!mInitted.getAndSet(true)) {
+ mDumpManager.registerDumpable(getClass().getSimpleName(), this);
+ mExecutor.execute(this::tryInitCamera);
+ }
}
+ @WorkerThread
private void tryInitCamera() {
+ if (!mHasFlashlight || mCameraId.get() != null) return;
try {
- mCameraId = getCameraId();
+ mCameraId.set(getCameraId());
} catch (Throwable e) {
Log.e(TAG, "Couldn't initialize.", e);
return;
}
- if (mCameraId != null) {
- ensureHandler();
- mCameraManager.registerTorchCallback(mTorchCallback, mHandler);
+ if (mCameraId.get() != null) {
+ mCameraManager.registerTorchCallback(mExecutor, mTorchCallback);
}
}
public void setFlashlight(boolean enabled) {
- boolean pendingError = false;
- synchronized (this) {
- if (mCameraId == null) return;
- if (mFlashlightEnabled != enabled) {
- mFlashlightEnabled = enabled;
- try {
- mCameraManager.setTorchMode(mCameraId, enabled);
- } catch (CameraAccessException e) {
- Log.e(TAG, "Couldn't set torch mode", e);
- mFlashlightEnabled = false;
- pendingError = true;
+ if (!mHasFlashlight) return;
+ if (mCameraId.get() == null) {
+ mExecutor.execute(this::tryInitCamera);
+ }
+ mExecutor.execute(() -> {
+ if (mCameraId.get() == null) return;
+ synchronized (this) {
+ if (mFlashlightEnabled != enabled) {
+ try {
+ mCameraManager.setTorchMode(mCameraId.get(), enabled);
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Couldn't set torch mode", e);
+ dispatchError();
+ }
}
}
- }
- dispatchModeChanged(mFlashlightEnabled);
- if (pendingError) {
- dispatchError();
- }
+ });
}
public boolean hasFlashlight() {
- return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
+ return mHasFlashlight;
}
public synchronized boolean isEnabled() {
@@ -131,13 +157,13 @@ public class FlashlightControllerImpl implements FlashlightController {
@Override
public void addCallback(@NonNull FlashlightListener l) {
synchronized (mListeners) {
- if (mCameraId == null) {
- tryInitCamera();
+ if (mCameraId.get() == null) {
+ mExecutor.execute(this::tryInitCamera);
}
cleanUpListenersLocked(l);
mListeners.add(new WeakReference<>(l));
- l.onFlashlightAvailabilityChanged(mTorchAvailable);
- l.onFlashlightChanged(mFlashlightEnabled);
+ l.onFlashlightAvailabilityChanged(isAvailable());
+ l.onFlashlightChanged(isEnabled());
}
}
@@ -148,14 +174,7 @@ public class FlashlightControllerImpl implements FlashlightController {
}
}
- private synchronized void ensureHandler() {
- if (mHandler == null) {
- HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
- thread.start();
- mHandler = new Handler(thread.getLooper());
- }
- }
-
+ @WorkerThread
private String getCameraId() throws CameraAccessException {
String[] ids = mCameraManager.getCameraIdList();
for (String id : ids) {
@@ -221,10 +240,9 @@ public class FlashlightControllerImpl implements FlashlightController {
@Override
@WorkerThread
public void onTorchModeUnavailable(String cameraId) {
- if (TextUtils.equals(cameraId, mCameraId)) {
+ if (TextUtils.equals(cameraId, mCameraId.get())) {
setCameraAvailable(false);
- Settings.Secure.putInt(
- mContext.getContentResolver(), Settings.Secure.FLASHLIGHT_AVAILABLE, 0);
+ mSecureSettings.putInt(Settings.Secure.FLASHLIGHT_AVAILABLE, 0);
}
}
@@ -232,14 +250,12 @@ public class FlashlightControllerImpl implements FlashlightController {
@Override
@WorkerThread
public void onTorchModeChanged(String cameraId, boolean enabled) {
- if (TextUtils.equals(cameraId, mCameraId)) {
+ if (TextUtils.equals(cameraId, mCameraId.get())) {
setCameraAvailable(true);
setTorchMode(enabled);
- Settings.Secure.putInt(
- mContext.getContentResolver(), Settings.Secure.FLASHLIGHT_AVAILABLE, 1);
- Settings.Secure.putInt(
- mContext.getContentResolver(), Secure.FLASHLIGHT_ENABLED, enabled ? 1 : 0);
- mContext.sendBroadcast(new Intent(ACTION_FLASHLIGHT_CHANGED));
+ mSecureSettings.putInt(Settings.Secure.FLASHLIGHT_AVAILABLE, 1);
+ mSecureSettings.putInt(Secure.FLASHLIGHT_ENABLED, enabled ? 1 : 0);
+ mBroadcastSender.sendBroadcast(new Intent(ACTION_FLASHLIGHT_CHANGED));
}
}
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 bce5a159f79c..d3837d7347dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -142,7 +142,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
protected void setEntryPinned(
@NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
- mLogger.logSetEntryPinned(headsUpEntry.mEntry.getKey(), isPinned);
+ mLogger.logSetEntryPinned(headsUpEntry.mEntry, isPinned);
NotificationEntry entry = headsUpEntry.mEntry;
if (entry.isRowPinned() != isPinned) {
entry.setRowPinned(isPinned);
@@ -183,7 +183,7 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
entry.setHeadsUp(false);
setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 0 /* visible */);
- mLogger.logNotificationActuallyRemoved(entry.getKey());
+ mLogger.logNotificationActuallyRemoved(entry);
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
index 6a74ba957b4b..d7c81af53d8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
@@ -20,6 +20,8 @@ import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel.INFO
import com.android.systemui.log.LogLevel.VERBOSE
import com.android.systemui.log.dagger.NotificationHeadsUpLog
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
/** Logger for [HeadsUpManager]. */
@@ -56,9 +58,9 @@ class HeadsUpManagerLogger @Inject constructor(
})
}
- fun logShowNotification(key: String) {
+ fun logShowNotification(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
}, {
"show notification $str1"
})
@@ -66,16 +68,16 @@ class HeadsUpManagerLogger @Inject constructor(
fun logRemoveNotification(key: String, releaseImmediately: Boolean) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = logKey(key)
bool1 = releaseImmediately
}, {
"remove notification $str1 releaseImmediately: $bool1"
})
}
- fun logNotificationActuallyRemoved(key: String) {
+ fun logNotificationActuallyRemoved(entry: NotificationEntry) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
}, {
"notification removed $str1 "
})
@@ -83,7 +85,7 @@ class HeadsUpManagerLogger @Inject constructor(
fun logUpdateNotification(key: String, alert: Boolean, hasEntry: Boolean) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = logKey(key)
bool1 = alert
bool2 = hasEntry
}, {
@@ -91,12 +93,12 @@ class HeadsUpManagerLogger @Inject constructor(
})
}
- fun logUpdateEntry(key: String, updatePostTime: Boolean) {
+ fun logUpdateEntry(entry: NotificationEntry, updatePostTime: Boolean) {
buffer.log(TAG, INFO, {
- str1 = key
+ str1 = entry.logKey
bool1 = updatePostTime
}, {
- "update entry $key updatePostTime: $bool1"
+ "update entry $str1 updatePostTime: $bool1"
})
}
@@ -108,9 +110,9 @@ class HeadsUpManagerLogger @Inject constructor(
})
}
- fun logSetEntryPinned(key: String, isPinned: Boolean) {
+ fun logSetEntryPinned(entry: NotificationEntry, isPinned: Boolean) {
buffer.log(TAG, VERBOSE, {
- str1 = key
+ str1 = entry.logKey
bool1 = isPinned
}, {
"set entry pinned $str1 pinned: $bool1"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 2fb16ee9b3b9..bdac88837969 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -424,11 +424,6 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
}
@Override
- public void onFaceUnlockStateChanged(boolean running, int userId) {
- update(false /* updateAlways */);
- }
-
- @Override
public void onStrongAuthStateChanged(int userId) {
update(false /* updateAlways */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
deleted file mode 100644
index 3d317143eb51..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.ActivityIntentHelper;
-import com.android.systemui.statusbar.phone.KeyguardPreviewContainer;
-
-import java.util.List;
-
-/**
- * Utility class to inflate previews for phone and camera affordance.
- */
-public class PreviewInflater {
-
- private static final String TAG = "PreviewInflater";
-
- private static final String META_DATA_KEYGUARD_LAYOUT = "com.android.keyguard.layout";
- private final ActivityIntentHelper mActivityIntentHelper;
-
- private Context mContext;
- private LockPatternUtils mLockPatternUtils;
-
- public PreviewInflater(Context context, LockPatternUtils lockPatternUtils,
- ActivityIntentHelper activityIntentHelper) {
- mContext = context;
- mLockPatternUtils = lockPatternUtils;
- mActivityIntentHelper = activityIntentHelper;
- }
-
- public View inflatePreview(Intent intent) {
- WidgetInfo info = getWidgetInfo(intent);
- return inflatePreview(info);
- }
-
- public View inflatePreviewFromService(ComponentName componentName) {
- WidgetInfo info = getWidgetInfoFromService(componentName);
- return inflatePreview(info);
- }
-
- private KeyguardPreviewContainer inflatePreview(WidgetInfo info) {
- if (info == null) {
- return null;
- }
- View v = inflateWidgetView(info);
- if (v == null) {
- return null;
- }
- KeyguardPreviewContainer container = new KeyguardPreviewContainer(mContext, null);
- container.addView(v);
- return container;
- }
-
- private View inflateWidgetView(WidgetInfo widgetInfo) {
- View widgetView = null;
- try {
- Context appContext = mContext.createPackageContext(
- widgetInfo.contextPackage, Context.CONTEXT_RESTRICTED);
- LayoutInflater appInflater = (LayoutInflater)
- appContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- appInflater = appInflater.cloneInContext(appContext);
- widgetView = appInflater.inflate(widgetInfo.layoutId, null, false);
- } catch (PackageManager.NameNotFoundException|RuntimeException e) {
- Log.w(TAG, "Error creating widget view", e);
- }
- return widgetView;
- }
-
- private WidgetInfo getWidgetInfoFromService(ComponentName componentName) {
- PackageManager packageManager = mContext.getPackageManager();
- // Look for the preview specified in the service meta-data
- try {
- Bundle metaData = packageManager.getServiceInfo(
- componentName, PackageManager.GET_META_DATA).metaData;
- return getWidgetInfoFromMetaData(componentName.getPackageName(), metaData);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Failed to load preview; " + componentName.flattenToShortString()
- + " not found", e);
- }
- return null;
- }
-
- private WidgetInfo getWidgetInfoFromMetaData(String contextPackage,
- Bundle metaData) {
- if (metaData == null) {
- return null;
- }
- int layoutId = metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
- if (layoutId == 0) {
- return null;
- }
- WidgetInfo info = new WidgetInfo();
- info.contextPackage = contextPackage;
- info.layoutId = layoutId;
- return info;
- }
-
- private WidgetInfo getWidgetInfo(Intent intent) {
- PackageManager packageManager = mContext.getPackageManager();
- int flags = PackageManager.MATCH_DEFAULT_ONLY
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
- final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
- intent, flags, KeyguardUpdateMonitor.getCurrentUser());
- if (appList.size() == 0) {
- return null;
- }
- ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
- flags | PackageManager.GET_META_DATA,
- KeyguardUpdateMonitor.getCurrentUser());
- if (mActivityIntentHelper.wouldLaunchResolverActivity(resolved, appList)) {
- return null;
- }
- if (resolved == null || resolved.activityInfo == null) {
- return null;
- }
- return getWidgetInfoFromMetaData(resolved.activityInfo.packageName,
- resolved.activityInfo.metaData);
- }
-
- private static class WidgetInfo {
- String contextPackage;
- int layoutId;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 1b685d0aad7a..40281a194b12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -45,6 +45,7 @@ import android.os.UserManager;
import android.provider.Settings;
import android.telephony.TelephonyCallback;
import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -63,9 +64,11 @@ import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.users.UserCreatingDialog;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Dumpable;
+import com.android.systemui.GuestResetOrExitSessionReceiver;
import com.android.systemui.GuestResumeSessionReceiver;
import com.android.systemui.R;
import com.android.systemui.SystemUISecondaryUserService;
+import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.BroadcastSender;
@@ -114,6 +117,9 @@ public class UserSwitcherController implements Dumpable {
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
private static final long MULTI_USER_JOURNEY_TIMEOUT = 20000l;
+ private static final String INTERACTION_JANK_ADD_NEW_USER_TAG = "add_new_user";
+ private static final String INTERACTION_JANK_EXIT_GUEST_MODE_TAG = "exit_guest_mode";
+
protected final Context mContext;
protected final UserTracker mUserTracker;
protected final UserManager mUserManager;
@@ -121,6 +127,8 @@ public class UserSwitcherController implements Dumpable {
private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
@VisibleForTesting
final GuestResumeSessionReceiver mGuestResumeSessionReceiver;
+ @VisibleForTesting
+ final GuestResetOrExitSessionReceiver mGuestResetOrExitSessionReceiver;
private final KeyguardStateController mKeyguardStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
private final DevicePolicyManager mDevicePolicyManager;
@@ -188,7 +196,9 @@ public class UserSwitcherController implements Dumpable {
InteractionJankMonitor interactionJankMonitor,
LatencyTracker latencyTracker,
DumpManager dumpManager,
- DialogLaunchAnimator dialogLaunchAnimator) {
+ DialogLaunchAnimator dialogLaunchAnimator,
+ GuestResumeSessionReceiver guestResumeSessionReceiver,
+ GuestResetOrExitSessionReceiver guestResetOrExitSessionReceiver) {
mContext = context;
mActivityManager = activityManager;
mUserTracker = userTracker;
@@ -200,14 +210,13 @@ public class UserSwitcherController implements Dumpable {
mInteractionJankMonitor = interactionJankMonitor;
mLatencyTracker = latencyTracker;
mGlobalSettings = globalSettings;
- mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
- this, mUserTracker, mUiEventLogger, secureSettings);
+ mGuestResumeSessionReceiver = guestResumeSessionReceiver;
+ mGuestResetOrExitSessionReceiver = guestResetOrExitSessionReceiver;
mBgExecutor = bgExecutor;
mLongRunningExecutor = longRunningExecutor;
mUiExecutor = uiExecutor;
- if (!UserManager.isGuestUserEphemeral()) {
- mGuestResumeSessionReceiver.register(mBroadcastDispatcher);
- }
+ mGuestResumeSessionReceiver.register();
+ mGuestResetOrExitSessionReceiver.register();
mGuestUserAutoCreated = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_guestUserAutoCreated);
mGuestIsResetting = new AtomicBoolean();
@@ -278,6 +287,10 @@ public class UserSwitcherController implements Dumpable {
refreshUsers(UserHandle.USER_NULL);
}
+ private static boolean isEnableGuestModeUxChanges(Context context) {
+ return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_GUEST_MODE_UX_CHANGES);
+ }
+
/**
* Refreshes users from UserManager.
*
@@ -524,20 +537,31 @@ public class UserSwitcherController implements Dumpable {
private void onUserListItemClicked(int id, UserRecord record, DialogShower dialogShower) {
int currUserId = mUserTracker.getUserId();
+ // If switching from guest and guest is ephemeral, then follow the flow
+ // of showExitGuestDialog to remove current guest,
+ // and switch to selected user
+ UserInfo currUserInfo = mUserTracker.getUserInfo();
if (currUserId == id) {
if (record.isGuest) {
- showExitGuestDialog(id, dialogShower);
+ showExitGuestDialog(id, currUserInfo.isEphemeral(), dialogShower);
}
return;
}
- if (UserManager.isGuestUserEphemeral()) {
- // If switching from guest, we want to bring up the guest exit dialog instead of switching
- UserInfo currUserInfo = mUserManager.getUserInfo(currUserId);
- if (currUserInfo != null && currUserInfo.isGuest()) {
- showExitGuestDialog(currUserId, record.resolveId(), dialogShower);
+
+ if (currUserInfo != null && currUserInfo.isGuest()) {
+ if (isEnableGuestModeUxChanges(mContext)) {
+ showExitGuestDialog(currUserId, currUserInfo.isEphemeral(),
+ record.resolveId(), dialogShower);
return;
+ } else {
+ if (currUserInfo.isEphemeral()) {
+ showExitGuestDialog(currUserId, currUserInfo.isEphemeral(),
+ record.resolveId(), dialogShower);
+ return;
+ }
}
}
+
if (dialogShower != null) {
// If we haven't morphed into another dialog, it means we have just switched users.
// Then, dismiss the dialog.
@@ -559,7 +583,7 @@ public class UserSwitcherController implements Dumpable {
}
}
- private void showExitGuestDialog(int id, DialogShower dialogShower) {
+ private void showExitGuestDialog(int id, boolean isGuestEphemeral, DialogShower dialogShower) {
int newId = UserHandle.USER_SYSTEM;
if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
@@ -567,16 +591,19 @@ public class UserSwitcherController implements Dumpable {
newId = info.id;
}
}
- showExitGuestDialog(id, newId, dialogShower);
+ showExitGuestDialog(id, isGuestEphemeral, newId, dialogShower);
}
- private void showExitGuestDialog(int id, int targetId, DialogShower dialogShower) {
+ private void showExitGuestDialog(int id, boolean isGuestEphemeral,
+ int targetId, DialogShower dialogShower) {
if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
mExitGuestDialog.cancel();
}
- mExitGuestDialog = new ExitGuestDialog(mContext, id, targetId);
+ mExitGuestDialog = new ExitGuestDialog(mContext, id, isGuestEphemeral, targetId);
if (dialogShower != null) {
- dialogShower.showDialog(mExitGuestDialog);
+ dialogShower.showDialog(mExitGuestDialog, new DialogCuj(
+ InteractionJankMonitor.CUJ_USER_DIALOG_OPEN,
+ INTERACTION_JANK_EXIT_GUEST_MODE_TAG));
} else {
mExitGuestDialog.show();
}
@@ -588,7 +615,11 @@ public class UserSwitcherController implements Dumpable {
}
mAddUserDialog = new AddUserDialog(mContext);
if (dialogShower != null) {
- dialogShower.showDialog(mAddUserDialog);
+ dialogShower.showDialog(mAddUserDialog,
+ new DialogCuj(
+ InteractionJankMonitor.CUJ_USER_DIALOG_OPEN,
+ INTERACTION_JANK_ADD_NEW_USER_TAG
+ ));
} else {
mAddUserDialog.show();
}
@@ -808,6 +839,52 @@ public class UserSwitcherController implements Dumpable {
}
}
+ /**
+ * Exits guest user and switches to previous non-guest user. The guest must be the current
+ * user.
+ *
+ * @param guestUserId user id of the guest user to exit
+ * @param targetUserId user id of the guest user to exit, set to UserHandle.USER_NULL when
+ * target user id is not known
+ * @param forceRemoveGuestOnExit true: remove guest before switching user,
+ * false: remove guest only if its ephemeral, else keep guest
+ */
+ public void exitGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId,
+ boolean forceRemoveGuestOnExit) {
+ UserInfo currentUser = mUserTracker.getUserInfo();
+ if (currentUser.id != guestUserId) {
+ Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")"
+ + " is not current user (" + currentUser.id + ")");
+ return;
+ }
+ if (!currentUser.isGuest()) {
+ Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")"
+ + " is not a guest");
+ return;
+ }
+
+ int newUserId = UserHandle.USER_SYSTEM;
+ if (targetUserId == UserHandle.USER_NULL) {
+ // when target user is not specified switch to last non guest user
+ if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
+ UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
+ if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) {
+ newUserId = info.id;
+ }
+ }
+ } else {
+ newUserId = targetUserId;
+ }
+
+ if (currentUser.isEphemeral() || forceRemoveGuestOnExit) {
+ mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
+ removeGuestUser(currentUser.id, newUserId);
+ } else {
+ mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_SWITCH);
+ switchToUserId(newUserId);
+ }
+ }
+
private void scheduleGuestCreation() {
if (!mGuestCreationScheduled.compareAndSet(false, true)) {
return;
@@ -975,9 +1052,14 @@ public class UserSwitcherController implements Dumpable {
public String getName(Context context, UserRecord item) {
if (item.isGuest) {
if (item.isCurrent) {
- return context.getString(mController.mGuestUserAutoCreated
+ if (isEnableGuestModeUxChanges(context)) {
+ return context.getString(
+ com.android.settingslib.R.string.guest_exit_quick_settings_button);
+ } else {
+ return context.getString(mController.mGuestUserAutoCreated
? com.android.settingslib.R.string.guest_reset_guest
: com.android.settingslib.R.string.guest_exit_guest);
+ }
} else {
if (item.info != null) {
return context.getString(com.android.internal.R.string.guest_name);
@@ -994,8 +1076,13 @@ public class UserSwitcherController implements Dumpable {
? com.android.settingslib.R.string.guest_resetting
: com.android.internal.R.string.guest_name);
} else {
- return context.getString(
- com.android.settingslib.R.string.guest_new_guest);
+ if (isEnableGuestModeUxChanges(context)) {
+ // we always show "guest" as string, instead of "add guest"
+ return context.getString(com.android.internal.R.string.guest_name);
+ } else {
+ return context.getString(
+ com.android.settingslib.R.string.guest_new_guest);
+ }
}
}
}
@@ -1017,7 +1104,11 @@ public class UserSwitcherController implements Dumpable {
protected static Drawable getIconDrawable(Context context, UserRecord item) {
int iconRes;
if (item.isAddUser) {
- iconRes = R.drawable.ic_account_circle_filled;
+ if (isEnableGuestModeUxChanges(context)) {
+ iconRes = R.drawable.ic_add;
+ } else {
+ iconRes = R.drawable.ic_account_circle_filled;
+ }
} else if (item.isGuest) {
iconRes = R.drawable.ic_account_circle;
} else if (item.isAddSupervisedUser) {
@@ -1189,24 +1280,58 @@ public class UserSwitcherController implements Dumpable {
private final int mGuestId;
private final int mTargetId;
+ private final boolean mIsGuestEphemeral;
- public ExitGuestDialog(Context context, int guestId, int targetId) {
+ ExitGuestDialog(Context context, int guestId, boolean isGuestEphemeral,
+ int targetId) {
super(context);
- setTitle(mGuestUserAutoCreated
- ? com.android.settingslib.R.string.guest_reset_guest_dialog_title
- : com.android.settingslib.R.string.guest_remove_guest_dialog_title);
- setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
- setButton(DialogInterface.BUTTON_NEUTRAL,
- context.getString(android.R.string.cancel), this);
- setButton(DialogInterface.BUTTON_POSITIVE,
- context.getString(mGuestUserAutoCreated
+ if (isEnableGuestModeUxChanges(context)) {
+ if (isGuestEphemeral) {
+ setTitle(context.getString(
+ com.android.settingslib.R.string.guest_exit_dialog_title));
+ setMessage(context.getString(
+ com.android.settingslib.R.string.guest_exit_dialog_message));
+ setButton(DialogInterface.BUTTON_NEUTRAL,
+ context.getString(android.R.string.cancel), this);
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ context.getString(
+ com.android.settingslib.R.string.guest_exit_dialog_button), this);
+ } else {
+ setTitle(context.getString(
+ com.android.settingslib
+ .R.string.guest_exit_dialog_title_non_ephemeral));
+ setMessage(context.getString(
+ com.android.settingslib
+ .R.string.guest_exit_dialog_message_non_ephemeral));
+ setButton(DialogInterface.BUTTON_NEUTRAL,
+ context.getString(android.R.string.cancel), this);
+ setButton(DialogInterface.BUTTON_NEGATIVE,
+ context.getString(
+ com.android.settingslib.R.string.guest_exit_clear_data_button),
+ this);
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ context.getString(
+ com.android.settingslib.R.string.guest_exit_save_data_button),
+ this);
+ }
+ } else {
+ setTitle(mGuestUserAutoCreated
+ ? com.android.settingslib.R.string.guest_reset_guest_dialog_title
+ : com.android.settingslib.R.string.guest_remove_guest_dialog_title);
+ setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
+ setButton(DialogInterface.BUTTON_NEUTRAL,
+ context.getString(android.R.string.cancel), this);
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ context.getString(mGuestUserAutoCreated
? com.android.settingslib.R.string.guest_reset_guest_confirm_button
: com.android.settingslib.R.string.guest_remove_guest_confirm_button),
- this);
+ this);
+ }
SystemUIDialog.setWindowOnTop(this, mKeyguardStateController.isShowing());
setCanceledOnTouchOutside(false);
mGuestId = guestId;
mTargetId = targetId;
+ mIsGuestEphemeral = isGuestEphemeral;
}
@Override
@@ -1216,12 +1341,40 @@ public class UserSwitcherController implements Dumpable {
if (mFalsingManager.isFalseTap(penalty)) {
return;
}
- if (which == BUTTON_NEUTRAL) {
- cancel();
+ if (isEnableGuestModeUxChanges(getContext())) {
+ if (mIsGuestEphemeral) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ mDialogLaunchAnimator.dismissStack(this);
+ // Ephemeral guest: exit guest, guest is removed by the system
+ // on exit, since its marked ephemeral
+ exitGuestUser(mGuestId, mTargetId, false);
+ } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+ // Cancel clicked, do nothing
+ cancel();
+ }
+ } else {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ mDialogLaunchAnimator.dismissStack(this);
+ // Non-ephemeral guest: exit guest, guest is not removed by the system
+ // on exit, since its marked non-ephemeral
+ exitGuestUser(mGuestId, mTargetId, false);
+ } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+ mDialogLaunchAnimator.dismissStack(this);
+ // Non-ephemeral guest: remove guest and then exit
+ exitGuestUser(mGuestId, mTargetId, true);
+ } else if (which == DialogInterface.BUTTON_NEUTRAL) {
+ // Cancel clicked, do nothing
+ cancel();
+ }
+ }
} else {
- mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
- mDialogLaunchAnimator.dismissStack(this);
- removeGuestUser(mGuestId, mTargetId);
+ if (which == BUTTON_NEUTRAL) {
+ cancel();
+ } else {
+ mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
+ mDialogLaunchAnimator.dismissStack(this);
+ removeGuestUser(mGuestId, mTargetId);
+ }
}
}
}
@@ -1230,10 +1383,17 @@ public class UserSwitcherController implements Dumpable {
final class AddUserDialog extends SystemUIDialog implements
DialogInterface.OnClickListener {
- public AddUserDialog(Context context) {
+ AddUserDialog(Context context) {
super(context);
+
setTitle(com.android.settingslib.R.string.user_add_user_title);
- setMessage(com.android.settingslib.R.string.user_add_user_message_short);
+ String message = context.getString(
+ com.android.settingslib.R.string.user_add_user_message_short);
+ UserInfo currentUser = mUserTracker.getUserInfo();
+ if (currentUser != null && currentUser.isGuest() && currentUser.isEphemeral()) {
+ message += context.getString(R.string.user_add_user_message_guest_remove);
+ }
+ setMessage(message);
setButton(DialogInterface.BUTTON_NEUTRAL,
context.getString(android.R.string.cancel), this);
setButton(DialogInterface.BUTTON_POSITIVE,
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIInitializer.java
index c99ad23ab23d..fabbb2c1f44d 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIInitializer.java
@@ -18,18 +18,20 @@ package com.android.systemui.tv;
import android.content.Context;
-import com.android.systemui.SystemUIFactory;
+import com.android.systemui.SystemUIInitializer;
import com.android.systemui.dagger.GlobalRootComponent;
/**
- * TV variant {@link SystemUIFactory}, that substitutes default {@link GlobalRootComponent} for
+ * TV variant {@link SystemUIInitializer}, that substitutes default {@link GlobalRootComponent} for
* {@link TvGlobalRootComponent}
*/
-public class TvSystemUIFactory extends SystemUIFactory {
+public class TvSystemUIInitializer extends SystemUIInitializer {
+ public TvSystemUIInitializer(Context context) {
+ super(context);
+ }
+
@Override
- protected GlobalRootComponent buildGlobalRootComponent(Context context) {
- return DaggerTvGlobalRootComponent.builder()
- .context(context)
- .build();
+ protected GlobalRootComponent.Builder getGlobalRootComponentBuilder() {
+ return DaggerTvGlobalRootComponent.builder();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 9a19d8d11190..f1e89ac81b1b 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -35,6 +35,7 @@ import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
+import com.android.systemui.navigationbar.gestural.GestureModule;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
@@ -45,6 +46,7 @@ import com.android.systemui.qs.dagger.QSModule;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImplementation;
+import com.android.systemui.screenshot.ReferenceScreenshotModule;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -87,8 +89,10 @@ import dagger.multibindings.IntoSet;
* overridden by the System UI implementation.
*/
@Module(includes = {
+ GestureModule.class,
PowerModule.class,
QSModule.class,
+ ReferenceScreenshotModule.class,
VolumeModule.class,
},
subcomponents = {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index 7350b37e4b66..13ac39c77c93 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -18,7 +18,7 @@ package com.android.systemui.unfold
import com.android.keyguard.KeyguardUnfoldTransition
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.phone.NotificationPanelUnfoldAnimationController
+import com.android.systemui.shade.NotificationPanelUnfoldAnimationController
import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index d2d2361d613d..eea6ac0e72e9 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -20,6 +20,7 @@ import android.content.Context
import android.view.IWindowManager
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.system.SystemUnfoldSharedModule
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
@@ -34,7 +35,7 @@ import java.util.Optional
import javax.inject.Named
import javax.inject.Singleton
-@Module(includes = [UnfoldSharedModule::class])
+@Module(includes = [UnfoldSharedModule::class, SystemUnfoldSharedModule::class])
class UnfoldTransitionModule {
@Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui"
@@ -62,11 +63,6 @@ class UnfoldTransitionModule {
@Provides
@Singleton
- fun provideUnfoldTransitionConfig(context: Context): UnfoldTransitionConfig =
- createConfig(context)
-
- @Provides
- @Singleton
fun provideNaturalRotationProgressProvider(
context: Context,
windowManager: IWindowManager,
diff --git a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
index cc6bf6a70d4e..f01712653e1b 100644
--- a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
@@ -31,6 +31,7 @@ import androidx.annotation.Nullable;
import com.android.settingslib.users.EditUserInfoController;
import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
import javax.inject.Inject;
@@ -55,15 +56,18 @@ public class CreateUserActivity extends Activity {
private final UserCreator mUserCreator;
private final EditUserInfoController mEditUserInfoController;
private final IActivityManager mActivityManager;
+ private final ActivityStarter mActivityStarter;
private Dialog mSetupUserDialog;
@Inject
public CreateUserActivity(UserCreator userCreator,
- EditUserInfoController editUserInfoController, IActivityManager activityManager) {
+ EditUserInfoController editUserInfoController, IActivityManager activityManager,
+ ActivityStarter activityStarter) {
mUserCreator = userCreator;
mEditUserInfoController = editUserInfoController;
mActivityManager = activityManager;
+ mActivityStarter = activityStarter;
}
@Override
@@ -104,10 +108,7 @@ public class CreateUserActivity extends Activity {
return mEditUserInfoController.createDialog(
this,
- (intent, requestCode) -> {
- mEditUserInfoController.startingActivityForResult();
- startActivityForResult(intent, requestCode);
- },
+ this::startActivity,
null,
defaultUserName,
getString(com.android.settingslib.R.string.user_add_user),
@@ -160,4 +161,17 @@ public class CreateUserActivity extends Activity {
Log.e(TAG, "Couldn't switch user.", e);
}
}
+
+ /**
+ * Lambda to start activity from an intent. Ensures that device is unlocked first.
+ * @param intent
+ * @param requestCode
+ */
+ private void startActivity(Intent intent, int requestCode) {
+ mActivityStarter.dismissKeyguardThenExecute(() -> {
+ mEditUserInfoController.startingActivityForResult();
+ startActivityForResult(intent, requestCode);
+ return true;
+ }, /* cancel= */ null, /* afterKeyguardGone= */ true);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
index ad734914170b..74d51112deeb 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
@@ -28,6 +28,7 @@ import android.os.Bundle
import android.os.UserManager
import android.provider.Settings
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
@@ -38,8 +39,10 @@ import androidx.constraintlayout.helper.widget.Flow
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.util.UserIcons
import com.android.settingslib.Utils
+import com.android.systemui.Gefingerpoken
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
import com.android.systemui.settings.UserTracker
@@ -61,12 +64,13 @@ class UserSwitcherActivity @Inject constructor(
private val userSwitcherController: UserSwitcherController,
private val broadcastDispatcher: BroadcastDispatcher,
private val layoutInflater: LayoutInflater,
+ private val falsingCollector: FalsingCollector,
private val falsingManager: FalsingManager,
private val userManager: UserManager,
private val userTracker: UserTracker
) : LifecycleActivity() {
- private lateinit var parent: ViewGroup
+ private lateinit var parent: UserSwitcherRootView
private lateinit var broadcastReceiver: BroadcastReceiver
private var popupMenu: UserSwitcherPopupMenu? = null
private lateinit var addButton: View
@@ -202,7 +206,14 @@ class UserSwitcherActivity @Inject constructor(
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
- parent = requireViewById<ViewGroup>(R.id.user_switcher_root)
+ parent = requireViewById<UserSwitcherRootView>(R.id.user_switcher_root)
+
+ parent.touchHandler = object : Gefingerpoken {
+ override fun onTouchEvent(ev: MotionEvent?): Boolean {
+ falsingCollector.onTouchEvent(ev)
+ return false
+ }
+ }
requireViewById<View>(R.id.cancel).apply {
setOnClickListener {
@@ -241,7 +252,7 @@ class UserSwitcherActivity @Inject constructor(
)
popupMenuAdapter.addAll(items)
- popupMenu = UserSwitcherPopupMenu(this, falsingManager).apply {
+ popupMenu = UserSwitcherPopupMenu(this).apply {
setAnchorView(addButton)
setAdapter(popupMenuAdapter)
setOnItemClickListener {
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt
index 754a9342bfb0..ee785b62bd50 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt
@@ -23,16 +23,13 @@ import android.view.View.MeasureSpec
import android.widget.ListAdapter
import android.widget.ListPopupWindow
import android.widget.ListView
-
import com.android.systemui.R
-import com.android.systemui.plugins.FalsingManager
/**
* Popup menu for displaying items on the fullscreen user switcher.
*/
class UserSwitcherPopupMenu(
- private val context: Context,
- private val falsingManager: FalsingManager
+ private val context: Context
) : ListPopupWindow(context) {
private val res = context.resources
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherRootView.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherRootView.kt
new file mode 100644
index 000000000000..66a301744025
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherRootView.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.user
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.MotionEvent
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.android.systemui.Gefingerpoken
+
+/** A simple subclass that allows for observing touch events as they happen. */
+class UserSwitcherRootView(
+ context: Context,
+ attrs: AttributeSet?
+) : ConstraintLayout(context, attrs) {
+
+ /** Assign this field to observer touch events. */
+ var touchHandler: Gefingerpoken? = null
+
+ override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
+ touchHandler?.onTouchEvent(ev)
+ return super.dispatchTouchEvent(ev)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/AsyncActivityLauncher.kt b/packages/SystemUI/src/com/android/systemui/util/AsyncActivityLauncher.kt
new file mode 100644
index 000000000000..80d0e4b90359
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/AsyncActivityLauncher.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util
+
+import android.app.IActivityTaskManager
+import android.app.WaitResult
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.os.UserHandle
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dagger.qualifiers.UiBackground
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Helper class that allows to launch an activity and asynchronously wait
+ * for it to be launched. This class uses application context, so the intent
+ * will be launched with FLAG_ACTIVITY_NEW_TASK.
+ */
+class AsyncActivityLauncher @Inject constructor(
+ private val context: Context,
+ private val activityTaskManager: IActivityTaskManager,
+ @UiBackground private val backgroundExecutor: Executor,
+ @Main private val mainExecutor: Executor
+) {
+
+ private var pendingCallback: ((WaitResult) -> Unit)? = null
+
+ /**
+ * Starts activity and notifies about the result using the provided [callback].
+ * If there is already pending activity launch the call will be ignored.
+ *
+ * @return true if launch has started, false otherwise
+ */
+ fun startActivityAsUser(intent: Intent, userHandle: UserHandle,
+ activityOptions: Bundle? = null,
+ callback: (WaitResult) -> Unit): Boolean {
+ if (pendingCallback != null) return false
+
+ pendingCallback = callback
+
+ intent.flags = intent.flags or Intent.FLAG_ACTIVITY_NEW_TASK
+
+ backgroundExecutor.execute {
+ val waitResult = activityTaskManager.startActivityAndWait(
+ /* caller = */ null,
+ /* callingPackage = */ context.packageName,
+ /* callingFeatureId = */ context.attributionTag,
+ /* intent = */ intent,
+ /* resolvedType = */ null,
+ /* resultTo = */ null,
+ /* resultWho = */ null,
+ /* requestCode = */ 0,
+ /* flags = */ 0,
+ /* profilerInfo = */ null,
+ /* options = */ activityOptions,
+ /* userId = */ userHandle.identifier
+ )
+ mainExecutor.execute {
+ pendingCallback?.invoke(waitResult)
+ }
+ }
+
+ return true
+ }
+
+ /**
+ * Cancels pending activity launches. It guarantees that the callback won't be fired
+ * but the activity will be launched anyway.
+ */
+ fun destroy() {
+ pendingCallback = null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
index 57b3f53c48fe..ec7aabb58b49 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
@@ -16,7 +16,9 @@
package com.android.systemui.util
+import android.graphics.Rect
import android.util.IndentingPrintWriter
+import android.view.View
import android.view.ViewGroup
import java.io.PrintWriter
@@ -45,4 +47,12 @@ inline fun PrintWriter.indentIfPossible(block: PrintWriter.() -> Unit) {
if (this is IndentingPrintWriter) increaseIndent()
block()
if (this is IndentingPrintWriter) decreaseIndent()
-} \ No newline at end of file
+}
+
+/** Convenience extension property for [View.getBoundsOnScreen]. */
+val View.boundsOnScreen: Rect
+ get() {
+ val bounds = Rect()
+ getBoundsOnScreen(bounds)
+ return bounds
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt b/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt
new file mode 100644
index 000000000000..8b90547f7bdb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util
+
+import android.content.Context
+import android.util.AttributeSet
+import com.android.systemui.R
+
+class DelayableMarqueeTextView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : SafeMarqueeTextView(context, attrs, defStyleAttr, defStyleRes) {
+
+ var marqueeDelay: Long = DEFAULT_MARQUEE_DELAY
+ private var wantsMarquee = false
+ private var marqueeBlocked = true
+
+ private val enableMarquee = Runnable {
+ if (wantsMarquee) {
+ marqueeBlocked = false
+ startMarquee()
+ }
+ }
+
+ init {
+ val typedArray = context.theme.obtainStyledAttributes(
+ attrs,
+ R.styleable.DelayableMarqueeTextView,
+ defStyleAttr,
+ defStyleRes
+ )
+ marqueeDelay = typedArray.getInteger(
+ R.styleable.DelayableMarqueeTextView_marqueeDelay,
+ DEFAULT_MARQUEE_DELAY.toInt()
+ ).toLong()
+ typedArray.recycle()
+ }
+
+ override fun startMarquee() {
+ if (!isSelected) {
+ return
+ }
+ wantsMarquee = true
+ if (marqueeBlocked) {
+ if (handler?.hasCallbacks(enableMarquee) == false) {
+ postDelayed(enableMarquee, marqueeDelay)
+ }
+ return
+ }
+ super.startMarquee()
+ }
+
+ override fun stopMarquee() {
+ handler?.removeCallbacks(enableMarquee)
+ wantsMarquee = false
+ marqueeBlocked = true
+ super.stopMarquee()
+ }
+
+ companion object {
+ const val DEFAULT_MARQUEE_DELAY = 2000L
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt b/packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt
index f9524769d304..018ef968ff2d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt
@@ -55,6 +55,10 @@ fun IndentingPrintWriter.withIncreasedIndent(runnable: Runnable) {
}
}
+/** Print a line which is '$label=$value' */
+fun IndentingPrintWriter.println(label: String, value: Any) =
+ append(label).append('=').println(value)
+
/** Return a readable string for the visibility */
fun visibilityString(@View.Visibility visibility: Int): String = when (visibility) {
View.GONE -> "gone"
diff --git a/packages/SystemUI/src/com/android/systemui/util/InitializationChecker.kt b/packages/SystemUI/src/com/android/systemui/util/InitializationChecker.kt
new file mode 100644
index 000000000000..f53b6824d45d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/InitializationChecker.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util
+
+import android.app.ActivityThread
+import android.os.Process
+import com.android.systemui.dagger.qualifiers.InstrumentationTest
+import javax.inject.Inject
+
+/**
+ * Used to check whether SystemUI should be fully initialized.
+ */
+class InitializationChecker @Inject constructor(
+ @InstrumentationTest private val instrumentationTest: Boolean
+) {
+
+ /**
+ * Only initialize components for the main system ui process running as the primary user
+ */
+ fun initializeComponents(): Boolean =
+ !instrumentationTest &&
+ Process.myUserHandle().isSystem &&
+ ActivityThread.currentProcessName() == ActivityThread.currentPackageName()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt b/packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt
new file mode 100644
index 000000000000..3095d80d1630
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.Choreographer
+import androidx.constraintlayout.motion.widget.MotionLayout
+
+/**
+ * [MotionLayout] that avoids remeasuring with the same inputs in the same frame.
+ *
+ * This is important when this view is the child of a view that performs more than one measure pass
+ * (e.g. [LinearLayout] or [ConstraintLayout]). In those cases, if this view is measured with the
+ * same inputs in the same frame, we use the last result.
+ */
+class NoRemeasureMotionLayout @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet?,
+ defStyle: Int = 0
+) : MotionLayout(context, attrs, defStyle) {
+
+ private var lastWidthSpec: Int? = null
+ private var lastHeightSpec: Int? = null
+ private var lastFrame: Long? = null
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ if (
+ lastWidthSpec == widthMeasureSpec &&
+ lastHeightSpec == heightMeasureSpec &&
+ Choreographer.getMainThreadInstance()?.frameTime == lastFrame
+ ) {
+ setMeasuredDimension(measuredWidth, measuredHeight)
+ return
+ }
+ traceSection("NoRemeasureMotionLayout - measure") {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ lastWidthSpec = widthMeasureSpec
+ lastHeightSpec = heightMeasureSpec
+ lastFrame = Choreographer.getMainThreadInstance()?.frameTime
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index 76dfcb182e97..53da213eb38e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -35,11 +35,15 @@ import javax.inject.Inject;
public class NotificationChannels extends CoreStartable {
public static String ALERTS = "ALR";
public static String SCREENSHOTS_HEADSUP = "SCN_HEADSUP";
- public static String GENERAL = "GEN";
+ // Deprecated. Please use or create a more specific channel that users will better understand
+ @Deprecated
+ static String GENERAL = "GEN";
public static String STORAGE = "DSK";
public static String BATTERY = "BAT";
public static String TVPIP = TvPipNotificationController.NOTIFICATION_CHANNEL; // "TVPIP"
public static String HINTS = "HNT";
+ public static String INSTANT = "INS";
+ public static String SETUP = "STP";
@Inject
public NotificationChannels(Context context) {
@@ -64,11 +68,17 @@ public class NotificationChannels extends CoreStartable {
context.getString(R.string.notification_channel_alerts),
NotificationManager.IMPORTANCE_HIGH);
- final NotificationChannel general = new NotificationChannel(
- GENERAL,
- context.getString(R.string.notification_channel_general),
+ final NotificationChannel instant = new NotificationChannel(
+ INSTANT,
+ context.getString(R.string.notification_channel_instant),
NotificationManager.IMPORTANCE_MIN);
+ final NotificationChannel setup = new NotificationChannel(
+ SETUP,
+ context.getString(R.string.notification_channel_setup),
+ NotificationManager.IMPORTANCE_DEFAULT);
+ setup.setSound(null, null);
+
final NotificationChannel storage = new NotificationChannel(
STORAGE,
context.getString(R.string.notification_channel_storage),
@@ -84,7 +94,8 @@ public class NotificationChannels extends CoreStartable {
nm.createNotificationChannels(Arrays.asList(
alerts,
- general,
+ instant,
+ setup,
storage,
createScreenshotChannel(
context.getString(R.string.notification_channel_screenshot)),
@@ -123,6 +134,11 @@ public class NotificationChannels extends CoreStartable {
@Override
public void start() {
createAll(mContext);
+ cleanUp();
+ }
+
+ private void cleanUp() {
+ mContext.getSystemService(NotificationManager.class).deleteNotificationChannel(GENERAL);
}
private static boolean isTv(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/PluralMessageFormater.kt b/packages/SystemUI/src/com/android/systemui/util/PluralMessageFormater.kt
new file mode 100644
index 000000000000..83b471dff4b1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/PluralMessageFormater.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util
+
+import android.annotation.StringRes
+import android.content.res.Resources
+import android.util.PluralsMessageFormatter
+
+/**
+ * Utility method that provides the localized plural string for the given [messageId]
+ * using the [count] parameter.
+ */
+fun icuMessageFormat(res: Resources, @StringRes messageId: Int, count: Int): String {
+ return PluralsMessageFormatter.format(res, mapOf<String, Any>("count" to count), messageId)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
index d3c6e9aa0da9..dac8a0bff28a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
@@ -16,16 +16,17 @@
package com.android.systemui.util.condition;
+import android.util.ArraySet;
import android.util.Log;
-import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.systemui.dagger.qualifiers.Main;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@@ -36,162 +37,208 @@ import javax.inject.Inject;
* {@link Monitor} takes in a set of conditions, monitors whether all of them have
* been fulfilled, and informs any registered listeners.
*/
-public class Monitor implements CallbackController<Monitor.Callback> {
+public class Monitor {
private final String mTag = getClass().getSimpleName();
+ private final Executor mExecutor;
- private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final HashMap<Condition, ArraySet<Subscription.Token>> mConditions = new HashMap<>();
+ private final HashMap<Subscription.Token, SubscriptionState> mSubscriptions = new HashMap<>();
- // Set of all conditions that need to be monitored.
- private final Set<Condition> mConditions;
- private final Executor mExecutor;
+ private static class SubscriptionState {
+ private final Subscription mSubscription;
+ private Boolean mAllConditionsMet;
+
+ SubscriptionState(Subscription subscription) {
+ mSubscription = subscription;
+ }
+
+ public Set<Condition> getConditions() {
+ return mSubscription.mConditions;
+ }
+
+ public void update() {
+ // Overriding conditions do not override each other
+ final Collection<Condition> overridingConditions = mSubscription.mConditions.stream()
+ .filter(Condition::isOverridingCondition).collect(Collectors.toSet());
+
+ final Collection<Condition> targetCollection = overridingConditions.isEmpty()
+ ? mSubscription.mConditions : overridingConditions;
+
+ final boolean newAllConditionsMet = targetCollection.isEmpty() ? true : targetCollection
+ .stream()
+ .map(Condition::isConditionMet)
+ .allMatch(conditionMet -> conditionMet);
- // Whether all conditions have been met.
- private boolean mAllConditionsMet = false;
+ if (mAllConditionsMet != null && newAllConditionsMet == mAllConditionsMet) {
+ return;
+ }
- // Whether the monitor has started listening for all the conditions.
- private boolean mHaveConditionsStarted = false;
+ mAllConditionsMet = newAllConditionsMet;
+ mSubscription.mCallback.onConditionsChanged(mAllConditionsMet);
+ }
+ }
// Callback for when each condition has been updated.
private final Condition.Callback mConditionCallback = new Condition.Callback() {
@Override
public void onConditionChanged(Condition condition) {
- mExecutor.execute(() -> updateConditionMetState());
+ mExecutor.execute(() -> updateConditionMetState(condition));
}
};
@Inject
- public Monitor(Executor executor, Set<Condition> conditions, Set<Callback> callbacks) {
- mConditions = new HashSet<>();
+ public Monitor(@Main Executor executor) {
mExecutor = executor;
+ }
- if (conditions != null) {
- mConditions.addAll(conditions);
- }
+ private void updateConditionMetState(Condition condition) {
+ final ArraySet<Subscription.Token> subscriptions = mConditions.get(condition);
- if (callbacks == null) {
+ // It's possible the condition was removed between the time the callback occurred and
+ // update was executed on the main thread.
+ if (subscriptions == null) {
return;
}
- for (Callback callback : callbacks) {
- addCallbackLocked(callback);
- }
+ subscriptions.stream().forEach(token -> mSubscriptions.get(token).update());
}
- private void updateConditionMetState() {
- // Overriding conditions do not override each other
- final Collection<Condition> overridingConditions = mConditions.stream()
- .filter(Condition::isOverridingCondition).collect(Collectors.toSet());
-
- final Collection<Condition> targetCollection = overridingConditions.isEmpty()
- ? mConditions : overridingConditions;
-
- final boolean newAllConditionsMet = targetCollection.isEmpty() ? true : targetCollection
- .stream()
- .map(Condition::isConditionMet)
- .allMatch(conditionMet -> conditionMet);
-
- if (newAllConditionsMet == mAllConditionsMet) {
- return;
- }
+ /**
+ * Registers a callback and the set of conditions to trigger it.
+ * @param subscription A {@link Subscription} detailing the desired conditions and callback.
+ * @return A {@link Subscription.Token} that can be used to remove the subscription.
+ */
+ public Subscription.Token addSubscription(@NotNull Subscription subscription) {
+ final Subscription.Token token = new Subscription.Token();
+ final SubscriptionState state = new SubscriptionState(subscription);
- if (shouldLog()) Log.d(mTag, "all conditions met: " + newAllConditionsMet);
- mAllConditionsMet = newAllConditionsMet;
-
- // Updates all callbacks.
- final Iterator<Callback> iterator = mCallbacks.iterator();
- while (iterator.hasNext()) {
- final Callback callback = iterator.next();
- if (callback == null) {
- iterator.remove();
- } else {
- callback.onConditionsChanged(mAllConditionsMet);
- }
- }
- }
+ mExecutor.execute(() -> {
+ mSubscriptions.put(token, state);
- private void addConditionLocked(@NotNull Condition condition) {
- mConditions.add(condition);
+ // Add and associate conditions.
+ subscription.getConditions().stream().forEach(condition -> {
+ if (!mConditions.containsKey(condition)) {
+ mConditions.put(condition, new ArraySet<>());
+ condition.addCallback(mConditionCallback);
+ }
- if (!mHaveConditionsStarted) {
- return;
- }
+ mConditions.get(condition).add(token);
+ });
- condition.addCallback(mConditionCallback);
- updateConditionMetState();
- }
+ // Update subscription state.
+ state.update();
- /**
- * Adds a condition for the monitor to listen to and consider when determining whether the
- * overall condition state is met.
- */
- public void addCondition(@NotNull Condition condition) {
- mExecutor.execute(() -> addConditionLocked(condition));
+ });
+ return token;
}
/**
- * Removes a condition from further consideration.
+ * Removes a subscription from participating in future callbacks.
+ * @param token The {@link Subscription.Token} returned when the {@link Subscription} was
+ * originally added.
*/
- public void removeCondition(@NotNull Condition condition) {
+ public void removeSubscription(@NotNull Subscription.Token token) {
mExecutor.execute(() -> {
- mConditions.remove(condition);
-
- if (!mHaveConditionsStarted) {
+ if (shouldLog()) Log.d(mTag, "removing callback");
+ if (!mSubscriptions.containsKey(token)) {
+ Log.e(mTag, "subscription not present:" + token);
return;
}
- condition.removeCallback(mConditionCallback);
- updateConditionMetState();
+ mSubscriptions.remove(token).getConditions().forEach(condition -> {
+ if (!mConditions.containsKey(condition)) {
+ Log.e(mTag, "condition not present:" + condition);
+ return;
+
+ }
+ final Set<Subscription.Token> conditionSubscriptions = mConditions.get(condition);
+
+ conditionSubscriptions.remove(token);
+ if (conditionSubscriptions.isEmpty()) {
+ condition.removeCallback(mConditionCallback);
+ mConditions.remove(condition);
+ }
+ });
});
}
- private void addCallbackLocked(@NotNull Callback callback) {
- if (mCallbacks.contains(callback)) {
- return;
- }
+ private boolean shouldLog() {
+ return Log.isLoggable(mTag, Log.DEBUG);
+ }
- if (shouldLog()) Log.d(mTag, "adding callback");
- mCallbacks.add(callback);
+ /**
+ * A {@link Subscription} represents a set of conditions and a callback that is informed when
+ * these conditions change.
+ */
+ public static class Subscription {
+ private final Set<Condition> mConditions;
+ private final Callback mCallback;
+
+ /** */
+ public Subscription(Set<Condition> conditions, Callback callback) {
+ this.mConditions = Collections.unmodifiableSet(conditions);
+ this.mCallback = callback;
+ }
- // Updates the callback immediately.
- callback.onConditionsChanged(mAllConditionsMet);
+ public Set<Condition> getConditions() {
+ return mConditions;
+ }
- if (!mHaveConditionsStarted) {
- if (shouldLog()) Log.d(mTag, "starting all conditions");
- mConditions.forEach(condition -> condition.addCallback(mConditionCallback));
- updateConditionMetState();
- mHaveConditionsStarted = true;
+ public Callback getCallback() {
+ return mCallback;
}
- }
- @Override
- public void addCallback(@NotNull Callback callback) {
- mExecutor.execute(() -> addCallbackLocked(callback));
- }
+ /**
+ * A {@link Token} is an identifier that is associated with a {@link Subscription} which is
+ * registered with a {@link Monitor}.
+ */
+ public static class Token {
+ }
- @Override
- public void removeCallback(@NotNull Callback callback) {
- mExecutor.execute(() -> {
- if (shouldLog()) Log.d(mTag, "removing callback");
- final Iterator<Callback> iterator = mCallbacks.iterator();
- while (iterator.hasNext()) {
- final Callback cb = iterator.next();
- if (cb == null || cb == callback) {
- iterator.remove();
- }
+ /**
+ * {@link Builder} is a helper class for constructing a {@link Subscription}.
+ */
+ public static class Builder {
+ private final Callback mCallback;
+ private final ArraySet<Condition> mConditions;
+
+ /**
+ * Default constructor specifying the {@link Callback} for the {@link Subscription}.
+ * @param callback
+ */
+ public Builder(Callback callback) {
+ mCallback = callback;
+ mConditions = new ArraySet<>();
}
- if (mCallbacks.isEmpty() && mHaveConditionsStarted) {
- if (shouldLog()) Log.d(mTag, "stopping all conditions");
- mConditions.forEach(condition -> condition.removeCallback(mConditionCallback));
+ /**
+ * Adds a {@link Condition} to be associated with the {@link Subscription}.
+ * @param condition
+ * @return The updated {@link Builder}.
+ */
+ public Builder addCondition(Condition condition) {
+ mConditions.add(condition);
+ return this;
+ }
- mAllConditionsMet = false;
- mHaveConditionsStarted = false;
+ /**
+ * Adds a set of {@link Condition} to be associated with the {@link Subscription}.
+ * @param condition
+ * @return The updated {@link Builder}.
+ */
+ public Builder addConditions(Set<Condition> condition) {
+ mConditions.addAll(condition);
+ return this;
}
- });
- }
- private boolean shouldLog() {
- return Log.isLoggable(mTag, Log.DEBUG);
+ /**
+ * Builds the {@link Subscription}.
+ * @return The resulting {@link Subscription}.
+ */
+ public Subscription build() {
+ return new Subscription(mConditions, mCallback);
+ }
+ }
}
/**
@@ -199,6 +246,13 @@ public class Monitor implements CallbackController<Monitor.Callback> {
*/
public interface Callback {
/**
+ * Returns the conditions associated with this callback.
+ */
+ default ArrayList<Condition> getConditions() {
+ return new ArrayList<>();
+ }
+
+ /**
* Triggered when the fulfillment of all conditions have been met.
*
* @param allConditionsMet True if all conditions have been fulfilled. False if none or
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java b/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java
deleted file mode 100644
index fc67973fe278..000000000000
--- a/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.util.condition.dagger;
-
-import com.android.systemui.util.condition.Condition;
-import com.android.systemui.util.condition.Monitor;
-
-import java.util.Set;
-
-import dagger.BindsInstance;
-import dagger.Subcomponent;
-
-/**
- * Component for {@link Monitor}.
- */
-@Subcomponent
-public interface MonitorComponent {
- /**
- * Factory for {@link MonitorComponent}.
- */
- @Subcomponent.Factory
- interface Factory {
- MonitorComponent create(@BindsInstance Set<Condition> conditions,
- @BindsInstance Set<Monitor.Callback> callbacks);
- }
-
- /**
- * Provides {@link Monitor}.
- * @return
- */
- Monitor getMonitor();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
index 7892d6eec98d..981bf01164e3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
@@ -18,7 +18,6 @@ package com.android.systemui.util.dagger;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.RingerModeTrackerImpl;
-import com.android.systemui.util.condition.dagger.MonitorComponent;
import com.android.systemui.util.wrapper.UtilWrapperModule;
import dagger.Binds;
@@ -27,9 +26,6 @@ import dagger.Module;
/** Dagger Module for code in the util package. */
@Module(includes = {
UtilWrapperModule.class
- },
- subcomponents = {
- MonitorComponent.class,
})
public interface UtilModule {
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
new file mode 100644
index 000000000000..05d087e232ba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
@@ -0,0 +1,41 @@
+package com.android.systemui.util.kotlin
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+
+/** Providers for various coroutines-related constructs. */
+@Module
+object CoroutinesModule {
+ @Provides
+ @SysUISingleton
+ @Application
+ fun applicationScope(
+ @Main dispatcher: CoroutineDispatcher,
+ ): CoroutineScope = CoroutineScope(dispatcher)
+
+ @Provides
+ @SysUISingleton
+ @Main
+ fun mainDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate
+
+ /**
+ * Provide a [CoroutineDispatcher] backed by a thread pool containing at most X threads, where
+ * X is the number of CPU cores available.
+ *
+ * Because there are multiple threads at play, there is no serialization order guarantee. You
+ * should use a [kotlinx.coroutines.channels.Channel] for serialization if necessary.
+ *
+ * @see Dispatchers.Default
+ */
+ @Provides
+ @SysUISingleton
+ @Background
+ fun bgDispatcher(): CoroutineDispatcher = Dispatchers.Default
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
index c57dbe37ca5f..064c224b0568 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java
@@ -155,11 +155,6 @@ public class ObservableServiceConnection<T> implements ServiceConnection {
* Disconnect from the service if bound.
*/
public void unbind() {
- if (!mBoundCalled) {
- return;
- }
- mBoundCalled = false;
- mContext.unbindService(this);
onDisconnected(DISCONNECT_REASON_UNBIND);
}
@@ -210,12 +205,15 @@ public class ObservableServiceConnection<T> implements ServiceConnection {
Log.d(TAG, "onDisconnected:" + reason);
}
+ // If not bound or already unbound, do not proceed setting reason, unbinding, and
+ // notifying
if (!mBoundCalled) {
return;
}
+ mBoundCalled = false;
mLastDisconnectReason = Optional.of(reason);
- unbind();
+ mContext.unbindService(this);
mProxy = null;
applyToCallbacksLocked(callback-> callback.onDisconnected(this,
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
index 292c062369c1..6e19bed49626 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
@@ -72,6 +72,11 @@ public class PersistentConnectionManager<T> {
@Override
public void onDisconnected(ObservableServiceConnection connection, int reason) {
+ // Do not attempt to reconnect if we were manually unbound
+ if (reason == ObservableServiceConnection.DISCONNECT_REASON_UNBIND) {
+ return;
+ }
+
if (mSystemClock.currentTimeMillis() - mStartTime > mMinConnectionDuration) {
initiateConnectionAttempt();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 83fc28189db5..708a8ab4fcf6 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -21,14 +21,10 @@ import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
-import static android.service.notification.NotificationListenerService.REASON_CANCEL;
-import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
-import static android.service.notification.NotificationListenerService.REASON_CLICK;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -38,7 +34,6 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.UserInfo;
-import android.content.res.Configuration;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -55,7 +50,6 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
@@ -63,9 +57,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationChannelHelper;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -78,7 +70,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.bubbles.Bubble;
@@ -113,7 +105,6 @@ public class BubblesManager implements Dumpable {
private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final NotificationLockscreenUserManager mNotifUserManager;
private final NotificationGroupManagerLegacy mNotificationGroupManager;
- private final NotificationEntryManager mNotificationEntryManager;
private final CommonNotifCollection mCommonNotifCollection;
private final NotifPipeline mNotifPipeline;
private final Executor mSysuiMainExecutor;
@@ -121,6 +112,7 @@ public class BubblesManager implements Dumpable {
private final Bubbles.SysuiProxy mSysuiProxy;
// TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
private final List<NotifCallback> mCallbacks = new ArrayList<>();
+ private final StatusBarWindowCallback mStatusBarWindowCallback;
/**
* Creates {@link BubblesManager}, returns {@code null} if Optional {@link Bubbles} not present
@@ -132,7 +124,6 @@ public class BubblesManager implements Dumpable {
NotificationShadeWindowController notificationShadeWindowController,
KeyguardStateController keyguardStateController,
ShadeController shadeController,
- ConfigurationController configurationController,
@Nullable IStatusBarService statusBarService,
INotificationManager notificationManager,
NotificationVisibilityProvider visibilityProvider,
@@ -140,11 +131,9 @@ public class BubblesManager implements Dumpable {
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManagerLegacy groupManager,
- NotificationEntryManager entryManager,
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
- NotifPipelineFlags notifPipelineFlags,
DumpManager dumpManager,
Executor sysuiMainExecutor) {
if (bubblesOptional.isPresent()) {
@@ -153,7 +142,6 @@ public class BubblesManager implements Dumpable {
notificationShadeWindowController,
keyguardStateController,
shadeController,
- configurationController,
statusBarService,
notificationManager,
visibilityProvider,
@@ -161,11 +149,9 @@ public class BubblesManager implements Dumpable {
zenModeController,
notifUserManager,
groupManager,
- entryManager,
notifCollection,
notifPipeline,
sysUiState,
- notifPipelineFlags,
dumpManager,
sysuiMainExecutor);
} else {
@@ -179,7 +165,6 @@ public class BubblesManager implements Dumpable {
NotificationShadeWindowController notificationShadeWindowController,
KeyguardStateController keyguardStateController,
ShadeController shadeController,
- ConfigurationController configurationController,
@Nullable IStatusBarService statusBarService,
INotificationManager notificationManager,
NotificationVisibilityProvider visibilityProvider,
@@ -187,11 +172,9 @@ public class BubblesManager implements Dumpable {
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManagerLegacy groupManager,
- NotificationEntryManager entryManager,
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
- NotifPipelineFlags notifPipelineFlags,
DumpManager dumpManager,
Executor sysuiMainExecutor) {
mContext = context;
@@ -203,7 +186,6 @@ public class BubblesManager implements Dumpable {
mNotificationInterruptStateProvider = interruptionStateProvider;
mNotifUserManager = notifUserManager;
mNotificationGroupManager = groupManager;
- mNotificationEntryManager = entryManager;
mCommonNotifCollection = notifCollection;
mNotifPipeline = notifPipeline;
mSysuiMainExecutor = sysuiMainExecutor;
@@ -213,11 +195,7 @@ public class BubblesManager implements Dumpable {
ServiceManager.getService(Context.STATUS_BAR_SERVICE))
: statusBarService;
- if (notifPipelineFlags.isNewPipelineEnabled()) {
- setupNotifPipeline();
- } else {
- setupNEM();
- }
+ setupNotifPipeline();
dumpManager.registerDumpable(TAG, this);
@@ -230,23 +208,6 @@ public class BubblesManager implements Dumpable {
}
});
- configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
- @Override
- public void onConfigChanged(Configuration newConfig) {
- mBubbles.onConfigChanged(newConfig);
- }
-
- @Override
- public void onUiModeChanged() {
- mBubbles.updateForThemeChanges();
- }
-
- @Override
- public void onThemeChanged() {
- mBubbles.updateForThemeChanges();
- }
- });
-
zenModeController.addCallback(new ZenModeController.Callback() {
@Override
public void onZenChanged(int zen) {
@@ -278,9 +239,15 @@ public class BubblesManager implements Dumpable {
});
+ // Store callback in a field so it won't get GC'd
+ mStatusBarWindowCallback =
+ (keyguardShowing, keyguardOccluded, bouncerShowing, isDozing, panelExpanded) ->
+ mBubbles.onNotificationPanelExpandedChanged(panelExpanded);
+ notificationShadeWindowController.registerCallback(mStatusBarWindowCallback);
+
mSysuiProxy = new Bubbles.SysuiProxy() {
@Override
- public void isNotificationShadeExpand(Consumer<Boolean> callback) {
+ public void isNotificationPanelExpand(Consumer<Boolean> callback) {
sysuiMainExecutor.execute(() -> {
callback.accept(mNotificationShadeWindowController.getPanelExpanded());
});
@@ -430,141 +397,6 @@ public class BubblesManager implements Dumpable {
mBubbles.setSysuiProxy(mSysuiProxy);
}
- private void setupNEM() {
- mNotificationEntryManager.addNotificationEntryListener(
- new NotificationEntryListener() {
- @Override
- public void onPendingEntryAdded(NotificationEntry entry) {
- BubblesManager.this.onEntryAdded(entry);
- }
-
- @Override
- public void onPreEntryUpdated(NotificationEntry entry) {
- BubblesManager.this.onEntryUpdated(entry);
- }
-
- @Override
- public void onEntryRemoved(
- NotificationEntry entry,
- @Nullable NotificationVisibility visibility,
- boolean removedByUser,
- int reason) {
- BubblesManager.this.onEntryRemoved(entry);
- }
-
- @Override
- public void onNotificationRankingUpdated(RankingMap rankingMap) {
- BubblesManager.this.onRankingUpdate(rankingMap);
- }
-
- @Override
- public void onNotificationChannelModified(
- String pkgName,
- UserHandle user,
- NotificationChannel channel,
- int modificationType) {
- BubblesManager.this.onNotificationChannelModified(pkgName,
- user,
- channel,
- modificationType);
- }
- });
-
- // The new pipeline takes care of this as a NotifDismissInterceptor BubbleCoordinator
- mNotificationEntryManager.addNotificationRemoveInterceptor(
- (key, entry, dismissReason) -> {
- final boolean isClearAll = dismissReason == REASON_CANCEL_ALL;
- final boolean isUserDismiss = dismissReason == REASON_CANCEL
- || dismissReason == REASON_CLICK;
- final boolean isAppCancel = dismissReason == REASON_APP_CANCEL
- || dismissReason == REASON_APP_CANCEL_ALL;
- final boolean isSummaryCancel =
- dismissReason == REASON_GROUP_SUMMARY_CANCELED;
-
- // Need to check for !appCancel here because the notification may have
- // previously been dismissed & entry.isRowDismissed would still be true
- boolean userRemovedNotif =
- (entry != null && entry.isRowDismissed() && !isAppCancel)
- || isClearAll || isUserDismiss || isSummaryCancel;
-
- if (userRemovedNotif) {
- return handleDismissalInterception(entry);
- }
- return false;
- });
-
- mNotificationGroupManager.registerGroupChangeListener(
- new NotificationGroupManagerLegacy.OnGroupChangeListener() {
- @Override
- public void onGroupSuppressionChanged(
- NotificationGroupManagerLegacy.NotificationGroup group,
- boolean suppressed) {
- // More notifications could be added causing summary to no longer
- // be suppressed -- in this case need to remove the key.
- final String groupKey = group.summary != null
- ? group.summary.getSbn().getGroupKey()
- : null;
- if (!suppressed && groupKey != null) {
- mBubbles.removeSuppressedSummaryIfNecessary(groupKey, null, null);
- }
- }
- });
-
- addNotifCallback(new NotifCallback() {
- @Override
- public void removeNotification(NotificationEntry entry,
- DismissedByUserStats dismissedByUserStats, int reason) {
- mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
- dismissedByUserStats, reason);
- }
-
- @Override
- public void invalidateNotifications(String reason) {
- mNotificationEntryManager.updateNotifications(reason);
- }
-
- @Override
- public void maybeCancelSummary(NotificationEntry entry) {
- // Check if removed bubble has an associated suppressed group summary that needs
- // to be removed now.
- final String groupKey = entry.getSbn().getGroupKey();
- mBubbles.removeSuppressedSummaryIfNecessary(groupKey, (summaryKey) -> {
- final NotificationEntry summary =
- mNotificationEntryManager.getActiveNotificationUnfiltered(summaryKey);
- if (summary != null) {
- mNotificationEntryManager.performRemoveNotification(
- summary.getSbn(),
- getDismissedByUserStats(summary, false),
- UNDEFINED_DISMISS_REASON);
- }
- }, mSysuiMainExecutor);
-
- // Check if we still need to remove the summary from NoManGroup because the summary
- // may not be in the mBubbleData.mSuppressedGroupKeys list and removed above.
- // For example:
- // 1. Bubbled notifications (group) is posted to shade and are visible bubbles
- // 2. User expands bubbles so now their respective notifications in the shade are
- // hidden, including the group summary
- // 3. User removes all bubbles
- // 4. We expect all the removed bubbles AND the summary (note: the summary was
- // never added to the suppressedSummary list in BubbleData, so we add this check)
- NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary(entry);
- if (summary != null) {
- ArrayList<NotificationEntry> summaryChildren =
- mNotificationGroupManager.getLogicalChildren(summary.getSbn());
- boolean isSummaryThisNotif = summary.getKey().equals(entry.getKey());
- if (!isSummaryThisNotif && (summaryChildren == null
- || summaryChildren.isEmpty())) {
- mNotificationEntryManager.performRemoveNotification(
- summary.getSbn(),
- getDismissedByUserStats(summary, false),
- UNDEFINED_DISMISS_REASON);
- }
- }
- }
- });
- }
-
private void setupNotifPipeline() {
mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index a6db2aa48a88..12597e0896b1 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -47,7 +47,6 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -55,10 +54,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
-import com.android.wm.shell.ShellCommandHandler;
-import com.android.wm.shell.compatui.CompatUI;
-import com.android.wm.shell.draganddrop.DragAndDrop;
-import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.nano.WmShellTraceProto;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedEventCallback;
@@ -67,6 +62,7 @@ import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.protolog.ShellProtoLogImpl;
import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.sysui.ShellInterface;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -106,19 +102,15 @@ public final class WMShell extends CoreStartable
| SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
// Shell interfaces
+ private final ShellInterface mShell;
private final Optional<Pip> mPipOptional;
private final Optional<SplitScreen> mSplitScreenOptional;
private final Optional<OneHanded> mOneHandedOptional;
- private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
- private final Optional<ShellCommandHandler> mShellCommandHandler;
- private final Optional<CompatUI> mCompatUIOptional;
- private final Optional<DragAndDrop> mDragAndDropOptional;
private final CommandQueue mCommandQueue;
private final ConfigurationController mConfigurationController;
private final KeyguardStateController mKeyguardStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final NavigationModeController mNavigationModeController;
private final ScreenLifecycle mScreenLifecycle;
private final SysUiState mSysUiState;
private final WakefulnessLifecycle mWakefulnessLifecycle;
@@ -127,26 +119,19 @@ public final class WMShell extends CoreStartable
private final Executor mSysUiMainExecutor;
private boolean mIsSysUiStateValid;
- private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
- private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
- private KeyguardStateController.Callback mCompatUIKeyguardCallback;
private WakefulnessLifecycle.Observer mWakefulnessObserver;
@Inject
public WMShell(Context context,
+ ShellInterface shell,
Optional<Pip> pipOptional,
Optional<SplitScreen> splitScreenOptional,
Optional<OneHanded> oneHandedOptional,
- Optional<HideDisplayCutout> hideDisplayCutoutOptional,
- Optional<ShellCommandHandler> shellCommandHandler,
- Optional<CompatUI> sizeCompatUIOptional,
- Optional<DragAndDrop> dragAndDropOptional,
CommandQueue commandQueue,
ConfigurationController configurationController,
KeyguardStateController keyguardStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- NavigationModeController navigationModeController,
ScreenLifecycle screenLifecycle,
SysUiState sysUiState,
ProtoTracer protoTracer,
@@ -154,28 +139,49 @@ public final class WMShell extends CoreStartable
UserInfoController userInfoController,
@Main Executor sysUiMainExecutor) {
super(context);
+ mShell = shell;
mCommandQueue = commandQueue;
mConfigurationController = configurationController;
mKeyguardStateController = keyguardStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mNavigationModeController = navigationModeController;
mScreenLifecycle = screenLifecycle;
mSysUiState = sysUiState;
mPipOptional = pipOptional;
mSplitScreenOptional = splitScreenOptional;
mOneHandedOptional = oneHandedOptional;
- mHideDisplayCutoutOptional = hideDisplayCutoutOptional;
mWakefulnessLifecycle = wakefulnessLifecycle;
mProtoTracer = protoTracer;
- mShellCommandHandler = shellCommandHandler;
- mCompatUIOptional = sizeCompatUIOptional;
- mDragAndDropOptional = dragAndDropOptional;
mUserInfoController = userInfoController;
mSysUiMainExecutor = sysUiMainExecutor;
}
@Override
public void start() {
+ // Notify with the initial configuration and subscribe for new config changes
+ mShell.onConfigurationChanged(mContext.getResources().getConfiguration());
+ mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ mShell.onConfigurationChanged(newConfig);
+ }
+ });
+
+ // Subscribe to keyguard changes
+ mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
+ mShell.onKeyguardVisibilityChanged(mKeyguardStateController.isShowing(),
+ mKeyguardStateController.isOccluded(),
+ mKeyguardStateController.isAnimatingBetweenKeyguardAndSurfaceBehind());
+ }
+ });
+ mKeyguardUpdateMonitor.registerCallback(new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onKeyguardDismissAnimationFinished() {
+ mShell.onKeyguardDismissAnimationFinished();
+ }
+ });
+
// TODO: Consider piping config change and other common calls to a shell component to
// delegate internally
mProtoTracer.add(this);
@@ -183,9 +189,6 @@ public final class WMShell extends CoreStartable
mPipOptional.ifPresent(this::initPip);
mSplitScreenOptional.ifPresent(this::initSplitScreen);
mOneHandedOptional.ifPresent(this::initOneHanded);
- mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
- mCompatUIOptional.ifPresent(this::initCompatUi);
- mDragAndDropOptional.ifPresent(this::initDragAndDrop);
}
@VisibleForTesting
@@ -197,42 +200,11 @@ public final class WMShell extends CoreStartable
}
});
- mPipKeyguardCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onKeyguardVisibilityChanged(boolean showing) {
- pip.onKeyguardVisibilityChanged(showing,
- mKeyguardStateController.isAnimatingBetweenKeyguardAndSurfaceBehind());
- }
-
- @Override
- public void onKeyguardDismissAnimationFinished() {
- pip.onKeyguardDismissAnimationFinished();
- }
- };
- mKeyguardUpdateMonitor.registerCallback(mPipKeyguardCallback);
-
mSysUiState.addCallback(sysUiStateFlag -> {
mIsSysUiStateValid = (sysUiStateFlag & INVALID_SYSUI_STATE_MASK) == 0;
pip.onSystemUiStateChanged(mIsSysUiStateValid, sysUiStateFlag);
});
- mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
- @Override
- public void onConfigChanged(Configuration newConfig) {
- pip.onConfigurationChanged(newConfig);
- }
-
- @Override
- public void onDensityOrFontScaleChanged() {
- pip.onDensityOrFontScaleChanged();
- }
-
- @Override
- public void onThemeChanged() {
- pip.onOverlayChanged();
- }
- });
-
// The media session listener needs to be re-registered when switching users
mUserInfoController.addCallback((String name, Drawable picture, String userAccount) ->
pip.registerSessionListenerForCurrentUser());
@@ -240,14 +212,6 @@ public final class WMShell extends CoreStartable
@VisibleForTesting
void initSplitScreen(SplitScreen splitScreen) {
- mSplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onKeyguardVisibilityChanged(boolean showing) {
- splitScreen.onKeyguardVisibilityChanged(showing);
- }
- };
- mKeyguardUpdateMonitor.registerCallback(mSplitScreenKeyguardCallback);
-
mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() {
@Override
public void onFinishedWakingUp() {
@@ -293,14 +257,9 @@ public final class WMShell extends CoreStartable
}
});
+ // TODO: Either move into ShellInterface or register a receiver on the Shell side directly
mOneHandedKeyguardCallback = new KeyguardUpdateMonitorCallback() {
@Override
- public void onKeyguardVisibilityChanged(boolean showing) {
- oneHanded.onKeyguardVisibilityChanged(showing);
- oneHanded.stopOneHanded();
- }
-
- @Override
public void onUserSwitchComplete(int userId) {
oneHanded.onUserSwitch(userId);
}
@@ -348,48 +307,6 @@ public final class WMShell extends CoreStartable
}
}
});
-
- mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
- @Override
- public void onConfigChanged(Configuration newConfig) {
- oneHanded.onConfigChanged(newConfig);
- }
- });
- }
-
- @VisibleForTesting
- void initHideDisplayCutout(HideDisplayCutout hideDisplayCutout) {
- mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
- @Override
- public void onConfigChanged(Configuration newConfig) {
- hideDisplayCutout.onConfigurationChanged(newConfig);
- }
- });
- }
-
- @VisibleForTesting
- void initCompatUi(CompatUI sizeCompatUI) {
- mCompatUIKeyguardCallback = new KeyguardStateController.Callback() {
- @Override
- public void onKeyguardShowingChanged() {
- sizeCompatUI.onKeyguardShowingChanged(mKeyguardStateController.isShowing());
- }
- };
- mKeyguardStateController.addCallback(mCompatUIKeyguardCallback);
- }
-
- void initDragAndDrop(DragAndDrop dragAndDrop) {
- mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
- @Override
- public void onConfigChanged(Configuration newConfig) {
- dragAndDrop.onConfigChanged(newConfig);
- }
-
- @Override
- public void onThemeChanged() {
- dragAndDrop.onThemeChanged();
- }
- });
}
@Override
@@ -404,8 +321,7 @@ public final class WMShell extends CoreStartable
@Override
public void dump(PrintWriter pw, String[] args) {
// Handle commands if provided
- if (mShellCommandHandler.isPresent()
- && mShellCommandHandler.get().handleCommand(args, pw)) {
+ if (mShell.handleCommand(args, pw)) {
return;
}
// Handle logging commands if provided
@@ -413,8 +329,7 @@ public final class WMShell extends CoreStartable
return;
}
// Dump WMShell stuff here if no commands were handled
- mShellCommandHandler.ifPresent(
- shellCommandHandler -> shellCommandHandler.dump(pw));
+ mShell.dump(pw);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
index ac1a83c269e0..4021652295c1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -31,6 +32,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
import android.view.Gravity;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -42,6 +44,7 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -104,6 +107,17 @@ public class KeyguardHostViewControllerTest extends SysuiTestCase {
}
@Test
+ public void onBouncerVisible_propagatesToKeyguardSecurityContainerController() {
+ mKeyguardHostViewController.onBouncerVisibilityChanged(ViewGroup.VISIBLE);
+ mKeyguardHostViewController.onBouncerVisibilityChanged(ViewGroup.INVISIBLE);
+
+ InOrder order = inOrder(mKeyguardSecurityContainerController);
+ order.verify(mKeyguardSecurityContainerController).onBouncerVisibilityChanged(View.VISIBLE);
+ order.verify(mKeyguardSecurityContainerController).onBouncerVisibilityChanged(
+ View.INVISIBLE);
+ }
+
+ @Test
public void testGravityReappliedOnConfigurationChange() {
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
index b1e2012ecd40..ad6d146fb5f3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -92,7 +92,6 @@ private fun faceModel(user: Int) = KeyguardFaceListenModel(
keyguardAwake = false,
keyguardGoingAway = false,
listeningForFaceAssistant = false,
- lockIconPressed = false,
occludingAppRequestingFaceAuth = false,
primaryUser = false,
scanningAllowedByStrongAuth = false,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
index 1518ea160016..8293cc21f3d1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -17,11 +17,13 @@
package com.android.keyguard;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -90,4 +92,19 @@ public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
mMessageAreaController.setBouncerShowing(true);
verify(mKeyguardMessageArea).setBouncerShowing(true);
}
+
+ @Test
+ public void testSetMessageIfEmpty_empty() {
+ mMessageAreaController.setMessage("");
+ mMessageAreaController.setMessageIfEmpty(R.string.keyguard_enter_your_pin);
+ verify(mKeyguardMessageArea).setMessage(R.string.keyguard_enter_your_pin);
+ }
+
+ @Test
+ public void testSetMessageIfEmpty_notEmpty() {
+ mMessageAreaController.setMessage("abc");
+ mMessageAreaController.setMessageIfEmpty(R.string.keyguard_enter_your_pin);
+ verify(mKeyguardMessageArea, never()).setMessage(getContext()
+ .getResources().getText(R.string.keyguard_enter_your_pin));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index a1e23a201b72..ec856031c23c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -73,25 +73,25 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- Mockito.`when`(keyguardPasswordView
- .findViewById<KeyguardMessageArea>(R.id.keyguard_message_area))
- .thenReturn(mKeyguardMessageArea)
+ Mockito.`when`(
+ keyguardPasswordView.findViewById<KeyguardMessageArea>(R.id.keyguard_message_area)
+ ).thenReturn(mKeyguardMessageArea)
Mockito.`when`(messageAreaControllerFactory.create(mKeyguardMessageArea))
- .thenReturn(mKeyguardMessageAreaController)
+ .thenReturn(mKeyguardMessageAreaController)
keyguardPasswordViewController = KeyguardPasswordViewController(
- keyguardPasswordView,
- keyguardUpdateMonitor,
- securityMode,
- lockPatternUtils,
- keyguardSecurityCallback,
- messageAreaControllerFactory,
- latencyTracker,
- inputMethodManager,
- emergencyButtonController,
- mainExecutor,
- mContext.resources,
- falsingCollector,
- keyguardViewController
+ keyguardPasswordView,
+ keyguardUpdateMonitor,
+ securityMode,
+ lockPatternUtils,
+ keyguardSecurityCallback,
+ messageAreaControllerFactory,
+ latencyTracker,
+ inputMethodManager,
+ emergencyButtonController,
+ mainExecutor,
+ mContext.resources,
+ falsingCollector,
+ keyguardViewController
)
}
@@ -110,4 +110,11 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() {
keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED)
verify(keyguardPasswordView, never()).requestFocus()
}
-} \ No newline at end of file
+
+ @Test
+ fun onResume_testSetInitialText() {
+ keyguardPasswordViewController.onResume(KeyguardSecurityView.SCREEN_ON)
+ verify(mKeyguardMessageAreaController)
+ .setMessageIfEmpty(R.string.keyguard_enter_your_password)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 8e1e42a9c9a2..616a1056d161 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -31,8 +31,8 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -41,28 +41,39 @@ import org.mockito.MockitoAnnotations
class KeyguardPatternViewControllerTest : SysuiTestCase() {
@Mock
private lateinit var mKeyguardPatternView: KeyguardPatternView
+
@Mock
private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor
+
@Mock
private lateinit var mSecurityMode: KeyguardSecurityModel.SecurityMode
+
@Mock
private lateinit var mLockPatternUtils: LockPatternUtils
+
@Mock
private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
+
@Mock
private lateinit var mLatencyTracker: LatencyTracker
private var mFalsingCollector: FalsingCollector = FalsingCollectorFake()
+
@Mock
private lateinit var mEmergencyButtonController: EmergencyButtonController
+
@Mock
private lateinit
var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
+
@Mock
private lateinit var mKeyguardMessageArea: KeyguardMessageArea
+
@Mock
private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController
+
@Mock
private lateinit var mLockPatternView: LockPatternView
+
@Mock
private lateinit var mPostureController: DevicePostureController
@@ -73,15 +84,17 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
`when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true)
`when`(mKeyguardPatternView.findViewById<KeyguardMessageArea>(R.id.keyguard_message_area))
- .thenReturn(mKeyguardMessageArea)
+ .thenReturn(mKeyguardMessageArea)
`when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView))
- .thenReturn(mLockPatternView)
+ .thenReturn(mLockPatternView)
`when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea))
- .thenReturn(mKeyguardMessageAreaController)
- mKeyguardPatternViewController = KeyguardPatternViewController(mKeyguardPatternView,
- mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
- mLatencyTracker, mFalsingCollector, mEmergencyButtonController,
- mKeyguardMessageAreaControllerFactory, mPostureController)
+ .thenReturn(mKeyguardMessageAreaController)
+ mKeyguardPatternViewController = KeyguardPatternViewController(
+ mKeyguardPatternView,
+ mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
+ mLatencyTracker, mFalsingCollector, mEmergencyButtonController,
+ mKeyguardMessageAreaControllerFactory, mPostureController
+ )
}
@Test
@@ -90,4 +103,11 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
mKeyguardPatternViewController.onPause()
verify(mKeyguardMessageAreaController).setMessage("")
}
+
+ @Test
+ fun onResume_setInitialText() {
+ mKeyguardPatternViewController.onResume(KeyguardSecurityView.SCREEN_ON)
+ verify(mKeyguardMessageAreaController)
+ .setMessageIfEmpty(R.string.keyguard_enter_your_pattern)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 9597cabb2eb8..7bc8e8a722f0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -113,5 +113,11 @@ public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
mKeyguardPinViewController.onResume(KeyguardSecurityView.SCREEN_ON);
verify(mPasswordEntry).requestFocus();
}
+
+ @Test
+ public void onResume_setInitialText() {
+ mKeyguardPinViewController.onResume(KeyguardSecurityView.SCREEN_ON);
+ verify(mKeyguardMessageAreaController).setMessageIfEmpty(R.string.keyguard_enter_your_pin);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 4d3343059718..68e49c0a1d4b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -34,9 +35,11 @@ import static org.mockito.Mockito.when;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.hardware.biometrics.BiometricSourceType;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
+import android.view.View;
import android.view.WindowInsetsController;
import androidx.test.filters.SmallTest;
@@ -47,6 +50,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.SidefpsController;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.log.SessionTracker;
@@ -60,16 +64,18 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Optional;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
- private static final int VIEW_WIDTH = 1600;
-
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@@ -104,6 +110,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
@Mock
private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
@Mock
+ private KeyguardMessageAreaController mKeyguardMessageAreaController;
+ @Mock
private KeyguardMessageArea mKeyguardMessageArea;
@Mock
private ConfigurationController mConfigurationController;
@@ -125,6 +133,14 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
private SessionTracker mSessionTracker;
@Mock
private KeyguardViewController mKeyguardViewController;
+ @Mock
+ private SidefpsController mSidefpsController;
+ @Mock
+ private KeyguardPasswordViewController mKeyguardPasswordViewControllerMock;
+
+ @Captor
+ private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback;
+
private Configuration mConfiguration;
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@@ -139,7 +155,6 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
when(mResources.getConfiguration()).thenReturn(mConfiguration);
when(mView.getContext()).thenReturn(mContext);
when(mView.getResources()).thenReturn(mResources);
- when(mView.getWidth()).thenReturn(VIEW_WIDTH);
when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
.thenReturn(mAdminSecondaryLockScreenController);
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
@@ -147,6 +162,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
when(mKeyguardPasswordView.getRootView()).thenReturn(mSecurityViewFlipper);
when(mKeyguardPasswordView.findViewById(R.id.keyguard_message_area))
.thenReturn(mKeyguardMessageArea);
+ when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
+ .thenReturn(mKeyguardMessageAreaController);
when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
mKeyguardPasswordViewController = new KeyguardPasswordViewController(
(KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor,
@@ -160,7 +177,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
mKeyguardStateController, mKeyguardSecurityViewFlipperController,
mConfigurationController, mFalsingCollector, mFalsingManager,
mUserSwitcherController, mFeatureFlags, mGlobalSettings,
- mSessionTracker).create(mSecurityCallback);
+ mSessionTracker, Optional.of(mSidefpsController)).create(mSecurityCallback);
}
@Test
@@ -208,49 +225,26 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
mUserSwitcherController);
}
- private void touchDownLeftSide() {
+ private void touchDown() {
mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent(
MotionEvent.obtain(
/* downTime= */0,
/* eventTime= */0,
MotionEvent.ACTION_DOWN,
- /* x= */VIEW_WIDTH / 3f,
- /* y= */0,
- /* metaState= */0));
- }
-
- private void touchDownRightSide() {
- mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent(
- MotionEvent.obtain(
- /* downTime= */0,
- /* eventTime= */0,
- MotionEvent.ACTION_DOWN,
- /* x= */(VIEW_WIDTH / 3f) * 2,
+ /* x= */0,
/* y= */0,
/* metaState= */0));
}
@Test
- public void onInterceptTap_inhibitsFalsingInOneHandedMode() {
- when(mView.getMode()).thenReturn(MODE_ONE_HANDED);
- when(mView.isOneHandedModeLeftAligned()).thenReturn(true);
+ public void onInterceptTap_inhibitsFalsingInSidedSecurityMode() {
- touchDownLeftSide();
+ when(mView.isTouchOnTheOtherSideOfSecurity(any())).thenReturn(false);
+ touchDown();
verify(mFalsingCollector, never()).avoidGesture();
- // Now on the right.
- touchDownRightSide();
- verify(mFalsingCollector).avoidGesture();
-
- // Move and re-test
- reset(mFalsingCollector);
- when(mView.isOneHandedModeLeftAligned()).thenReturn(false);
-
- // On the right...
- touchDownRightSide();
- verify(mFalsingCollector, never()).avoidGesture();
-
- touchDownLeftSide();
+ when(mView.isTouchOnTheOtherSideOfSecurity(any())).thenReturn(true);
+ touchDown();
verify(mFalsingCollector).avoidGesture();
}
@@ -281,9 +275,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
@Test
public void showSecurityScreen_twoHandedMode_flagEnabled_noOneHandedMode() {
when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(true);
- when(mKeyguardSecurityViewFlipperController.getSecurityView(
- eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
- .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+ setupGetSecurityView();
mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
@@ -299,4 +291,126 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
verify(mUserSwitcherController)
.removeUserSwitchCallback(any(UserSwitcherController.UserSwitchCallback.class));
}
+
+ @Test
+ public void onBouncerVisibilityChanged_allConditionsGood_sideFpsHintShown() {
+ setupConditionsToEnableSideFpsHint();
+ reset(mSidefpsController);
+
+ mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+
+ verify(mSidefpsController).show();
+ verify(mSidefpsController, never()).hide();
+ }
+
+ @Test
+ public void onBouncerVisibilityChanged_fpsSensorNotRunning_sideFpsHintHidden() {
+ setupConditionsToEnableSideFpsHint();
+ setFingerprintDetectionRunning(false);
+ reset(mSidefpsController);
+
+ mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+
+ verify(mSidefpsController).hide();
+ verify(mSidefpsController, never()).show();
+ }
+
+ @Test
+ public void onBouncerVisibilityChanged_withoutSidedSecurity_sideFpsHintHidden() {
+ setupConditionsToEnableSideFpsHint();
+ setSidedSecurityMode(false);
+ reset(mSidefpsController);
+
+ mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+
+ verify(mSidefpsController).hide();
+ verify(mSidefpsController, never()).show();
+ }
+
+ @Test
+ public void onBouncerVisibilityChanged_needsStrongAuth_sideFpsHintHidden() {
+ setupConditionsToEnableSideFpsHint();
+ setNeedsStrongAuth(true);
+ reset(mSidefpsController);
+
+ mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+
+ verify(mSidefpsController).hide();
+ verify(mSidefpsController, never()).show();
+ }
+
+ @Test
+ public void onBouncerVisibilityChanged_sideFpsHintShown_sideFpsHintHidden() {
+ setupGetSecurityView();
+ setupConditionsToEnableSideFpsHint();
+ mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+ verify(mSidefpsController, atLeastOnce()).show();
+ reset(mSidefpsController);
+
+ mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.INVISIBLE);
+
+ verify(mSidefpsController).hide();
+ verify(mSidefpsController, never()).show();
+ }
+
+ @Test
+ public void onStartingToHide_sideFpsHintShown_sideFpsHintHidden() {
+ setupGetSecurityView();
+ setupConditionsToEnableSideFpsHint();
+ mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+ verify(mSidefpsController, atLeastOnce()).show();
+ reset(mSidefpsController);
+
+ mKeyguardSecurityContainerController.onStartingToHide();
+
+ verify(mSidefpsController).hide();
+ verify(mSidefpsController, never()).show();
+ }
+
+ @Test
+ public void onPause_sideFpsHintShown_sideFpsHintHidden() {
+ setupGetSecurityView();
+ setupConditionsToEnableSideFpsHint();
+ mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE);
+ verify(mSidefpsController, atLeastOnce()).show();
+ reset(mSidefpsController);
+
+ mKeyguardSecurityContainerController.onPause();
+
+ verify(mSidefpsController).hide();
+ verify(mSidefpsController, never()).show();
+ }
+
+ private void setupConditionsToEnableSideFpsHint() {
+ attachView();
+ setSidedSecurityMode(true);
+ setFingerprintDetectionRunning(true);
+ setNeedsStrongAuth(false);
+ }
+
+ private void attachView() {
+ mKeyguardSecurityContainerController.onViewAttached();
+ verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture());
+ }
+
+ private void setFingerprintDetectionRunning(boolean running) {
+ when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(running);
+ mKeyguardUpdateMonitorCallback.getValue().onBiometricRunningStateChanged(running,
+ BiometricSourceType.FINGERPRINT);
+ }
+
+ private void setSidedSecurityMode(boolean sided) {
+ when(mView.isSidedSecurityMode()).thenReturn(sided);
+ }
+
+ private void setNeedsStrongAuth(boolean needed) {
+ when(mKeyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(needed);
+ mKeyguardUpdateMonitorCallback.getValue().onStrongAuthStateChanged(/* userId= */ 0);
+ }
+
+ private void setupGetSecurityView() {
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ any(), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewControllerMock);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index d49f4d8172dc..f2ac0c7a7736 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -16,6 +16,11 @@
package com.android.keyguard;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE;
+import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
+import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
@@ -25,7 +30,9 @@ import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -34,14 +41,13 @@ import static org.mockito.Mockito.when;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Insets;
-import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Gravity;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
-import android.view.WindowInsetsController;
import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
@@ -70,6 +76,9 @@ import java.util.ArrayList;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerTest extends SysuiTestCase {
+
+ private static final int VIEW_WIDTH = 1600;
+
private int mScreenWidth;
private int mFakeMeasureSpec;
@@ -77,8 +86,6 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
public MockitoRule mRule = MockitoJUnit.rule();
@Mock
- private WindowInsetsController mWindowInsetsController;
- @Mock
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
@Mock
private GlobalSettings mGlobalSettings;
@@ -102,7 +109,6 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
mSecurityViewFlipperLayoutParams = new FrameLayout.LayoutParams(
MATCH_PARENT, MATCH_PARENT);
- when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
@@ -212,14 +218,12 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
mKeyguardSecurityContainer.updatePositionByTouchX(
mKeyguardSecurityContainer.getWidth() - 1f);
- verify(mGlobalSettings).putInt(Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
- Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
- verify(mSecurityViewFlipper).setTranslationX(
+ verify(mGlobalSettings).putInt(ONE_HANDED_KEYGUARD_SIDE, ONE_HANDED_KEYGUARD_SIDE_RIGHT);
+ assertSecurityTranslationX(
mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
mKeyguardSecurityContainer.updatePositionByTouchX(1f);
- verify(mGlobalSettings).putInt(Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
- Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT);
+ verify(mGlobalSettings).putInt(ONE_HANDED_KEYGUARD_SIDE, ONE_HANDED_KEYGUARD_SIDE_LEFT);
verify(mSecurityViewFlipper).setTranslationX(0.0f);
}
@@ -237,49 +241,43 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
}
@Test
- public void testUserSwitcherModeViewGravityLandscape() {
+ public void testUserSwitcherModeViewPositionLandscape() {
// GIVEN one user has been setup and in landscape
when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
- Configuration config = new Configuration();
- config.orientation = Configuration.ORIENTATION_LANDSCAPE;
- when(getContext().getResources().getConfiguration()).thenReturn(config);
+ Configuration landscapeConfig = configuration(ORIENTATION_LANDSCAPE);
+ when(getContext().getResources().getConfiguration()).thenReturn(landscapeConfig);
// WHEN UserSwitcherViewMode is initialized and config has changed
setupUserSwitcher();
- reset(mSecurityViewFlipper);
- when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
- mKeyguardSecurityContainer.onConfigurationChanged(config);
+ mKeyguardSecurityContainer.onConfigurationChanged(landscapeConfig);
// THEN views are oriented side by side
- verify(mSecurityViewFlipper).setLayoutParams(mLayoutCaptor.capture());
- assertThat(mLayoutCaptor.getValue().gravity).isEqualTo(Gravity.RIGHT | Gravity.BOTTOM);
- ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
- R.id.keyguard_bouncer_user_switcher);
- assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
- .isEqualTo(Gravity.LEFT | Gravity.CENTER_VERTICAL);
+ assertSecurityGravity(Gravity.LEFT | Gravity.BOTTOM);
+ assertUserSwitcherGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
+ assertSecurityTranslationX(
+ mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
+ assertUserSwitcherTranslationX(0f);
+
}
@Test
public void testUserSwitcherModeViewGravityPortrait() {
// GIVEN one user has been setup and in landscape
when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
- Configuration config = new Configuration();
- config.orientation = Configuration.ORIENTATION_PORTRAIT;
- when(getContext().getResources().getConfiguration()).thenReturn(config);
+ Configuration portraitConfig = configuration(ORIENTATION_PORTRAIT);
+ when(getContext().getResources().getConfiguration()).thenReturn(portraitConfig);
// WHEN UserSwitcherViewMode is initialized and config has changed
setupUserSwitcher();
reset(mSecurityViewFlipper);
when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
- mKeyguardSecurityContainer.onConfigurationChanged(config);
+ mKeyguardSecurityContainer.onConfigurationChanged(portraitConfig);
// THEN views are both centered horizontally
- verify(mSecurityViewFlipper).setLayoutParams(mLayoutCaptor.capture());
- assertThat(mLayoutCaptor.getValue().gravity).isEqualTo(Gravity.CENTER_HORIZONTAL);
- ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
- R.id.keyguard_bouncer_user_switcher);
- assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
- .isEqualTo(Gravity.CENTER_HORIZONTAL);
+ assertSecurityGravity(Gravity.CENTER_HORIZONTAL);
+ assertUserSwitcherGravity(Gravity.CENTER_HORIZONTAL);
+ assertSecurityTranslationX(0);
+ assertUserSwitcherTranslationX(0);
}
@Test
@@ -310,9 +308,102 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
assertThat(anchor.isClickable()).isTrue();
}
+ @Test
+ public void testTouchesAreRecognizedAsBeingOnTheOtherSideOfSecurity() {
+ setupUserSwitcher();
+ setViewWidth(VIEW_WIDTH);
+
+ // security is on the right side by default
+ assertThat(mKeyguardSecurityContainer.isTouchOnTheOtherSideOfSecurity(
+ touchEventLeftSide())).isTrue();
+ assertThat(mKeyguardSecurityContainer.isTouchOnTheOtherSideOfSecurity(
+ touchEventRightSide())).isFalse();
+
+ // move security to the left side
+ when(mGlobalSettings.getInt(any(), anyInt())).thenReturn(ONE_HANDED_KEYGUARD_SIDE_LEFT);
+ mKeyguardSecurityContainer.onConfigurationChanged(new Configuration());
+
+ assertThat(mKeyguardSecurityContainer.isTouchOnTheOtherSideOfSecurity(
+ touchEventLeftSide())).isFalse();
+ assertThat(mKeyguardSecurityContainer.isTouchOnTheOtherSideOfSecurity(
+ touchEventRightSide())).isTrue();
+ }
+
+ @Test
+ public void testSecuritySwitchesSidesInLandscapeUserSwitcherMode() {
+ when(getContext().getResources().getConfiguration())
+ .thenReturn(configuration(ORIENTATION_LANDSCAPE));
+ setupUserSwitcher();
+
+ // switch sides
+ when(mGlobalSettings.getInt(any(), anyInt())).thenReturn(ONE_HANDED_KEYGUARD_SIDE_LEFT);
+ mKeyguardSecurityContainer.onConfigurationChanged(new Configuration());
+
+ assertSecurityTranslationX(0);
+ assertUserSwitcherTranslationX(
+ mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
+ }
+
+ private Configuration configuration(@Configuration.Orientation int orientation) {
+ Configuration config = new Configuration();
+ config.orientation = orientation;
+ return config;
+ }
+
+ private void assertSecurityTranslationX(float translation) {
+ verify(mSecurityViewFlipper).setTranslationX(translation);
+ }
+
+ private void assertUserSwitcherTranslationX(float translation) {
+ ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
+ R.id.keyguard_bouncer_user_switcher);
+ assertThat(userSwitcher.getTranslationX()).isEqualTo(translation);
+ }
+
+ private void assertUserSwitcherGravity(@Gravity.GravityFlags int gravity) {
+ ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
+ R.id.keyguard_bouncer_user_switcher);
+ assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
+ .isEqualTo(gravity);
+ }
+
+ private void assertSecurityGravity(@Gravity.GravityFlags int gravity) {
+ verify(mSecurityViewFlipper, atLeastOnce()).setLayoutParams(mLayoutCaptor.capture());
+ assertThat(mLayoutCaptor.getValue().gravity).isEqualTo(gravity);
+ }
+
+ private void setViewWidth(int width) {
+ mKeyguardSecurityContainer.setRight(width);
+ mKeyguardSecurityContainer.setLeft(0);
+ }
+
+ private MotionEvent touchEventLeftSide() {
+ return MotionEvent.obtain(
+ /* downTime= */0,
+ /* eventTime= */0,
+ MotionEvent.ACTION_DOWN,
+ /* x= */VIEW_WIDTH / 3f,
+ /* y= */0,
+ /* metaState= */0);
+ }
+
+ private MotionEvent touchEventRightSide() {
+ return MotionEvent.obtain(
+ /* downTime= */0,
+ /* eventTime= */0,
+ MotionEvent.ACTION_DOWN,
+ /* x= */(VIEW_WIDTH / 3f) * 2,
+ /* y= */0,
+ /* metaState= */0);
+ }
+
private void setupUserSwitcher() {
+ when(mGlobalSettings.getInt(any(), anyInt())).thenReturn(ONE_HANDED_KEYGUARD_SIDE_RIGHT);
mKeyguardSecurityContainer.initMode(KeyguardSecurityContainer.MODE_USER_SWITCHER,
mGlobalSettings, mFalsingManager, mUserSwitcherController);
+ // reset mSecurityViewFlipper so setup doesn't influence test verifications
+ reset(mSecurityViewFlipper);
+ when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
}
private ArrayList<UserRecord> buildUserRecords(int count) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 2dc066c8a9db..84903d17852f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -1109,8 +1109,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// THEN face unlock is not running b/c status bar state changes don't cause biometric
// listening state to update
- assertThat(mKeyguardUpdateMonitor.isFaceUnlockRunning(
- KeyguardUpdateMonitor.getCurrentUser())).isEqualTo(false);
+ assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isEqualTo(false);
// WHEN biometric listening state is updated
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
index 5d8e4351cfd9..a0fdc8f1555e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -26,6 +26,8 @@ import com.android.systemui.statusbar.policy.FlashlightController;
import org.junit.Assert;
import org.junit.Test;
+import java.util.concurrent.ExecutionException;
+
@SmallTest
public class DependencyTest extends SysuiTestCase {
@@ -44,10 +46,12 @@ public class DependencyTest extends SysuiTestCase {
}
@Test
- public void testInitDependency() {
+ public void testInitDependency() throws ExecutionException, InterruptedException {
Dependency.clearDependencies();
- Dependency dependency =
- SystemUIFactory.getInstance().getSysUIComponent().createDependency();
+ SystemUIInitializer initializer =
+ SystemUIInitializerFactory.createFromConfigNoAssert(mContext);
+ initializer.init(true);
+ Dependency dependency = initializer.getSysUIComponent().createDependency();
dependency.start();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
index 8c20b248d02c..9179efc9f39f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
@@ -34,6 +34,8 @@ import org.junit.Before;
import org.junit.Rule;
import org.mockito.Mockito;
+import java.util.concurrent.ExecutionException;
+
public abstract class SysuiBaseFragmentTest extends BaseFragmentTest {
public static final Class<?>[] ALL_SUPPORTED_CLASSES = LeakCheckedTest.ALL_SUPPORTED_CLASSES;
@@ -54,10 +56,11 @@ public abstract class SysuiBaseFragmentTest extends BaseFragmentTest {
}
@Before
- public void SysuiSetup() {
- SystemUIFactory.createFromConfig(mContext, true);
- mDependency = new TestableDependency(
- SystemUIFactory.getInstance().getSysUIComponent().createDependency());
+ public void sysuiSetup() throws ExecutionException, InterruptedException {
+ SystemUIInitializer initializer =
+ SystemUIInitializerFactory.createFromConfigNoAssert(mContext);
+ initializer.init(true);
+ mDependency = new TestableDependency(initializer.getSysUIComponent().createDependency());
Dependency.setInstance(mDependency);
// TODO: Figure out another way to give reference to a SysuiTestableContext.
@@ -77,7 +80,6 @@ public abstract class SysuiBaseFragmentTest extends BaseFragmentTest {
public void SysuiTeardown() {
InstrumentationRegistry.registerInstance(mRealInstrumentation,
InstrumentationRegistry.getArguments());
- SystemUIFactory.cleanup();
}
@AfterClass
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 8c7927782d2d..c52ea60f0bfc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -76,9 +76,10 @@ public abstract class SysuiTestCase {
@Before
public void SysuiSetup() throws Exception {
- SystemUIFactory.createFromConfig(mContext, true);
- mDependency = new TestableDependency(
- SystemUIFactory.getInstance().getSysUIComponent().createDependency());
+ SystemUIInitializer initializer =
+ SystemUIInitializerFactory.createFromConfigNoAssert(mContext);
+ initializer.init(true);
+ mDependency = new TestableDependency(initializer.getSysUIComponent().createDependency());
Dependency.setInstance(mDependency);
mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(mContext, mock(Looper.class),
mock(Executor.class), mock(DumpManager.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
index 7e9f84c1ef8c..bebd8712a750 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
@@ -191,6 +191,16 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
}
@Test
+ public void hideMenuViewWhenStartingAnimation_animatorNotRunning() {
+ mMenuView.show();
+
+ mMenuView.mDragAnimator.start();
+ mMenuView.hide();
+
+ assertThat(mMenuView.mDragAnimator.isRunning()).isFalse();
+ }
+
+ @Test
public void onTargetsChanged_singleTarget_expectedRadii() {
final Position alignRightPosition = new Position(1.0f, 0.0f);
final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index fe2efa5ea30d..4218e0904c43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -15,6 +15,7 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.WindowManager
import android.widget.LinearLayout
import androidx.test.filters.SmallTest
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.policy.DecorView
import com.android.systemui.SysuiTestCase
import junit.framework.Assert.assertEquals
@@ -29,6 +30,8 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@SmallTest
@@ -40,12 +43,14 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
private val attachedViews = mutableSetOf<View>()
@Mock lateinit var dreamManager: IDreamManager
+ @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
@get:Rule val rule = MockitoJUnit.rule()
@Before
fun setUp() {
dialogLaunchAnimator = DialogLaunchAnimator(
- dreamManager, launchAnimator, isForTesting = true)
+ dreamManager, interactionJankMonitor, launchAnimator, isForTesting = true
+ )
}
@After
@@ -90,7 +95,8 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
// The dialog content is inside this fake window view.
assertNotNull(
- dialogContentWithBackground.findViewByPredicate { it === dialog.contentView })
+ dialogContentWithBackground.findViewByPredicate { it === dialog.contentView }
+ )
// Clicking the transparent background should dismiss the dialog.
runOnMainThreadAndWaitForIdleSync {
@@ -161,6 +167,32 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
assertNotEquals(0, dialog.window.attributes.windowAnimations)
}
+ @Test
+ fun testCujSpecificationLogsInteraction() {
+ val touchSurface = createTouchSurface()
+ runOnMainThreadAndWaitForIdleSync {
+ val dialog = TestDialog(context)
+ dialogLaunchAnimator.showFromView(
+ dialog, touchSurface, cuj = DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN))
+ }
+
+ verify(interactionJankMonitor).begin(any())
+ verify(interactionJankMonitor).end(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN)
+ }
+
+ @Test
+ fun testShowFromDialogCujSpecificationLogsInteraction() {
+ val firstDialog = createAndShowDialog()
+ runOnMainThreadAndWaitForIdleSync {
+ val dialog = TestDialog(context)
+ dialogLaunchAnimator.showFromDialog(
+ dialog, firstDialog, cuj = DialogCuj(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN))
+ dialog
+ }
+ verify(interactionJankMonitor).begin(any())
+ verify(interactionJankMonitor).end(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN)
+ }
+
private fun createAndShowDialog(): TestDialog {
val touchSurface = createTouchSurface()
return runOnMainThreadAndWaitForIdleSync {
@@ -224,4 +256,4 @@ class DialogLaunchAnimatorTest : SysuiTestCase() {
window.setBackgroundDrawable(windowBackground)
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
index b61fbbe1ea75..273786d74782 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
@@ -207,25 +207,30 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
}
@Test
- fun animatesAppearingViewsFromStartToEnd() {
- // Starting GONE.
- rootView.visibility = View.GONE
- rootView.layout(0 /* l */, 50 /* t */, 50 /* r */, 100 /* b */)
- var success = ViewHierarchyAnimator.animateAddition(rootView)
- rootView.visibility = View.VISIBLE
- rootView.layout(0 /* l */, 100 /* t */, 100 /* r */, 200 /* b */)
+ fun animatesInvisibleViews() {
+ rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
+ rootView.visibility = View.INVISIBLE
+
+ val success = ViewHierarchyAnimator.animate(rootView)
+ // Change all bounds.
+ rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */)
assertTrue(success)
assertNotNull(rootView.getTag(R.id.tag_animator))
- checkBounds(rootView, l = 50, t = 150, r = 50, b = 150)
+ // The initial values should be those of the previous layout.
+ checkBounds(rootView, l = 10, t = 10, r = 50, b = 50)
endAnimation(rootView)
assertNull(rootView.getTag(R.id.tag_animator))
- checkBounds(rootView, l = 0, t = 100, r = 100, b = 200)
+ // The end values should be those of the latest layout.
+ checkBounds(rootView, l = 0, t = 15, r = 70, b = 80)
+ }
- // Starting INVISIBLE.
- rootView.visibility = View.INVISIBLE
+ @Test
+ fun animatesAppearingViewsFromStartToEnd() {
+ // Starting GONE.
+ rootView.visibility = View.GONE
rootView.layout(0 /* l */, 50 /* t */, 50 /* r */, 100 /* b */)
- success = ViewHierarchyAnimator.animateAddition(rootView)
+ var success = ViewHierarchyAnimator.animateAddition(rootView)
rootView.visibility = View.VISIBLE
rootView.layout(0 /* l */, 100 /* t */, 100 /* r */, 200 /* b */)
@@ -937,7 +942,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
}
@Test
- fun doesNotAnimateInvisibleViews() {
+ fun doesNotAnimateGoneViews() {
rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */)
// GONE
@@ -948,15 +953,6 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
assertFalse(success)
assertNull(rootView.getTag(R.id.tag_animator))
checkBounds(rootView, l = 0, t = 15, r = 55, b = 80)
-
- // INVISIBLE.
- rootView.visibility = View.INVISIBLE
- success = ViewHierarchyAnimator.animate(rootView)
- rootView.layout(0 /* l */, 20 /* t */, 10 /* r */, 50 /* b */)
-
- assertFalse(success)
- assertNull(rootView.getTag(R.id.tag_animator))
- checkBounds(rootView, l = 0, t = 20, r = 10, b = 50)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index bc5a4d3d274e..bf3788e4c76a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -48,11 +48,12 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyLong
import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
@@ -87,7 +88,7 @@ class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testNotifiesAnimatedIn() {
initializeFingerprintContainer()
- verify(callback).onDialogAnimatedIn()
+ verify(callback).onDialogAnimatedIn(authContainer?.requestId ?: 0L)
}
@Test
@@ -96,13 +97,13 @@ class AuthContainerViewTest : SysuiTestCase() {
container.dismissFromSystemServer()
waitForIdleSync()
- verify(callback, never()).onDialogAnimatedIn()
+ verify(callback, never()).onDialogAnimatedIn(anyLong())
container.addToView()
waitForIdleSync()
// attaching the view resets the state and allows this to happen again
- verify(callback).onDialogAnimatedIn()
+ verify(callback).onDialogAnimatedIn(authContainer?.requestId ?: 0L)
}
@Test
@@ -110,14 +111,17 @@ class AuthContainerViewTest : SysuiTestCase() {
val container = initializeFingerprintContainer()
waitForIdleSync()
- verify(callback).onDialogAnimatedIn()
+ val requestID = authContainer?.requestId ?: 0L
+
+ verify(callback).onDialogAnimatedIn(requestID)
container.onWindowFocusChanged(false)
waitForIdleSync()
verify(callback).onDismissed(
- eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
- eq<ByteArray?>(null) /* credentialAttestation */
+ eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
+ eq<ByteArray?>(null), /* credentialAttestation */
+ eq(requestID)
)
assertThat(container.parent).isNull()
}
@@ -131,8 +135,9 @@ class AuthContainerViewTest : SysuiTestCase() {
waitForIdleSync()
verify(callback).onDismissed(
- eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED),
- eq<ByteArray?>(null) /* credentialAttestation */
+ eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED),
+ eq<ByteArray?>(null), /* credentialAttestation */
+ eq(authContainer?.requestId ?: 0L)
)
assertThat(container.parent).isNull()
}
@@ -146,11 +151,13 @@ class AuthContainerViewTest : SysuiTestCase() {
waitForIdleSync()
verify(callback).onSystemEvent(
- eq(BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL)
+ eq(BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL),
+ eq(authContainer?.requestId ?: 0L)
)
verify(callback).onDismissed(
- eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
- eq<ByteArray?>(null) /* credentialAttestation */
+ eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
+ eq<ByteArray?>(null), /* credentialAttestation */
+ eq(authContainer?.requestId ?: 0L)
)
assertThat(container.parent).isNull()
}
@@ -164,8 +171,9 @@ class AuthContainerViewTest : SysuiTestCase() {
waitForIdleSync()
verify(callback).onDismissed(
- eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE),
- eq<ByteArray?>(null) /* credentialAttestation */
+ eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE),
+ eq<ByteArray?>(null), /* credentialAttestation */
+ eq(authContainer?.requestId ?: 0L)
)
assertThat(container.parent).isNull()
}
@@ -180,7 +188,7 @@ class AuthContainerViewTest : SysuiTestCase() {
)
waitForIdleSync()
- verify(callback).onTryAgainPressed()
+ verify(callback).onTryAgainPressed(authContainer?.requestId ?: 0L)
}
@Test
@@ -192,8 +200,9 @@ class AuthContainerViewTest : SysuiTestCase() {
waitForIdleSync()
verify(callback).onDismissed(
- eq(AuthDialogCallback.DISMISSED_ERROR),
- eq<ByteArray?>(null) /* credentialAttestation */
+ eq(AuthDialogCallback.DISMISSED_ERROR),
+ eq<ByteArray?>(null), /* credentialAttestation */
+ eq(authContainer?.requestId ?: 0L)
)
assertThat(authContainer!!.parent).isNull()
}
@@ -209,7 +218,7 @@ class AuthContainerViewTest : SysuiTestCase() {
)
waitForIdleSync()
- verify(callback).onDeviceCredentialPressed()
+ verify(callback).onDeviceCredentialPressed(authContainer?.requestId ?: 0L)
assertThat(container.hasCredentialView()).isTrue()
}
@@ -297,6 +306,16 @@ class AuthContainerViewTest : SysuiTestCase() {
}
@Test
+ fun testLayoutParams_hasDimbehindWindowFlag() {
+ val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")
+ val lpFlags = layoutParams.flags
+ val lpDimAmount = layoutParams.dimAmount
+
+ assertThat((lpFlags and WindowManager.LayoutParams.FLAG_DIM_BEHIND) != 0).isTrue()
+ assertThat(lpDimAmount).isGreaterThan(0f)
+ }
+
+ @Test
fun testLayoutParams_excludesImeInsets() {
val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")
assertThat((layoutParams.fitInsetsTypes and WindowInsets.Type.ime()) == 0).isTrue()
@@ -312,12 +331,12 @@ class AuthContainerViewTest : SysuiTestCase() {
container.onAuthenticationFailed(BiometricAuthenticator.TYPE_FACE, "failed")
waitForIdleSync()
- verify(callback, never()).onTryAgainPressed()
+ verify(callback, never()).onTryAgainPressed(anyLong())
container.onPointerDown()
waitForIdleSync()
- verify(callback).onTryAgainPressed()
+ verify(callback).onTryAgainPressed(authContainer?.requestId ?: 0L)
}
private fun initializeFingerprintContainer(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index d948a99f8ad8..d158892e4ec5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -291,7 +291,8 @@ public class AuthControllerTest extends SysuiTestCase {
public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception {
showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
- null /* credentialAttestation */);
+ null, /* credentialAttestation */
+ mAuthController.mCurrentDialog.getRequestId());
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL),
eq(null) /* credentialAttestation */);
@@ -301,7 +302,8 @@ public class AuthControllerTest extends SysuiTestCase {
public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception {
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE,
- null /* credentialAttestation */);
+ null, /* credentialAttestation */
+ mAuthController.mCurrentDialog.getRequestId());
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_NEGATIVE),
eq(null) /* credentialAttestation */);
@@ -311,7 +313,8 @@ public class AuthControllerTest extends SysuiTestCase {
public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception {
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE,
- null /* credentialAttestation */);
+ null, /* credentialAttestation */
+ mAuthController.mCurrentDialog.getRequestId());
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED),
eq(null) /* credentialAttestation */);
@@ -321,7 +324,8 @@ public class AuthControllerTest extends SysuiTestCase {
public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception {
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED,
- null /* credentialAttestation */);
+ null, /* credentialAttestation */
+ mAuthController.mCurrentDialog.getRequestId());
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED),
eq(null) /* credentialAttestation */);
@@ -331,7 +335,8 @@ public class AuthControllerTest extends SysuiTestCase {
public void testSendsReasonError_whenDismissedByError() throws Exception {
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR,
- null /* credentialAttestation */);
+ null, /* credentialAttestation */
+ mAuthController.mCurrentDialog.getRequestId());
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_ERROR),
eq(null) /* credentialAttestation */);
@@ -341,7 +346,8 @@ public class AuthControllerTest extends SysuiTestCase {
public void testSendsReasonServerRequested_whenDismissedByServer() throws Exception {
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER,
- null /* credentialAttestation */);
+ null, /* credentialAttestation */
+ mAuthController.mCurrentDialog.getRequestId());
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED),
eq(null) /* credentialAttestation */);
@@ -355,7 +361,7 @@ public class AuthControllerTest extends SysuiTestCase {
final byte[] credentialAttestation = generateRandomHAT();
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED,
- credentialAttestation);
+ credentialAttestation, mAuthController.mCurrentDialog.getRequestId());
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED),
AdditionalMatchers.aryEq(credentialAttestation));
@@ -531,7 +537,7 @@ public class AuthControllerTest extends SysuiTestCase {
final byte[] credentialAttestation = generateRandomHAT();
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED,
- credentialAttestation);
+ credentialAttestation, mAuthController.mCurrentDialog.getRequestId());
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED),
AdditionalMatchers.aryEq(credentialAttestation));
@@ -640,17 +646,19 @@ public class AuthControllerTest extends SysuiTestCase {
@Test
public void testDoesNotCrash_whenTryAgainPressedAfterDismissal() {
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
+ final long requestID = mAuthController.mCurrentDialog.getRequestId();
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
- null /* credentialAttestation */);
- mAuthController.onTryAgainPressed();
+ null, /* credentialAttestation */requestID);
+ mAuthController.onTryAgainPressed(requestID);
}
@Test
public void testDoesNotCrash_whenDeviceCredentialPressedAfterDismissal() {
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
+ final long requestID = mAuthController.mCurrentDialog.getRequestId();
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
- null /* credentialAttestation */);
- mAuthController.onDeviceCredentialPressed();
+ null /* credentialAttestation */, requestID);
+ mAuthController.onDeviceCredentialPressed(requestID);
}
@Test
@@ -708,7 +716,8 @@ public class AuthControllerTest extends SysuiTestCase {
// WHEN dialog is shown and then dismissed
showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */);
mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED,
- null /* credentialAttestation */);
+ null /* credentialAttestation */,
+ mAuthController.mCurrentDialog.getRequestId());
// THEN callback should be received
verify(callback).onBiometricPromptDismissed();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS
new file mode 100644
index 000000000000..adb10f01b5e1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS
@@ -0,0 +1,4 @@
+set noparent
+
+include /services/core/java/com/android/server/biometrics/OWNERS
+beverlyt@google.com
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
index dec2b82ed88f..6157ccbd597a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
@@ -62,7 +62,6 @@ import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.anyInt
@@ -72,6 +71,7 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenEver
import org.mockito.junit.MockitoJUnit
private const val DISPLAY_ID = 2
@@ -126,15 +126,15 @@ class SidefpsControllerTest : SysuiTestCase() {
context.addMockSystemService(DisplayManager::class.java, displayManager)
context.addMockSystemService(WindowManager::class.java, windowManager)
- `when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sidefpsView)
- `when`(sidefpsView.findViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
+ whenEver(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sidefpsView)
+ whenEver(sidefpsView.findViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
.thenReturn(mock(LottieAnimationView::class.java))
with(mock(ViewPropertyAnimator::class.java)) {
- `when`(sidefpsView.animate()).thenReturn(this)
- `when`(alpha(anyFloat())).thenReturn(this)
- `when`(setStartDelay(anyLong())).thenReturn(this)
- `when`(setDuration(anyLong())).thenReturn(this)
- `when`(setListener(any())).thenAnswer {
+ whenEver(sidefpsView.animate()).thenReturn(this)
+ whenEver(alpha(anyFloat())).thenReturn(this)
+ whenEver(setStartDelay(anyLong())).thenReturn(this)
+ whenEver(setDuration(anyLong())).thenReturn(this)
+ whenEver(setListener(any())).thenAnswer {
(it.arguments[0] as Animator.AnimatorListener)
.onAnimationEnd(mock(Animator::class.java))
this
@@ -177,7 +177,7 @@ class SidefpsControllerTest : SysuiTestCase() {
displayBounds = Rect(0, 0, displayWidth, displayHeight)
var locations = listOf(sensorLocation)
- `when`(fingerprintManager.sensorPropertiesInternal).thenReturn(
+ whenEver(fingerprintManager.sensorPropertiesInternal).thenReturn(
listOf(
FingerprintSensorPropertiesInternal(
SENSOR_ID,
@@ -196,12 +196,12 @@ class SidefpsControllerTest : SysuiTestCase() {
displayInfo.initInfo()
val dmGlobal = mock(DisplayManagerGlobal::class.java)
val display = Display(dmGlobal, DISPLAY_ID, displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS)
- `when`(dmGlobal.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(displayInfo)
- `when`(windowManager.defaultDisplay).thenReturn(display)
- `when`(windowManager.maximumWindowMetrics).thenReturn(
+ whenEver(dmGlobal.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(displayInfo)
+ whenEver(windowManager.defaultDisplay).thenReturn(display)
+ whenEver(windowManager.maximumWindowMetrics).thenReturn(
WindowMetrics(displayBounds, WindowInsets.CONSUMED)
)
- `when`(windowManager.currentWindowMetrics).thenReturn(
+ whenEver(windowManager.currentWindowMetrics).thenReturn(
WindowMetrics(displayBounds, windowInsets)
)
@@ -277,13 +277,13 @@ class SidefpsControllerTest : SysuiTestCase() {
@Test
fun testShowsForMostSettings() = testWithDisplay {
- `when`(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpEnrollTask()))
+ whenEver(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpEnrollTask()))
testIgnoredFor(REASON_AUTH_SETTINGS, ignored = false)
}
@Test
fun testIgnoredForVerySpecificSettings() = testWithDisplay {
- `when`(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpSettingsTask()))
+ whenEver(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpSettingsTask()))
testIgnoredFor(REASON_AUTH_SETTINGS)
}
@@ -424,6 +424,20 @@ class SidefpsControllerTest : SysuiTestCase() {
assertThat(overlayViewParamsCaptor.value.x).isEqualTo(displayWidth - boundsWidth)
assertThat(overlayViewParamsCaptor.value.y).isEqualTo(sensorLocation.sensorLocationY)
}
+
+ @Test
+ fun hasSideFpsSensor_withSensorProps_returnsTrue() = testWithDisplay {
+ // By default all those tests assume the side fps sensor is available.
+
+ assertThat(fingerprintManager.hasSideFpsSensor()).isTrue()
+ }
+
+ @Test
+ fun hasSideFpsSensor_withoutSensorProps_returnsFalse() {
+ whenEver(fingerprintManager.sensorPropertiesInternal).thenReturn(null)
+
+ assertThat(fingerprintManager.hasSideFpsSensor()).isFalse()
+ }
}
private fun insetsForSmallNavbar() = insetsWithBottom(60)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 638e6f32a241..09dc8e4fdb8e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -740,12 +740,12 @@ public class UdfpsControllerTest extends SysuiTestCase {
anyString(),
any(),
eq("udfps-onStart-click"),
- eq(UdfpsController.VIBRATION_ATTRIBUTES));
+ eq(UdfpsController.UDFPS_VIBRATION_ATTRIBUTES));
// THEN make sure vibration attributes has so that it always will play the haptic,
// even in battery saver mode
assertEquals(VibrationAttributes.USAGE_COMMUNICATION_REQUEST,
- UdfpsController.VIBRATION_ATTRIBUTES.getUsage());
+ UdfpsController.UDFPS_VIBRATION_ATTRIBUTES.getUsage());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 0fdd9054e4bc..b61bda8edd10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -468,6 +469,40 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
verify(mView).setUnpausedAlpha(255);
}
+ @Test
+ public void testUpdatePanelExpansion_pauseAuth() {
+ // GIVEN view is attached + on the keyguard
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+ captureStatusBarExpansionListeners();
+ sendStatusBarStateChanged(StatusBarState.KEYGUARD);
+ reset(mView);
+
+ // WHEN panelViewExpansion changes to hide
+ when(mView.getUnpausedAlpha()).thenReturn(0);
+ updateStatusBarExpansion(0f, false);
+
+ // THEN pause auth is updated to PAUSE
+ verify(mView, atLeastOnce()).setPauseAuth(true);
+ }
+
+ @Test
+ public void testUpdatePanelExpansion_unpauseAuth() {
+ // GIVEN view is attached + on the keyguard + panel expansion is 0f
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+ captureStatusBarExpansionListeners();
+ sendStatusBarStateChanged(StatusBarState.KEYGUARD);
+ reset(mView);
+
+ // WHEN panelViewExpansion changes to expanded
+ when(mView.getUnpausedAlpha()).thenReturn(255);
+ updateStatusBarExpansion(1f, true);
+
+ // THEN pause auth is updated to NOT pause
+ verify(mView, atLeastOnce()).setPauseAuth(false);
+ }
+
private void sendStatusBarStateChanged(int statusBarState) {
mStatusBarStateListener.onStateChanged(statusBarState);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java
new file mode 100644
index 000000000000..a61cecbabf30
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import androidx.test.filters.SmallTest;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class BroadcastDialogTest extends SysuiTestCase {
+
+ private static final String SWITCH_APP = "Music";
+ private static final String TEST_PACKAGE = "com.google.android.apps.nbu.files";
+ private BroadcastDialog mBroadcastDialog;
+ private View mDialogView;
+ private TextView mSubTitle;
+ private Button mChangeOutputButton;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mBroadcastDialog = new BroadcastDialog(mContext, mock(MediaOutputDialogFactory.class),
+ SWITCH_APP, TEST_PACKAGE, mock(UiEventLogger.class));
+ mBroadcastDialog.show();
+ mDialogView = mBroadcastDialog.mDialogView;
+ }
+
+ @After
+ public void tearDown() {
+ mBroadcastDialog.dismiss();
+ }
+
+ @Test
+ public void onCreate_withCurrentApp_checkSwitchAppContent() {
+ mSubTitle = mDialogView.requireViewById(R.id.dialog_subtitle);
+
+ assertThat(mSubTitle.getText()).isEqualTo(
+ mContext.getString(R.string.bt_le_audio_broadcast_dialog_sub_title, SWITCH_APP));
+ }
+
+ @Test
+ public void onClick_withChangeOutput_dismissBroadcastDialog() {
+ mChangeOutputButton = mDialogView.requireViewById(R.id.change_output);
+ mChangeOutputButton.performClick();
+
+ assertThat(mBroadcastDialog.isShowing()).isFalse();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
new file mode 100644
index 000000000000..ca94ea826782
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.camera
+
+import android.app.ActivityManager
+import android.app.IActivityTaskManager
+import android.content.ComponentName
+import android.content.ContentResolver
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.mockito.KotlinArgumentCaptor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class CameraGestureHelperTest : SysuiTestCase() {
+
+ @Mock
+ lateinit var centralSurfaces: CentralSurfaces
+ @Mock
+ lateinit var keyguardStateController: KeyguardStateController
+ @Mock
+ lateinit var packageManager: PackageManager
+ @Mock
+ lateinit var activityManager: ActivityManager
+ @Mock
+ lateinit var activityStarter: ActivityStarter
+ @Mock
+ lateinit var activityIntentHelper: ActivityIntentHelper
+ @Mock
+ lateinit var activityTaskManager: IActivityTaskManager
+ @Mock
+ lateinit var cameraIntents: CameraIntentsWrapper
+ @Mock
+ lateinit var contentResolver: ContentResolver
+
+ private lateinit var underTest: CameraGestureHelper
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(cameraIntents.getSecureCameraIntent()).thenReturn(
+ Intent(CameraIntents.DEFAULT_SECURE_CAMERA_INTENT_ACTION)
+ )
+ whenever(cameraIntents.getInsecureCameraIntent()).thenReturn(
+ Intent(CameraIntents.DEFAULT_INSECURE_CAMERA_INTENT_ACTION)
+ )
+
+ prepare()
+
+ underTest = CameraGestureHelper(
+ context = mock(),
+ centralSurfaces = centralSurfaces,
+ keyguardStateController = keyguardStateController,
+ packageManager = packageManager,
+ activityManager = activityManager,
+ activityStarter = activityStarter,
+ activityIntentHelper = activityIntentHelper,
+ activityTaskManager = activityTaskManager,
+ cameraIntents = cameraIntents,
+ contentResolver = contentResolver,
+ uiExecutor = MoreExecutors.directExecutor(),
+ )
+ }
+
+ /**
+ * Prepares for tests by setting up the various mocks to emulate a specific device state.
+ *
+ * <p>Safe to call multiple times in a single test (for example, once in [setUp] and once in the
+ * actual test case).
+ *
+ * @param isCameraAllowedByAdmin Whether the device administrator allows use of the camera app
+ * @param installedCameraAppCount The number of installed camera apps on the device
+ * @param isUsingSecureScreenLockOption Whether the user-controlled setting for Screen Lock is
+ * set with a "secure" option that requires the user to provide some secret/credentials to be
+ * able to unlock the device, for example "Face Unlock", "PIN", or "Password". Examples of
+ * non-secure options are "None" and "Swipe"
+ * @param isCameraActivityRunningOnTop Whether the camera activity is running at the top of the
+ * most recent/current task of activities
+ * @param isTaskListEmpty Whether there are no active activity tasks at all. Note that this is
+ * treated as `false` if [isCameraActivityRunningOnTop] is set to `true`
+ */
+ private fun prepare(
+ isCameraAllowedByAdmin: Boolean = true,
+ installedCameraAppCount: Int = 1,
+ isUsingSecureScreenLockOption: Boolean = true,
+ isCameraActivityRunningOnTop: Boolean = false,
+ isTaskListEmpty: Boolean = false,
+ ) {
+ whenever(centralSurfaces.isCameraAllowedByAdmin).thenReturn(isCameraAllowedByAdmin)
+
+ whenever(activityIntentHelper.wouldLaunchResolverActivity(any(), anyInt()))
+ .thenReturn(installedCameraAppCount > 1)
+
+ whenever(keyguardStateController.isMethodSecure).thenReturn(isUsingSecureScreenLockOption)
+ whenever(keyguardStateController.canDismissLockScreen())
+ .thenReturn(!isUsingSecureScreenLockOption)
+
+ if (installedCameraAppCount >= 1) {
+ val resolveInfo = ResolveInfo().apply {
+ this.activityInfo = ActivityInfo().apply {
+ packageName = CAMERA_APP_PACKAGE_NAME
+ }
+ }
+ whenever(packageManager.resolveActivityAsUser(any(), anyInt(), anyInt())).thenReturn(
+ resolveInfo
+ )
+ } else {
+ whenever(packageManager.resolveActivityAsUser(any(), anyInt(), anyInt())).thenReturn(
+ null
+ )
+ }
+
+ when {
+ isCameraActivityRunningOnTop -> {
+ val runningTaskInfo = ActivityManager.RunningTaskInfo().apply {
+ topActivity = ComponentName(CAMERA_APP_PACKAGE_NAME, "cameraActivity")
+ }
+ whenever(activityManager.getRunningTasks(anyInt())).thenReturn(
+ listOf(
+ runningTaskInfo
+ )
+ )
+ }
+ isTaskListEmpty -> {
+ whenever(activityManager.getRunningTasks(anyInt())).thenReturn(emptyList())
+ }
+ else -> {
+ whenever(activityManager.getRunningTasks(anyInt())).thenReturn(listOf())
+ }
+ }
+ }
+
+ @Test
+ fun `canCameraGestureBeLaunched - status bar state is keyguard - returns true`() {
+ assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isTrue()
+ }
+
+ @Test
+ fun `canCameraGestureBeLaunched - state is shade-locked - returns true`() {
+ assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE_LOCKED)).isTrue()
+ }
+
+ @Test
+ fun `canCameraGestureBeLaunched - state is keyguard - camera activity on top - returns true`() {
+ prepare(isCameraActivityRunningOnTop = true)
+
+ assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isTrue()
+ }
+
+ @Test
+ fun `canCameraGestureBeLaunched - state is shade-locked - camera activity on top - true`() {
+ prepare(isCameraActivityRunningOnTop = true)
+
+ assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE_LOCKED)).isTrue()
+ }
+
+ @Test
+ fun `canCameraGestureBeLaunched - not allowed by admin - returns false`() {
+ prepare(isCameraAllowedByAdmin = false)
+
+ assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isFalse()
+ }
+
+ @Test
+ fun `canCameraGestureBeLaunched - intent does not resolve to any app - returns false`() {
+ prepare(installedCameraAppCount = 0)
+
+ assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isFalse()
+ }
+
+ @Test
+ fun `canCameraGestureBeLaunched - state is shade - no running tasks - returns true`() {
+ prepare(isCameraActivityRunningOnTop = false, isTaskListEmpty = true)
+
+ assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isTrue()
+ }
+
+ @Test
+ fun `canCameraGestureBeLaunched - state is shade - camera activity on top - returns false`() {
+ prepare(isCameraActivityRunningOnTop = true)
+
+ assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isFalse()
+ }
+
+ @Test
+ fun `canCameraGestureBeLaunched - state is shade - camera activity not on top - true`() {
+ assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isTrue()
+ }
+
+ @Test
+ fun `launchCamera - only one camera app installed - using secure screen lock option`() {
+ val source = 1337
+
+ underTest.launchCamera(source)
+
+ assertActivityStarting(isSecure = true, source = source)
+ }
+
+ @Test
+ fun `launchCamera - only one camera app installed - using non-secure screen lock option`() {
+ prepare(isUsingSecureScreenLockOption = false)
+ val source = 1337
+
+ underTest.launchCamera(source)
+
+ assertActivityStarting(isSecure = false, source = source)
+ }
+
+ @Test
+ fun `launchCamera - multiple camera apps installed - using secure screen lock option`() {
+ prepare(installedCameraAppCount = 2)
+ val source = 1337
+
+ underTest.launchCamera(source)
+
+ assertActivityStarting(
+ isSecure = true,
+ source = source,
+ moreThanOneCameraAppInstalled = true
+ )
+ }
+
+ @Test
+ fun `launchCamera - multiple camera apps installed - using non-secure screen lock option`() {
+ prepare(
+ isUsingSecureScreenLockOption = false,
+ installedCameraAppCount = 2,
+ )
+ val source = 1337
+
+ underTest.launchCamera(source)
+
+ assertActivityStarting(
+ isSecure = false,
+ moreThanOneCameraAppInstalled = true,
+ source = source
+ )
+ }
+
+ private fun assertActivityStarting(
+ isSecure: Boolean,
+ source: Int,
+ moreThanOneCameraAppInstalled: Boolean = false,
+ ) {
+ val intentCaptor = KotlinArgumentCaptor(Intent::class.java)
+ if (isSecure && !moreThanOneCameraAppInstalled) {
+ verify(activityTaskManager).startActivityAsUser(
+ any(),
+ any(),
+ any(),
+ intentCaptor.capture(),
+ any(),
+ any(),
+ any(),
+ anyInt(),
+ anyInt(),
+ any(),
+ any(),
+ anyInt()
+ )
+ } else {
+ verify(activityStarter).startActivity(intentCaptor.capture(), eq(false))
+ }
+ val intent = intentCaptor.value
+
+ assertThat(CameraIntents.isSecureCameraIntent(intent)).isEqualTo(isSecure)
+ assertThat(intent.getIntExtra(CameraGestureHelper.EXTRA_CAMERA_LAUNCH_SOURCE, -1))
+ .isEqualTo(source)
+ }
+
+ companion object {
+ private const val CAMERA_APP_PACKAGE_NAME = "cameraAppPackageName"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index b4cae38d8b6e..6978490a1252 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.charging
+package com.android.systemui.charging
import android.testing.AndroidTestingRunner
import android.view.View
@@ -24,6 +24,7 @@ import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.ripple.RippleView
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -50,7 +51,7 @@ class WiredChargingRippleControllerTest : SysuiTestCase() {
@Mock private lateinit var batteryController: BatteryController
@Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var configurationController: ConfigurationController
- @Mock private lateinit var rippleView: ChargingRippleView
+ @Mock private lateinit var rippleView: RippleView
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var uiEventLogger: UiEventLogger
private val systemClock = FakeSystemClock()
@@ -74,9 +75,9 @@ class WiredChargingRippleControllerTest : SysuiTestCase() {
// Verify ripple added to window manager.
captor.value.onBatteryLevelChanged(
- 0 /* unusedBatteryLevel */,
- true /* plugged in */,
- false /* charging */)
+ /* unusedBatteryLevel= */ 0,
+ /* plugged in= */ true,
+ /* charging= */ false)
val attachListenerCaptor =
ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
verify(rippleView).addOnAttachStateChangeListener(attachListenerCaptor.capture())
@@ -144,4 +145,22 @@ class WiredChargingRippleControllerTest : SysuiTestCase() {
// Verify that ripple is triggered.
verify(rippleView).addOnAttachStateChangeListener(ArgumentMatchers.any())
}
+
+ @Test
+ fun testRipple_whenDocked_doesNotPlayRipple() {
+ `when`(batteryController.isChargingSourceDock).thenReturn(true)
+ val captor = ArgumentCaptor
+ .forClass(BatteryController.BatteryStateChangeCallback::class.java)
+ verify(batteryController).addCallback(captor.capture())
+
+ captor.value.onBatteryLevelChanged(
+ /* unusedBatteryLevel= */ 0,
+ /* plugged in= */ true,
+ /* charging= */ false)
+
+ val attachListenerCaptor =
+ ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
+ verify(rippleView, never()).addOnAttachStateChangeListener(attachListenerCaptor.capture())
+ verify(windowManager, never()).addView(eq(rippleView), any<WindowManager.LayoutParams>())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index 3c7ea4fe6f35..c31fd828c730 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -37,9 +37,14 @@ import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dump.DumpManager
+import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.util.Optional
+import java.util.function.Consumer
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
@@ -50,20 +55,20 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.Optional
-import java.util.function.Consumer
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -85,6 +90,8 @@ class ControlsControllerImplTest : SysuiTestCase() {
private lateinit var listingController: ControlsListingController
@Mock(stubOnly = true)
private lateinit var userTracker: UserTracker
+ @Mock
+ private lateinit var userFileManager: UserFileManager
@Captor
private lateinit var structureInfoCaptor: ArgumentCaptor<StructureInfo>
@@ -153,6 +160,9 @@ class ControlsControllerImplTest : SysuiTestCase() {
canceller = DidRunRunnable()
`when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller)
+ `when`(userFileManager.getFile(anyString(), anyInt())).thenReturn(mock(File::class.java))
+ `when`(userFileManager.getSharedPreferences(anyString(), anyInt(), anyInt()))
+ .thenReturn(context.getSharedPreferences("test", Context.MODE_PRIVATE))
controller = ControlsControllerImpl(
wrapper,
@@ -161,6 +171,7 @@ class ControlsControllerImplTest : SysuiTestCase() {
bindingController,
listingController,
broadcastDispatcher,
+ userFileManager,
Optional.of(persistenceWrapper),
mock(DumpManager::class.java),
userTracker
@@ -217,6 +228,7 @@ class ControlsControllerImplTest : SysuiTestCase() {
bindingController,
listingController,
broadcastDispatcher,
+ userFileManager,
Optional.of(persistenceWrapper),
mock(DumpManager::class.java),
userTracker
@@ -911,6 +923,14 @@ class ControlsControllerImplTest : SysuiTestCase() {
assertTrue(controller.getFavoritesForStructure(TEST_COMPONENT_2, TEST_STRUCTURE).isEmpty())
}
+
+ @Test
+ fun testUserStructure() {
+ val userStructure = UserStructure(context, context.user, userFileManager)
+ verify(userFileManager, times(2))
+ .getFile(ControlsFavoritePersistenceWrapper.FILE_NAME, context.user.identifier)
+ assertThat(userStructure.file).isNotNull()
+ }
}
private class DidRunRunnable() : Runnable {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 3340f2fd5749..abe7ae1a0b1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -21,7 +21,9 @@ import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_DOCKED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING_BRIGHT;
import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_SUSPEND_TRIGGERS;
import static com.android.systemui.doze.DozeMachine.State.FINISH;
import static com.android.systemui.doze.DozeMachine.State.INITIALIZED;
import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
@@ -39,6 +41,8 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.UiModeManager;
+import android.content.res.Configuration;
import android.hardware.display.AmbientDisplayConfiguration;
import android.testing.AndroidTestingRunner;
import android.testing.UiThreadTest;
@@ -50,7 +54,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.wakelock.WakeLockFake;
import org.junit.Before;
@@ -70,9 +73,12 @@ public class DozeMachineTest extends SysuiTestCase {
private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock
private DozeLog mDozeLog;
- @Mock private DockManager mDockManager;
+ @Mock
+ private DockManager mDockManager;
@Mock
private DozeHost mHost;
+ @Mock
+ private UiModeManager mUiModeManager;
private DozeServiceFake mServiceFake;
private WakeLockFake mWakeLockFake;
private AmbientDisplayConfiguration mConfigMock;
@@ -89,7 +95,7 @@ public class DozeMachineTest extends SysuiTestCase {
when(mDockManager.isHidden()).thenReturn(false);
mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake,
- mWakefulnessLifecycle, mock(BatteryController.class), mDozeLog, mDockManager,
+ mWakefulnessLifecycle, mUiModeManager, mDozeLog, mDockManager,
mHost, new DozeMachine.Part[]{mPartMock});
}
@@ -462,4 +468,64 @@ public class DozeMachineTest extends SysuiTestCase {
assertEquals(Display.STATE_ON, DOZE_REQUEST_PULSE.screenState(dozeParameters));
}
+
+ @Test
+ public void testTransitionToInitialized_carModeIsEnabled() {
+ when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
+ mMachine.requestState(INITIALIZED);
+
+ verify(mPartMock).transitionTo(UNINITIALIZED, INITIALIZED);
+ verify(mPartMock).transitionTo(INITIALIZED, DOZE_SUSPEND_TRIGGERS);
+ assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState());
+ }
+
+ @Test
+ public void testTransitionToFinish_carModeIsEnabled() {
+ when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestState(FINISH);
+
+ assertEquals(FINISH, mMachine.getState());
+ }
+
+ @Test
+ public void testDozeToDozeSuspendTriggers_carModeIsEnabled() {
+ when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestState(DOZE);
+
+ assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState());
+ }
+
+ @Test
+ public void testDozeAoDToDozeSuspendTriggers_carModeIsEnabled() {
+ when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestState(DOZE_AOD);
+
+ assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState());
+ }
+
+ @Test
+ public void testDozePulsingBrightDozeSuspendTriggers_carModeIsEnabled() {
+ when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestState(DOZE_PULSING_BRIGHT);
+
+ assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState());
+ }
+
+ @Test
+ public void testDozeAodDockedDozeSuspendTriggers_carModeIsEnabled() {
+ when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestState(DOZE_AOD_DOCKED);
+
+ assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState());
+ }
+
+ @Test
+ public void testDozeSuppressTriggers_screenState() {
+ assertEquals(Display.STATE_OFF, DOZE_SUSPEND_TRIGGERS.screenState(null));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 2e7b88d5fd38..03827dab0c96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -24,6 +24,7 @@ import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_SUSPEND_TRIGGERS;
import static com.android.systemui.doze.DozeMachine.State.FINISH;
import static com.android.systemui.doze.DozeMachine.State.INITIALIZED;
import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
@@ -182,6 +183,21 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
}
@Test
+ public void dozeSuspendTriggers_doesNotUseLightSensor() {
+ // GIVEN the device is DOZE and the display state changes to ON
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE_SUSPEND_TRIGGERS);
+ waitForSensorManager();
+
+ // WHEN new sensor event sent
+ mSensor.sendSensorEvent(3);
+
+ // THEN brightness is NOT changed, it's set to the default brightness
+ assertNotSame(3, mServiceFake.screenBrightness);
+ assertEquals(DEFAULT_BRIGHTNESS, mServiceFake.screenBrightness);
+ }
+
+ @Test
public void aod_usesLightSensor() {
// GIVEN the device is DOZE_AOD and the display state changes to ON
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
index aa0a909622bb..0f29dcd5a939 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
@@ -16,19 +16,26 @@
package com.android.systemui.doze;
+import static android.app.UiModeManager.ACTION_ENTER_CAR_MODE;
+import static android.app.UiModeManager.ACTION_EXIT_CAR_MODE;
+
import static com.android.systemui.doze.DozeMachine.State.DOZE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_SUSPEND_TRIGGERS;
import static com.android.systemui.doze.DozeMachine.State.FINISH;
import static com.android.systemui.doze.DozeMachine.State.INITIALIZED;
import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
-import static org.mockito.Matchers.anyObject;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.UiModeManager;
import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Configuration;
import android.hardware.display.AmbientDisplayConfiguration;
import android.testing.AndroidTestingRunner;
@@ -77,7 +84,10 @@ public class DozeSuppressorTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+ @Captor
+ private ArgumentCaptor<IntentFilter> mIntentFilterCaptor;
private BroadcastReceiver mBroadcastReceiver;
+ private IntentFilter mIntentFilter;
@Captor
private ArgumentCaptor<DozeHost.Callback> mDozeHostCaptor;
@@ -122,15 +132,59 @@ public class DozeSuppressorTest extends SysuiTestCase {
}
@Test
- public void testEndDoze_carMode() {
+ public void testSuspendTriggersDoze_carMode() {
// GIVEN car mode
when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
// WHEN dozing begins
mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
- // THEN doze immediately ends
- verify(mDozeMachine).requestState(FINISH);
+ // THEN doze continues with all doze triggers disabled.
+ verify(mDozeMachine).requestState(DOZE_SUSPEND_TRIGGERS);
+ }
+
+ @Test
+ public void testSuspendTriggersDoze_enterCarMode() {
+ // GIVEN currently dozing
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+ captureBroadcastReceiver();
+ mDozeSuppressor.transitionTo(INITIALIZED, DOZE);
+
+ // WHEN car mode entered
+ mBroadcastReceiver.onReceive(null, new Intent(ACTION_ENTER_CAR_MODE));
+
+ // THEN doze continues with all doze triggers disabled.
+ verify(mDozeMachine).requestState(DOZE_SUSPEND_TRIGGERS);
+ }
+
+ @Test
+ public void testDozeResume_exitCarMode() {
+ // GIVEN currently suspended, with AOD not enabled
+ when(mConfig.alwaysOnEnabled(anyInt())).thenReturn(false);
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+ captureBroadcastReceiver();
+ mDozeSuppressor.transitionTo(INITIALIZED, DOZE_SUSPEND_TRIGGERS);
+
+ // WHEN exiting car mode
+ mBroadcastReceiver.onReceive(null, new Intent(ACTION_EXIT_CAR_MODE));
+
+ // THEN doze is resumed
+ verify(mDozeMachine).requestState(DOZE);
+ }
+
+ @Test
+ public void testDozeAoDResume_exitCarMode() {
+ // GIVEN currently suspended, with AOD not enabled
+ when(mConfig.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+ captureBroadcastReceiver();
+ mDozeSuppressor.transitionTo(INITIALIZED, DOZE_SUSPEND_TRIGGERS);
+
+ // WHEN exiting car mode
+ mBroadcastReceiver.onReceive(null, new Intent(ACTION_EXIT_CAR_MODE));
+
+ // THEN doze AOD is resumed
+ verify(mDozeMachine).requestState(DOZE_AOD);
}
@Test
@@ -225,7 +279,11 @@ public class DozeSuppressorTest extends SysuiTestCase {
private void captureBroadcastReceiver() {
verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverCaptor.capture(),
- anyObject());
+ mIntentFilterCaptor.capture());
mBroadcastReceiver = mBroadcastReceiverCaptor.getValue();
+ mIntentFilter = mIntentFilterCaptor.getValue();
+ assertEquals(2, mIntentFilter.countActions());
+ org.hamcrest.MatcherAssert.assertThat(() -> mIntentFilter.actionsIterator(),
+ containsInAnyOrder(ACTION_ENTER_CAR_MODE, ACTION_EXIT_CAR_MODE));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 4eeb4acf18b8..01a1a3718765 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -17,6 +17,8 @@
package com.android.systemui.doze;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+import static com.android.systemui.doze.DozeMachine.State.INITIALIZED;
+import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -133,7 +135,7 @@ public class DozeTriggersTest extends SysuiTestCase {
ArgumentCaptor<DozeHost.Callback> captor = ArgumentCaptor.forClass(DozeHost.Callback.class);
doAnswer(invocation -> null).when(mHost).addCallback(captor.capture());
- mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
+ mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED);
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
clearInvocations(mMachine);
@@ -192,8 +194,21 @@ public class DozeTriggersTest extends SysuiTestCase {
}
@Test
+ public void transitionToDozeSuspendTriggers_disablesAllCallbacks() {
+ mTriggers.transitionTo(UNINITIALIZED, INITIALIZED);
+ when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_SUSPEND_TRIGGERS);
+
+ mTriggers.transitionTo(DozeMachine.State.INITIALIZED,
+ DozeMachine.State.DOZE_SUSPEND_TRIGGERS);
+
+ verify(mDockManager).removeListener(any());
+ verify(mBroadcastDispatcher).unregisterReceiver(any());
+ verify(mHost).removeCallback(any());
+ }
+
+ @Test
public void testDockEventListener_registerAndUnregister() {
- mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
+ mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED);
verify(mDockManager).addListener(any());
mTriggers.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.FINISH);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index e5a75e231f8d..49cdfa72f344 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -86,7 +86,7 @@ public class DozeUiTest extends SysuiTestCase {
mHandler = mHandlerThread.getThreadHandler();
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
- mDozeParameters, mKeyguardUpdateMonitor, mStatusBarStateController, mDozeLog);
+ mDozeParameters, mStatusBarStateController, mDozeLog);
mDozeUi.setDozeMachine(mMachine);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
index b7de6c15459c..25d4c472dc63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.dreams;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -48,6 +49,8 @@ public class DreamOverlayNotificationCountProviderTest extends SysuiTestCase {
@Mock
StatusBarNotification mNotification2;
@Mock
+ StatusBarNotification mNotification3;
+ @Mock
NotificationListenerService.RankingMap mRankingMap;
private DreamOverlayNotificationCountProvider mProvider;
@@ -58,6 +61,8 @@ public class DreamOverlayNotificationCountProviderTest extends SysuiTestCase {
when(mNotification1.getKey()).thenReturn("key1");
when(mNotification2.getKey()).thenReturn("key2");
+ when(mNotification3.getKey()).thenReturn("key3");
+ when(mNotification3.isOngoing()).thenReturn(true);
final StatusBarNotification[] notifications = {mNotification1};
when(mNotificationListener.getActiveNotifications()).thenReturn(notifications);
@@ -83,4 +88,13 @@ public class DreamOverlayNotificationCountProviderTest extends SysuiTestCase {
handlerArgumentCaptor.getValue().onNotificationRemoved(mNotification1, mRankingMap);
verify(mCallback).onNotificationCountChanged(0);
}
+
+ @Test
+ public void testPostingOngoingNotificationDoesNotCallCallbackWithNotificationCount() {
+ final ArgumentCaptor<NotificationHandler> handlerArgumentCaptor =
+ ArgumentCaptor.forClass(NotificationHandler.class);
+ verify(mNotificationListener).addNotificationHandler(handlerArgumentCaptor.capture());
+ handlerArgumentCaptor.getValue().onNotificationPosted(mNotification3, mRankingMap);
+ verify(mCallback, never()).onNotificationCountChanged(2);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index fb64c7b58aac..2adf2857a385 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -207,4 +207,15 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
assertThat(complications.contains(weatherComplication)).isFalse();
}
}
+
+ @Test
+ public void testComplicationWithNoTypeNotFiltered() {
+ final Complication complication = Mockito.mock(Complication.class);
+ final DreamOverlayStateController stateController =
+ new DreamOverlayStateController(mExecutor);
+ stateController.addComplication(complication);
+ mExecutor.runAllReady();
+ assertThat(stateController.getComplications(true).contains(complication))
+ .isTrue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 3c28d48c2eb5..60e5a9423c61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -57,6 +57,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
import java.util.concurrent.Executor;
@SmallTest
@@ -115,7 +116,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
mNextAlarmController,
mDateFormatUtil,
mSensorPrivacyController,
- mDreamOverlayNotificationCountProvider,
+ Optional.of(mDreamOverlayNotificationCountProvider),
mZenModeController,
mStatusBarWindowStateController);
}
@@ -183,25 +184,36 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
}
@Test
- public void testOnViewAttachedShowsMicCameraIconWhenDisabled() {
+ public void testOnViewAttachedShowsMicIconWhenDisabled() {
when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE))
.thenReturn(true);
when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA))
- .thenReturn(true);
+ .thenReturn(false);
mController.onViewAttached();
verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null);
+ DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED, true, null);
}
@Test
- public void testOnViewAttachedHidesMicCameraIconWhenEnabled() {
+ public void testOnViewAttachedShowsCameraIconWhenDisabled() {
when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE))
.thenReturn(false);
when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA))
- .thenReturn(false);
+ .thenReturn(true);
+ mController.onViewAttached();
+ verify(mView).showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED, true, null);
+ }
+
+ @Test
+ public void testOnViewAttachedShowsMicCameraIconWhenDisabled() {
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE))
+ .thenReturn(true);
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA))
+ .thenReturn(true);
mController.onViewAttached();
verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false, null);
+ DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null);
}
@Test
@@ -231,6 +243,26 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
}
@Test
+ public void testNotificationsIconNotShownWhenCountProviderAbsent() {
+ DreamOverlayStatusBarViewController controller = new DreamOverlayStatusBarViewController(
+ mView,
+ mResources,
+ mMainExecutor,
+ mConnectivityManager,
+ mTouchSession,
+ mAlarmManager,
+ mNextAlarmController,
+ mDateFormatUtil,
+ mSensorPrivacyController,
+ Optional.empty(),
+ mZenModeController,
+ mStatusBarWindowStateController);
+ controller.onViewAttached();
+ verify(mView, never()).showIcon(
+ eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+ }
+
+ @Test
public void testOnViewAttachedShowsPriorityModeIconWhenEnabled() {
when(mZenModeController.getZen()).thenReturn(
Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
@@ -365,24 +397,6 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
}
@Test
- public void testMicCameraIconHiddenWhenSensorsNotBlocked() {
- when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE))
- .thenReturn(true).thenReturn(false);
- when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA))
- .thenReturn(true).thenReturn(false);
- mController.onViewAttached();
-
- final ArgumentCaptor<IndividualSensorPrivacyController.Callback> callbackCapture =
- ArgumentCaptor.forClass(IndividualSensorPrivacyController.Callback.class);
- verify(mSensorPrivacyController).addCallback(callbackCapture.capture());
- callbackCapture.getValue().onSensorBlockedChanged(
- SensorPrivacyManager.Sensors.MICROPHONE, false);
-
- verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false, null);
- }
-
- @Test
public void testPriorityModeIconShownWhenZenModeEnabled() {
mController.onViewAttached();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java
index ed1cf69ad8c0..964e6d79f0bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.dreams;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -23,11 +25,14 @@ import static org.mockito.Mockito.when;
import android.app.smartspace.SmartspaceTarget;
import android.content.Context;
import android.testing.AndroidTestingRunner;
+import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dreams.smartspace.DreamsSmartspaceController;
+import com.android.systemui.dreams.complication.Complication;
+import com.android.systemui.dreams.complication.ComplicationViewModel;
+import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import org.junit.Before;
@@ -47,7 +52,7 @@ public class SmartSpaceComplicationTest extends SysuiTestCase {
private Context mContext;
@Mock
- private DreamsSmartspaceController mSmartspaceController;
+ private DreamSmartspaceController mSmartspaceController;
@Mock
private DreamOverlayStateController mDreamOverlayStateController;
@@ -55,25 +60,79 @@ public class SmartSpaceComplicationTest extends SysuiTestCase {
@Mock
private SmartSpaceComplication mComplication;
+ @Mock
+ private ComplicationViewModel mComplicationViewModel;
+
+ @Mock
+ private View mBcSmartspaceView;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
/**
- * Ensures {@link SmartSpaceComplication} is only registered when it is available.
+ * Ensures {@link SmartSpaceComplication} isn't registered right away on start.
*/
@Test
- public void testAvailability() {
+ public void testRegistrantStart_doesNotAddComplication() {
+ final SmartSpaceComplication.Registrant registrant = getRegistrant();
+ registrant.start();
+ verify(mDreamOverlayStateController, never()).addComplication(eq(mComplication));
+ }
- final SmartSpaceComplication.Registrant registrant = new SmartSpaceComplication.Registrant(
+ private SmartSpaceComplication.Registrant getRegistrant() {
+ return new SmartSpaceComplication.Registrant(
mContext,
mDreamOverlayStateController,
mComplication,
mSmartspaceController);
+ }
+
+ @Test
+ public void testOverlayActive_addsTargetListener() {
+ final SmartSpaceComplication.Registrant registrant = getRegistrant();
registrant.start();
- verify(mDreamOverlayStateController, never()).addComplication(eq(mComplication));
+ final ArgumentCaptor<DreamOverlayStateController.Callback> dreamCallbackCaptor =
+ ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
+ verify(mDreamOverlayStateController).addCallback(dreamCallbackCaptor.capture());
+
+ when(mDreamOverlayStateController.isOverlayActive()).thenReturn(true);
+ dreamCallbackCaptor.getValue().onStateChanged();
+
+ // Test
+ final ArgumentCaptor<BcSmartspaceDataPlugin.SmartspaceTargetListener> listenerCaptor =
+ ArgumentCaptor.forClass(BcSmartspaceDataPlugin.SmartspaceTargetListener.class);
+ verify(mSmartspaceController).addListener(listenerCaptor.capture());
+ }
+
+ @Test
+ public void testOverlayActive_targetsNonEmpty_addsComplication() {
+ final SmartSpaceComplication.Registrant registrant = getRegistrant();
+ registrant.start();
+
+ final ArgumentCaptor<DreamOverlayStateController.Callback> dreamCallbackCaptor =
+ ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
+ verify(mDreamOverlayStateController).addCallback(dreamCallbackCaptor.capture());
+
+ when(mDreamOverlayStateController.isOverlayActive()).thenReturn(true);
+ dreamCallbackCaptor.getValue().onStateChanged();
+
+ final ArgumentCaptor<BcSmartspaceDataPlugin.SmartspaceTargetListener> listenerCaptor =
+ ArgumentCaptor.forClass(BcSmartspaceDataPlugin.SmartspaceTargetListener.class);
+ verify(mSmartspaceController).addListener(listenerCaptor.capture());
+
+ // Test
+ final SmartspaceTarget target = Mockito.mock(SmartspaceTarget.class);
+ listenerCaptor.getValue().onSmartspaceTargetsUpdated(Arrays.asList(target));
+ verify(mDreamOverlayStateController).addComplication(eq(mComplication));
+ }
+
+ @Test
+ public void testOverlayActive_targetsEmpty_removesComplication() {
+ final SmartSpaceComplication.Registrant registrant = getRegistrant();
+ registrant.start();
final ArgumentCaptor<DreamOverlayStateController.Callback> dreamCallbackCaptor =
ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
@@ -89,5 +148,45 @@ public class SmartSpaceComplicationTest extends SysuiTestCase {
final SmartspaceTarget target = Mockito.mock(SmartspaceTarget.class);
listenerCaptor.getValue().onSmartspaceTargetsUpdated(Arrays.asList(target));
verify(mDreamOverlayStateController).addComplication(eq(mComplication));
+
+ // Test
+ listenerCaptor.getValue().onSmartspaceTargetsUpdated(Arrays.asList());
+ verify(mDreamOverlayStateController).removeComplication(eq(mComplication));
+ }
+
+ @Test
+ public void testOverlayInActive_removesTargetListener_removesComplication() {
+ final SmartSpaceComplication.Registrant registrant = getRegistrant();
+ registrant.start();
+
+ final ArgumentCaptor<DreamOverlayStateController.Callback> dreamCallbackCaptor =
+ ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
+ verify(mDreamOverlayStateController).addCallback(dreamCallbackCaptor.capture());
+
+ when(mDreamOverlayStateController.isOverlayActive()).thenReturn(true);
+ dreamCallbackCaptor.getValue().onStateChanged();
+
+ final ArgumentCaptor<BcSmartspaceDataPlugin.SmartspaceTargetListener> listenerCaptor =
+ ArgumentCaptor.forClass(BcSmartspaceDataPlugin.SmartspaceTargetListener.class);
+ verify(mSmartspaceController).addListener(listenerCaptor.capture());
+
+ final SmartspaceTarget target = Mockito.mock(SmartspaceTarget.class);
+ listenerCaptor.getValue().onSmartspaceTargetsUpdated(Arrays.asList(target));
+ verify(mDreamOverlayStateController).addComplication(eq(mComplication));
+
+ // Test
+ when(mDreamOverlayStateController.isOverlayActive()).thenReturn(false);
+ dreamCallbackCaptor.getValue().onStateChanged();
+ verify(mSmartspaceController).removeListener(listenerCaptor.getValue());
+ verify(mDreamOverlayStateController).removeComplication(eq(mComplication));
+ }
+
+ @Test
+ public void testGetView_reusesSameView() {
+ final SmartSpaceComplication complication = new SmartSpaceComplication(getContext(),
+ mSmartspaceController);
+ final Complication.ViewHolder viewHolder = complication.createView(mComplicationViewModel);
+ when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mBcSmartspaceView);
+ assertEquals(viewHolder.getView(), viewHolder.getView());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java
deleted file mode 100644
index 151742af7e1a..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.dreams.complication;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class DreamWeatherComplicationTest extends SysuiTestCase {
- @SuppressWarnings("HidingField")
- @Mock
- private Context mContext;
-
- @Mock
- private LockscreenSmartspaceController mSmartspaceController;
-
- @Mock
- private DreamOverlayStateController mDreamOverlayStateController;
-
- @Mock
- private DreamWeatherComplication mComplication;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- /**
- * Ensures {@link DreamWeatherComplication} is only registered when it is available.
- */
- @Test
- public void testComplicationAvailability() {
- when(mSmartspaceController.isEnabled()).thenReturn(false);
- final DreamWeatherComplication.Registrant registrant =
- new DreamWeatherComplication.Registrant(
- mContext,
- mSmartspaceController,
- mDreamOverlayStateController,
- mComplication);
- registrant.start();
- verify(mDreamOverlayStateController, never()).addComplication(any());
-
- when(mSmartspaceController.isEnabled()).thenReturn(true);
- registrant.start();
- verify(mDreamOverlayStateController).addComplication(eq(mComplication));
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index 89c82fbff901..c3fca29a9883 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -422,7 +422,29 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_BOUNCER_FULLY_VISIBLE);
}
+ /**
+ * Ensures {@link CentralSurfaces}
+ */
+ @Test
+ public void testInformBouncerShowingOnExpand() {
+ swipeToPosition(1f, Direction.UP, 0);
+ }
+
+ /**
+ * Ensures {@link CentralSurfaces}
+ */
+ @Test
+ public void testInformBouncerHidingOnCollapse() {
+ // Must swipe up to set initial state.
+ swipeToPosition(1f, Direction.UP, 0);
+ Mockito.clearInvocations(mCentralSurfaces);
+
+ swipeToPosition(0f, Direction.DOWN, 0);
+ }
+
+
private void swipeToPosition(float percent, Direction direction, float velocityY) {
+ Mockito.clearInvocations(mTouchSession);
mTouchHandler.onSessionStart(mTouchSession);
ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/HideComplicationTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/HideComplicationTouchHandlerTest.java
index 0a021331d9d1..14a5702c8e5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/HideComplicationTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/HideComplicationTouchHandlerTest.java
@@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.complication.Complication;
import com.android.systemui.shared.system.InputChannelCompat;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -59,6 +60,9 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
TouchInsetManager mTouchInsetManager;
@Mock
+ StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+
+ @Mock
Handler mHandler;
@Mock
@@ -83,12 +87,45 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
mVisibilityController,
RESTORE_TIMEOUT,
mTouchInsetManager,
+ mStatusBarKeyguardViewManager,
mFakeExecutor,
mHandler);
// Report multiple active sessions.
when(mSession.getActiveSessionCount()).thenReturn(2);
+ // Bouncer hidden.
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+
+ // Start session.
+ touchHandler.onSessionStart(mSession);
+
+ // Verify session end.
+ verify(mSession).pop();
+
+ // Verify no interaction with visibility controller.
+ verify(mVisibilityController, never()).setVisibility(anyInt(), anyBoolean());
+ }
+
+ /**
+ * Ensures no actions are taken when the bouncer is showing.
+ */
+ @Test
+ public void testSessionEndWhenBouncerShowing() {
+ final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler(
+ mVisibilityController,
+ RESTORE_TIMEOUT,
+ mTouchInsetManager,
+ mStatusBarKeyguardViewManager,
+ mFakeExecutor,
+ mHandler);
+
+ // Report one session.
+ when(mSession.getActiveSessionCount()).thenReturn(1);
+
+ // Bouncer is showing.
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
+
// Start session.
touchHandler.onSessionStart(mSession);
@@ -108,12 +145,16 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
mVisibilityController,
RESTORE_TIMEOUT,
mTouchInsetManager,
+ mStatusBarKeyguardViewManager,
mFakeExecutor,
mHandler);
// Report one session
when(mSession.getActiveSessionCount()).thenReturn(1);
+ // Bouncer hidden.
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+
// Start session
touchHandler.onSessionStart(mSession);
@@ -149,12 +190,16 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase {
mVisibilityController,
RESTORE_TIMEOUT,
mTouchInsetManager,
+ mStatusBarKeyguardViewManager,
mFakeExecutor,
mHandler);
// Report one session
when(mSession.getActiveSessionCount()).thenReturn(1);
+ // Bouncer hidden.
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+
// Start session
touchHandler.onSessionStart(mSession);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
index b43856aae4cf..51f3404a9bca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
@@ -26,6 +26,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.DeviceConfigProxyFake
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
@@ -75,6 +76,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
private lateinit var broadcastReceiver: BroadcastReceiver
private lateinit var clearCacheAction: Consumer<Int>
+ private val deviceConfig = DeviceConfigProxyFake()
private val teamfoodableFlagA = BooleanFlag(500, false, true)
private val teamfoodableFlagB = BooleanFlag(501, true, true)
@@ -90,6 +92,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
systemProperties,
resources,
dumpManager,
+ deviceConfig,
flagMap,
commandRegistry,
barService
@@ -195,6 +198,21 @@ class FeatureFlagsDebugTest : SysuiTestCase() {
}
@Test
+ fun testReadDeviceConfigBooleanFlag() {
+ val namespace = "test_namespace"
+ deviceConfig.setProperty(namespace, "a", "true", false)
+ deviceConfig.setProperty(namespace, "b", "false", false)
+ deviceConfig.setProperty(namespace, "c", null, false)
+
+ assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(1, "a", namespace)))
+ .isTrue()
+ assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(2, "b", namespace)))
+ .isFalse()
+ assertThat(mFeatureFlagsDebug.isEnabled(DeviceConfigBooleanFlag(3, "c", namespace)))
+ .isFalse()
+ }
+
+ @Test
fun testReadStringFlag() {
whenever(flagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo")
whenever(flagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
index b5deb0b55e11..6b683f456ea7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
@@ -20,6 +20,7 @@ import android.content.res.Resources
import android.test.suitebuilder.annotation.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.DeviceConfigProxyFake
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.After
@@ -44,10 +45,13 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
@Mock private lateinit var mSystemProperties: SystemPropertiesHelper
@Mock private lateinit var mDumpManager: DumpManager
+ private val deviceConfig = DeviceConfigProxyFake()
+
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- mFeatureFlagsRelease = FeatureFlagsRelease(mResources, mSystemProperties, mDumpManager)
+ mFeatureFlagsRelease = FeatureFlagsRelease(mResources, mSystemProperties, deviceConfig,
+ mDumpManager)
}
@After
@@ -85,6 +89,21 @@ class FeatureFlagsReleaseTest : SysuiTestCase() {
}
@Test
+ fun testReadDeviceConfigBooleanFlag() {
+ val namespace = "test_namespace"
+ deviceConfig.setProperty(namespace, "a", "true", false)
+ deviceConfig.setProperty(namespace, "b", "false", false)
+ deviceConfig.setProperty(namespace, "c", null, false)
+
+ assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(1, "a", namespace)))
+ .isTrue()
+ assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(2, "b", namespace)))
+ .isFalse()
+ assertThat(mFeatureFlagsRelease.isEnabled(DeviceConfigBooleanFlag(3, "c", namespace)))
+ .isFalse()
+ }
+
+ @Test
fun testSysPropBooleanFlag() {
val flagId = 213
val flagName = "sys_prop_flag"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index f00fbe6ac4d7..141a213a5b6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -480,4 +481,13 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
// hide dialog again
mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
}
+
+ @Test
+ public void testBugreportAction_whenDebugMode_shouldOfferBugreportButtonBeforeProvisioning() {
+ doReturn(1).when(mGlobalSettings).getInt(anyString(), anyInt());
+
+ GlobalActionsDialogLite.BugReportAction bugReportAction =
+ mGlobalActionsDialogLite.makeBugReportActionForTesting();
+ assertThat(bugReportAction.showBeforeProvisioning()).isTrue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index 51c258055465..23516c94d851 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -45,6 +45,7 @@ import androidx.slice.core.SliceQuery;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SystemUIInitializerImpl;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -100,7 +101,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mIsZenMode = false;
mProvider = new TestableKeyguardSliceProvider();
- mProvider.setContextAvailableCallback(context -> { });
+ mProvider.setContextAvailableCallback(context -> new SystemUIInitializerImpl(mContext));
mProvider.attachInfo(getContext(), null);
reset(mContentResolver);
SliceProvider.setSpecs(new HashSet<>(Arrays.asList(SliceSpecs.LIST)));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index a80aed7a6d18..9b665555562a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -203,6 +203,13 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mViewMediator.mViewMediatorCallback.getBouncerPromptReason());
}
+ @Test
+ public void testHideSurfaceBehindKeyguardMarksKeyguardNotGoingAway() {
+ mViewMediator.hideSurfaceBehindKeyguard();
+
+ verify(mKeyguardStateController).notifyKeyguardGoingAway(false);
+ }
+
private void createAndStartViewMediator() {
mViewMediator = new KeyguardViewMediator(
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/WindowAddedViewLifecycleOwnerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/WindowAddedViewLifecycleOwnerTest.kt
new file mode 100644
index 000000000000..4f5c570ee812
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/WindowAddedViewLifecycleOwnerTest.kt
@@ -0,0 +1,150 @@
+package com.android.systemui.lifecycle
+
+import android.view.View
+import android.view.ViewTreeObserver
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleRegistry
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class WindowAddedViewLifecycleOwnerTest : SysuiTestCase() {
+
+ @Mock lateinit var view: View
+ @Mock lateinit var viewTreeObserver: ViewTreeObserver
+
+ private lateinit var underTest: WindowAddedViewLifecycleOwner
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(view.viewTreeObserver).thenReturn(viewTreeObserver)
+ whenever(view.isAttachedToWindow).thenReturn(false)
+ whenever(view.windowVisibility).thenReturn(View.INVISIBLE)
+ whenever(view.hasWindowFocus()).thenReturn(false)
+
+ underTest = WindowAddedViewLifecycleOwner(view) { LifecycleRegistry.createUnsafe(it) }
+ }
+
+ @Test
+ fun `detached - invisible - does not have focus -- INITIALIZED`() {
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
+ }
+
+ @Test
+ fun `detached - invisible - has focus -- INITIALIZED`() {
+ whenever(view.hasWindowFocus()).thenReturn(true)
+ val captor = argumentCaptor<ViewTreeObserver.OnWindowFocusChangeListener>()
+ verify(viewTreeObserver).addOnWindowFocusChangeListener(capture(captor))
+ captor.value.onWindowFocusChanged(true)
+
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
+ }
+
+ @Test
+ fun `detached - visible - does not have focus -- INITIALIZED`() {
+ whenever(view.windowVisibility).thenReturn(View.VISIBLE)
+ val captor = argumentCaptor<ViewTreeObserver.OnWindowVisibilityChangeListener>()
+ verify(viewTreeObserver).addOnWindowVisibilityChangeListener(capture(captor))
+ captor.value.onWindowVisibilityChanged(View.VISIBLE)
+
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
+ }
+
+ @Test
+ fun `detached - visible - has focus -- INITIALIZED`() {
+ whenever(view.hasWindowFocus()).thenReturn(true)
+ val focusCaptor = argumentCaptor<ViewTreeObserver.OnWindowFocusChangeListener>()
+ verify(viewTreeObserver).addOnWindowFocusChangeListener(capture(focusCaptor))
+ focusCaptor.value.onWindowFocusChanged(true)
+
+ whenever(view.windowVisibility).thenReturn(View.VISIBLE)
+ val visibilityCaptor = argumentCaptor<ViewTreeObserver.OnWindowVisibilityChangeListener>()
+ verify(viewTreeObserver).addOnWindowVisibilityChangeListener(capture(visibilityCaptor))
+ visibilityCaptor.value.onWindowVisibilityChanged(View.VISIBLE)
+
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
+ }
+
+ @Test
+ fun `attached - invisible - does not have focus -- CREATED`() {
+ whenever(view.isAttachedToWindow).thenReturn(true)
+ val captor = argumentCaptor<ViewTreeObserver.OnWindowAttachListener>()
+ verify(viewTreeObserver).addOnWindowAttachListener(capture(captor))
+ captor.value.onWindowAttached()
+
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED)
+ }
+
+ @Test
+ fun `attached - invisible - has focus -- CREATED`() {
+ whenever(view.isAttachedToWindow).thenReturn(true)
+ val attachCaptor = argumentCaptor<ViewTreeObserver.OnWindowAttachListener>()
+ verify(viewTreeObserver).addOnWindowAttachListener(capture(attachCaptor))
+ attachCaptor.value.onWindowAttached()
+
+ whenever(view.hasWindowFocus()).thenReturn(true)
+ val focusCaptor = argumentCaptor<ViewTreeObserver.OnWindowFocusChangeListener>()
+ verify(viewTreeObserver).addOnWindowFocusChangeListener(capture(focusCaptor))
+ focusCaptor.value.onWindowFocusChanged(true)
+
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED)
+ }
+
+ @Test
+ fun `attached - visible - does not have focus -- STARTED`() {
+ whenever(view.isAttachedToWindow).thenReturn(true)
+ val attachCaptor = argumentCaptor<ViewTreeObserver.OnWindowAttachListener>()
+ verify(viewTreeObserver).addOnWindowAttachListener(capture(attachCaptor))
+ attachCaptor.value.onWindowAttached()
+
+ whenever(view.windowVisibility).thenReturn(View.VISIBLE)
+ val visibilityCaptor = argumentCaptor<ViewTreeObserver.OnWindowVisibilityChangeListener>()
+ verify(viewTreeObserver).addOnWindowVisibilityChangeListener(capture(visibilityCaptor))
+ visibilityCaptor.value.onWindowVisibilityChanged(View.VISIBLE)
+
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED)
+ }
+
+ @Test
+ fun `attached - visible - has focus -- RESUMED`() {
+ whenever(view.isAttachedToWindow).thenReturn(true)
+ val attachCaptor = argumentCaptor<ViewTreeObserver.OnWindowAttachListener>()
+ verify(viewTreeObserver).addOnWindowAttachListener(capture(attachCaptor))
+ attachCaptor.value.onWindowAttached()
+
+ whenever(view.hasWindowFocus()).thenReturn(true)
+ val focusCaptor = argumentCaptor<ViewTreeObserver.OnWindowFocusChangeListener>()
+ verify(viewTreeObserver).addOnWindowFocusChangeListener(capture(focusCaptor))
+ focusCaptor.value.onWindowFocusChanged(true)
+
+ whenever(view.windowVisibility).thenReturn(View.VISIBLE)
+ val visibilityCaptor = argumentCaptor<ViewTreeObserver.OnWindowVisibilityChangeListener>()
+ verify(viewTreeObserver).addOnWindowVisibilityChangeListener(capture(visibilityCaptor))
+ visibilityCaptor.value.onWindowVisibilityChanged(View.VISIBLE)
+
+ assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.RESUMED)
+ }
+
+ @Test
+ fun dispose() {
+ underTest.dispose()
+
+ verify(viewTreeObserver).removeOnWindowAttachListener(any())
+ verify(viewTreeObserver).removeOnWindowVisibilityChangeListener(any())
+ verify(viewTreeObserver).removeOnWindowFocusChangeListener(any())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt
index b97924107322..f56d42ec3fb4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.media
-import org.mockito.Mockito.`when` as whenever
import android.animation.ValueAnimator
import android.graphics.Color
import android.testing.AndroidTestingRunner
@@ -34,6 +33,7 @@ import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
private const val DEFAULT_COLOR = Color.RED
@@ -147,8 +147,8 @@ class ColorSchemeTransitionTest : SysuiTestCase() {
@Test
fun testColorSchemeTransition_update() {
- colorSchemeTransition.updateColorScheme(colorScheme, true)
- verify(mockAnimatingTransition, times(10)).updateColorScheme(colorScheme)
+ colorSchemeTransition.updateColorScheme(colorScheme)
+ verify(mockAnimatingTransition, times(8)).updateColorScheme(colorScheme)
verify(gutsViewHolder).colorScheme = colorScheme
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 6f4579bb14b5..178502269e73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -21,7 +21,6 @@ import android.animation.AnimatorSet
import android.app.PendingIntent
import android.app.smartspace.SmartspaceAction
import android.content.Context
-import org.mockito.Mockito.`when` as whenever
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
@@ -58,6 +57,7 @@ import com.android.internal.logging.InstanceId
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.bluetooth.BroadcastDialogController
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.media.MediaControlPanel.KEY_SMARTSPACE_APP_NAME
import com.android.systemui.media.dialog.MediaOutputDialogFactory
@@ -68,8 +68,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.KotlinArgumentCaptor
-import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.withArgCaptor
@@ -92,6 +92,7 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
private const val KEY = "TEST_KEY"
@@ -104,6 +105,7 @@ private const val SESSION_ARTIST = "SESSION_ARTIST"
private const val SESSION_TITLE = "SESSION_TITLE"
private const val DISABLED_DEVICE_NAME = "DISABLED_DEVICE_NAME"
private const val REC_APP_NAME = "REC APP NAME"
+private const val APP_NAME = "APP_NAME"
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -130,6 +132,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Mock private lateinit var mediaCarouselController: MediaCarouselController
@Mock private lateinit var falsingManager: FalsingManager
@Mock private lateinit var transitionParent: ViewGroup
+ @Mock private lateinit var broadcastDialogController: BroadcastDialogController
private lateinit var appIcon: ImageView
@Mock private lateinit var albumView: ImageView
private lateinit var titleText: TextView
@@ -160,8 +163,9 @@ public class MediaControlPanelTest : SysuiTestCase() {
private lateinit var dismissText: TextView
private lateinit var session: MediaSession
- private val device = MediaDeviceData(true, null, DEVICE_NAME)
- private val disabledDevice = MediaDeviceData(false, null, DISABLED_DEVICE_NAME)
+ private lateinit var device: MediaDeviceData
+ private val disabledDevice = MediaDeviceData(false, null, DISABLED_DEVICE_NAME, null,
+ showBroadcastButton = false)
private lateinit var mediaData: MediaData
private val clock = FakeSystemClock()
@Mock private lateinit var logger: MediaUiEventLogger
@@ -187,6 +191,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
private lateinit var recSubtitle1: TextView
private lateinit var recSubtitle2: TextView
private lateinit var recSubtitle3: TextView
+ private var shouldShowBroadcastButton: Boolean = false
@JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -223,7 +228,8 @@ public class MediaControlPanelTest : SysuiTestCase() {
logger,
keyguardStateController,
activityIntentHelper,
- lockscreenUserManager) {
+ lockscreenUserManager,
+ broadcastDialogController) {
override fun loadAnimator(
animId: Int,
otionInterpolator: Interpolator,
@@ -236,28 +242,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
initGutsViewHolderMocks()
initMediaViewHolderMocks()
- // Create media session
- val metadataBuilder = MediaMetadata.Builder().apply {
- putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST)
- putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
- }
- val playbackBuilder = PlaybackState.Builder().apply {
- setState(PlaybackState.STATE_PAUSED, 6000L, 1f)
- setActions(PlaybackState.ACTION_PLAY)
- }
- session = MediaSession(context, SESSION_KEY).apply {
- setMetadata(metadataBuilder.build())
- setPlaybackState(playbackBuilder.build())
- }
- session.setActive(true)
-
- mediaData = MediaTestUtils.emptyMediaData.copy(
- artist = ARTIST,
- song = TITLE,
- packageName = PACKAGE,
- token = session.sessionToken,
- device = device,
- instanceId = instanceId)
+ initDeviceMediaData(false, DEVICE_NAME)
// Set up recommendation view
initRecommendationViewHolderMocks()
@@ -293,6 +278,34 @@ public class MediaControlPanelTest : SysuiTestCase() {
whenever(gutsViewHolder.dismissText).thenReturn(dismissText)
}
+ private fun initDeviceMediaData(shouldShowBroadcastButton: Boolean, name: String) {
+ device = MediaDeviceData(true, null, name, null,
+ showBroadcastButton = shouldShowBroadcastButton)
+
+ // Create media session
+ val metadataBuilder = MediaMetadata.Builder().apply {
+ putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST)
+ putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
+ }
+ val playbackBuilder = PlaybackState.Builder().apply {
+ setState(PlaybackState.STATE_PAUSED, 6000L, 1f)
+ setActions(PlaybackState.ACTION_PLAY)
+ }
+ session = MediaSession(context, SESSION_KEY).apply {
+ setMetadata(metadataBuilder.build())
+ setPlaybackState(playbackBuilder.build())
+ }
+ session.setActive(true)
+
+ mediaData = MediaTestUtils.emptyMediaData.copy(
+ artist = ARTIST,
+ song = TITLE,
+ packageName = PACKAGE,
+ token = session.sessionToken,
+ device = device,
+ instanceId = instanceId)
+ }
+
/**
* Initialize elements in media view holder
*/
@@ -342,6 +355,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
whenever(viewHolder.player).thenReturn(view)
whenever(viewHolder.appIcon).thenReturn(appIcon)
whenever(viewHolder.albumView).thenReturn(albumView)
+ whenever(albumView.foreground).thenReturn(mock(Drawable::class.java))
whenever(viewHolder.titleText).thenReturn(titleText)
whenever(viewHolder.artistText).thenReturn(artistText)
whenever(seamlessBackground.getDrawable(0)).thenReturn(mock(GradientDrawable::class.java))
@@ -1045,6 +1059,28 @@ public class MediaControlPanelTest : SysuiTestCase() {
assertThat(seamless.isEnabled()).isFalse()
}
+ @Test
+ fun bindBroadcastButton() {
+ initMediaViewHolderMocks()
+ initDeviceMediaData(true, APP_NAME)
+
+ val mockAvd0 = mock(AnimatedVectorDrawable::class.java)
+ whenever(mockAvd0.mutate()).thenReturn(mockAvd0)
+ val semanticActions0 = MediaButton(
+ playOrPause = MediaAction(mockAvd0, Runnable {}, "play", null)
+ )
+ val state = mediaData.copy(resumption = true, semanticActions = semanticActions0,
+ isPlaying = false)
+ player.attachPlayer(viewHolder)
+ player.bindPlayer(state, PACKAGE)
+ assertThat(seamlessText.getText()).isEqualTo(APP_NAME)
+ assertThat(seamless.isEnabled()).isTrue()
+
+ seamless.callOnClick()
+
+ verify(logger).logOpenBroadcastDialog(anyInt(), eq(PACKAGE), eq(instanceId))
+ }
+
/* ***** Guts tests for the player ***** */
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index 3e335c5163a9..04b93d79f83b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -79,7 +79,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase {
new ArrayList<>(), new ArrayList<>(), null, PACKAGE, null, null, null, true, null,
MediaData.PLAYBACK_LOCAL, false, KEY, false, false, false, 0L,
InstanceId.fakeInstanceId(-1), -1);
- mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME);
+ mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME, null, false);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index 187210a40b6d..ee104262dc29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -16,6 +16,10 @@
package com.android.systemui.media
+import android.bluetooth.BluetoothLeBroadcast
+import android.bluetooth.BluetoothLeBroadcastMetadata
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
@@ -25,8 +29,12 @@ import android.media.session.MediaSession
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager
@@ -42,6 +50,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.mock
@@ -60,6 +69,8 @@ private const val SESSION_KEY = "SESSION_KEY"
private const val DEVICE_ID = "DEVICE_ID"
private const val DEVICE_NAME = "DEVICE_NAME"
private const val REMOTE_DEVICE_NAME = "REMOTE_DEVICE_NAME"
+private const val BROADCAST_APP_NAME = "BROADCAST_APP_NAME"
+private const val NORMAL_APP_NAME = "NORMAL_APP_NAME"
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -83,6 +94,12 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Mock private lateinit var controller: MediaController
@Mock private lateinit var playbackInfo: PlaybackInfo
@Mock private lateinit var configurationController: ConfigurationController
+ @Mock private lateinit var bluetoothLeBroadcast: BluetoothLeBroadcast
+ @Mock private lateinit var localBluetoothProfileManager: LocalBluetoothProfileManager
+ @Mock private lateinit var localBluetoothLeBroadcast: LocalBluetoothLeBroadcast
+ @Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var applicationInfo: ApplicationInfo
+ private lateinit var localBluetoothManager: LocalBluetoothManager
private lateinit var session: MediaSession
private lateinit var mediaData: MediaData
@JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -91,12 +108,15 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
fun setUp() {
fakeFgExecutor = FakeExecutor(FakeSystemClock())
fakeBgExecutor = FakeExecutor(FakeSystemClock())
+ localBluetoothManager = mDependency.injectMockDependency(LocalBluetoothManager::class.java)
manager = MediaDeviceManager(
+ context,
controllerFactory,
lmmFactory,
mr2,
muteAwaitFactory,
configurationController,
+ localBluetoothManager,
fakeFgExecutor,
fakeBgExecutor,
dumpster
@@ -119,6 +139,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
token = session.sessionToken)
whenever(controllerFactory.create(session.sessionToken))
.thenReturn(controller)
+ setupLeAudioConfiguration(false)
}
@After
@@ -545,7 +566,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Test
fun testRemotePlaybackDeviceOverride() {
whenever(route.name).thenReturn(DEVICE_NAME)
- val deviceData = MediaDeviceData(false, null, REMOTE_DEVICE_NAME, null)
+ val deviceData = MediaDeviceData(false, null, REMOTE_DEVICE_NAME, null,
+ showBroadcastButton = false)
val mediaDataWithDevice = mediaData.copy(device = deviceData)
// GIVEN media data that already has a device set
@@ -560,12 +582,95 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
verify(lmm, never()).registerCallback(any())
}
+ @Test
+ fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting() {
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(true)
+ setupBroadcastPackage(BROADCAST_APP_NAME)
+ broadcastCallback.onBroadcastStarted(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isTrue()
+ assertThat(data.enabled).isTrue()
+ assertThat(data.name).isEqualTo(context.getString(
+ R.string.broadcasting_description_is_broadcasting))
+ }
+
+ @Test
+ fun onBroadcastStarted_currentMediaDeviceDataIsNotBroadcasting() {
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(true)
+ setupBroadcastPackage(NORMAL_APP_NAME)
+ broadcastCallback.onBroadcastStarted(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isTrue()
+ assertThat(data.enabled).isTrue()
+ assertThat(data.name).isEqualTo(BROADCAST_APP_NAME)
+ }
+
+ @Test
+ fun onBroadcastStopped_bluetoothLeBroadcastIsDisabledAndBroadcastingButtonIsGone() {
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(false)
+ broadcastCallback.onBroadcastStopped(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isFalse()
+ }
+
fun captureCallback(): LocalMediaManager.DeviceCallback {
val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java)
verify(lmm).registerCallback(captor.capture())
return captor.getValue()
}
+ fun setupBroadcastCallback(): BluetoothLeBroadcast.Callback {
+ val callback: BluetoothLeBroadcast.Callback = object : BluetoothLeBroadcast.Callback {
+ override fun onBroadcastStarted(reason: Int, broadcastId: Int) {}
+ override fun onBroadcastStartFailed(reason: Int) {}
+ override fun onBroadcastStopped(reason: Int, broadcastId: Int) {}
+ override fun onBroadcastStopFailed(reason: Int) {}
+ override fun onPlaybackStarted(reason: Int, broadcastId: Int) {}
+ override fun onPlaybackStopped(reason: Int, broadcastId: Int) {}
+ override fun onBroadcastUpdated(reason: Int, broadcastId: Int) {}
+ override fun onBroadcastUpdateFailed(reason: Int, broadcastId: Int) {}
+ override fun onBroadcastMetadataChanged(broadcastId: Int,
+ metadata: BluetoothLeBroadcastMetadata) {}
+ }
+
+ bluetoothLeBroadcast.registerCallback(fakeFgExecutor, callback)
+ return callback;
+ }
+
+ fun setupLeAudioConfiguration(isLeAudio: Boolean) {
+ whenever(localBluetoothManager.profileManager).thenReturn(localBluetoothProfileManager)
+ whenever(localBluetoothProfileManager.leAudioBroadcastProfile)
+ .thenReturn(localBluetoothLeBroadcast)
+ whenever(localBluetoothLeBroadcast.isEnabled(any())).thenReturn(isLeAudio)
+ whenever(localBluetoothLeBroadcast.appSourceName).thenReturn(BROADCAST_APP_NAME)
+ }
+
+ fun setupBroadcastPackage(currentName: String) {
+ whenever(lmm.packageName).thenReturn(PACKAGE)
+ whenever(packageManager.getApplicationInfo(eq(PACKAGE), anyInt()))
+ .thenReturn(applicationInfo)
+ whenever(packageManager.getApplicationLabel(applicationInfo)).thenReturn(currentName)
+ context.setMockPackageManager(packageManager)
+ }
+
fun captureDeviceData(key: String, oldKey: String? = null): MediaDeviceData {
val captor = ArgumentCaptor.forClass(MediaDeviceData::class.java)
verify(listener).onMediaDeviceChanged(eq(key), eq(oldKey), captor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTestUtils.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTestUtils.kt
index ae58fe67da23..3d9ed5fe7f7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTestUtils.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTestUtils.kt
@@ -20,7 +20,8 @@ class MediaTestUtils {
device = null,
active = true,
resumeAction = null,
+ isPlaying = false,
instanceId = InstanceId.fakeInstanceId(-1),
appUid = -1)
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 59475cf0cb90..568e0cb22f18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -71,14 +71,13 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
@Before
public void setUp() {
- mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController, mMediaOutputDialog);
+ mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
.onCreateViewHolder(new LinearLayout(mContext), 0);
mSpyMediaOutputSeekbar = spy(mViewHolder.mSeekBar);
when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices);
when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false);
- when(mMediaOutputController.isZeroMode()).thenReturn(false);
when(mMediaOutputController.isTransferring()).thenReturn(false);
when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat);
@@ -98,28 +97,12 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
}
@Test
- public void getItemCount_nonZeroMode_isDeviceSize() {
- assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size());
- }
-
- @Test
- public void getItemCount_zeroMode_containExtraOneForPairNew() {
- when(mMediaOutputController.isZeroMode()).thenReturn(true);
-
+ public void getItemCount_containExtraOneForPairNew() {
assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size() + 1);
}
@Test
- public void getItemCount_withDynamicGroup_containExtraOneForGroup() {
- when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
- when(mMediaOutputController.isZeroMode()).thenReturn(false);
-
- assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size());
- }
-
- @Test
- public void onBindViewHolder_zeroMode_bindPairNew_verifyView() {
- when(mMediaOutputController.isZeroMode()).thenReturn(true);
+ public void onBindViewHolder_bindPairNew_verifyView() {
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -133,7 +116,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
@Test
public void onBindViewHolder_bindGroup_withSessionName_verifyView() {
when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
- when(mMediaOutputController.isZeroMode()).thenReturn(false);
when(mMediaOutputController.getSessionName()).thenReturn(TEST_SESSION_NAME);
mMediaOutputAdapter.getItemCount();
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
@@ -148,7 +130,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
@Test
public void onBindViewHolder_bindGroup_noSessionName_verifyView() {
when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
- when(mMediaOutputController.isZeroMode()).thenReturn(false);
when(mMediaOutputController.getSessionName()).thenReturn(null);
mMediaOutputAdapter.getItemCount();
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
@@ -257,7 +238,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
@Test
public void onItemClick_clickPairNew_verifyLaunchBluetoothPairing() {
- when(mMediaOutputController.isZeroMode()).thenReturn(true);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
mViewHolder.mContainerLayout.performClick();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 9eaa20c2afed..d414660018a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -43,6 +43,8 @@ import android.widget.TextView;
import androidx.core.graphics.drawable.IconCompat;
import androidx.test.filters.SmallTest;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -98,10 +100,17 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
private CharSequence mHeaderSubtitle;
private String mStopText;
private boolean mIsBroadcasting;
+ private boolean mIsBroadcastIconVisibility;
+
@Before
public void setUp() {
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
+ final CachedBluetoothDeviceManager cachedBluetoothDeviceManager = mock(
+ CachedBluetoothDeviceManager.class);
+ when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(
+ cachedBluetoothDeviceManager);
+ when(cachedBluetoothDeviceManager.findDevice(any())).thenReturn(null);
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null);
when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_NONE);
@@ -153,6 +162,27 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
}
@Test
+ public void refresh_broadcastIconVisibilityOff_broadcastIconLayoutNotVisible() {
+ mIsBroadcastIconVisibility = false;
+
+ mMediaOutputBaseDialogImpl.refresh();
+ final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.broadcast_icon);
+
+ assertThat(view.getVisibility()).isEqualTo(View.GONE);
+ }
+ @Test
+ public void refresh_broadcastIconVisibilityOn_broadcastIconLayoutVisible() {
+ mIsBroadcastIconVisibility = true;
+
+ mMediaOutputBaseDialogImpl.refresh();
+ final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
+ R.id.broadcast_icon);
+
+ assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
public void refresh_checkTitle() {
mHeaderTitle = "test_string";
@@ -308,5 +338,10 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
public CharSequence getStopButtonText() {
return mStopText;
}
+
+ @Override
+ public int getBroadcastIconVisibility() {
+ return mIsBroadcastIconVisibility ? View.VISIBLE : View.GONE;
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 2bf5f0fcbfcb..751c8951859c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -383,62 +383,6 @@ public class MediaOutputControllerTest extends SysuiTestCase {
}
@Test
- public void isZeroMode_onlyFromPhoneOutput_returnTrue() {
- // Multiple available devices
- assertThat(mMediaOutputController.isZeroMode()).isFalse();
- when(mMediaDevice1.getDeviceType()).thenReturn(
- MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE);
- mMediaDevices.clear();
- mMediaDevices.add(mMediaDevice1);
- mMediaOutputController.start(mCb);
- mMediaOutputController.onDeviceListUpdate(mMediaDevices);
-
- assertThat(mMediaOutputController.isZeroMode()).isTrue();
-
- when(mMediaDevice1.getDeviceType()).thenReturn(
- MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE);
-
- assertThat(mMediaOutputController.isZeroMode()).isTrue();
-
- when(mMediaDevice1.getDeviceType()).thenReturn(
- MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE);
-
- assertThat(mMediaOutputController.isZeroMode()).isTrue();
- }
-
- @Test
- public void isZeroMode_notFromPhoneOutput_returnFalse() {
- when(mMediaDevice1.getDeviceType()).thenReturn(
- MediaDevice.MediaDeviceType.TYPE_UNKNOWN);
- mMediaDevices.clear();
- mMediaDevices.add(mMediaDevice1);
- mMediaOutputController.start(mCb);
- mMediaOutputController.onDeviceListUpdate(mMediaDevices);
-
- assertThat(mMediaOutputController.isZeroMode()).isFalse();
-
- when(mMediaDevice1.getDeviceType()).thenReturn(
- MediaDevice.MediaDeviceType.TYPE_FAST_PAIR_BLUETOOTH_DEVICE);
-
- assertThat(mMediaOutputController.isZeroMode()).isFalse();
-
- when(mMediaDevice1.getDeviceType()).thenReturn(
- MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
-
- assertThat(mMediaOutputController.isZeroMode()).isFalse();
-
- when(mMediaDevice1.getDeviceType()).thenReturn(
- MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE);
-
- assertThat(mMediaOutputController.isZeroMode()).isFalse();
-
- when(mMediaDevice1.getDeviceType()).thenReturn(
- MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE);
-
- assertThat(mMediaOutputController.isZeroMode()).isFalse();
- }
-
- @Test
public void getGroupMediaDevices_differentDeviceOrder_showingSameOrder() {
final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index ef0fc95f95b2..6afed1a846b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -140,16 +140,31 @@ public class MediaOutputDialogTest extends SysuiTestCase {
mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK);
assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
+ }
- mFeatures.clear();
+ @Test
+ public void getStopButtonVisibility_remoteBLEDevice_returnVisible() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
when(mLocalBluetoothLeBroadcast.isEnabled(any())).thenReturn(false);
when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_PLAYING);
+ when(mMediaDevice.isBLEDevice()).thenReturn(true);
+
assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
}
@Test
+ public void getStopButtonVisibility_remoteNonBLEDevice_returnGone() {
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+ mLocalBluetoothLeBroadcast);
+ when(mLocalBluetoothLeBroadcast.isEnabled(any())).thenReturn(false);
+ when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_PLAYING);
+ when(mMediaDevice.isBLEDevice()).thenReturn(false);
+
+ assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
public void getStopButtonVisibility_localDevice_returnGone() {
mFeatures.add(MediaRoute2Info.FEATURE_LOCAL_PLAYBACK);
@@ -157,6 +172,39 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ public void getBroadcastIconVisibility_isBroadcasting_returnVisible() {
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+ mLocalBluetoothLeBroadcast);
+ when(mLocalBluetoothLeBroadcast.isEnabled(any())).thenReturn(true);
+ when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_PLAYING);
+ when(mMediaDevice.isBLEDevice()).thenReturn(true);
+
+ assertThat(mMediaOutputDialog.getBroadcastIconVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void getBroadcastIconVisibility_noBroadcasting_returnGone() {
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+ mLocalBluetoothLeBroadcast);
+ when(mLocalBluetoothLeBroadcast.isEnabled(any())).thenReturn(false);
+ when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_PLAYING);
+ when(mMediaDevice.isBLEDevice()).thenReturn(true);
+
+ assertThat(mMediaOutputDialog.getBroadcastIconVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void getBroadcastIconVisibility_remoteNonLeDevice_returnGone() {
+ when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
+ mLocalBluetoothLeBroadcast);
+ when(mLocalBluetoothLeBroadcast.isEnabled(any())).thenReturn(false);
+ when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_PLAYING);
+ when(mMediaDevice.isBLEDevice()).thenReturn(false);
+
+ assertThat(mMediaOutputDialog.getBroadcastIconVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
// Check the visibility metric logging by creating a new MediaOutput dialog,
// and verify if the calling times increases.
public void onCreate_ShouldLogVisibility() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
deleted file mode 100644
index 9256cd32291f..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.media.dialog;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.graphics.drawable.Icon;
-import android.testing.AndroidTestingRunner;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.test.filters.SmallTest;
-
-import com.android.settingslib.media.MediaDevice;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class MediaOutputGroupAdapterTest extends SysuiTestCase {
-
- private static final String TEST_DEVICE_NAME_1 = "test_device_name_1";
- private static final String TEST_DEVICE_NAME_2 = "test_device_name_2";
- private static final String TEST_DEVICE_ID_1 = "test_device_id_1";
- private static final String TEST_DEVICE_ID_2 = "test_device_id_2";
- private static final int TEST_VOLUME = 10;
- private static final int TEST_MAX_VOLUME = 50;
-
- // Mock
- private MediaOutputController mMediaOutputController = mock(MediaOutputController.class);
- private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
- private MediaDevice mMediaDevice2 = mock(MediaDevice.class);
- private Icon mIcon = mock(Icon.class);
- private IconCompat mIconCompat = mock(IconCompat.class);
-
- private MediaOutputGroupAdapter mGroupAdapter;
- private MediaOutputGroupAdapter.GroupViewHolder mGroupViewHolder;
- private List<MediaDevice> mGroupMediaDevices = new ArrayList<>();
- private List<MediaDevice> mSelectableMediaDevices = new ArrayList<>();
- private List<MediaDevice> mSelectedMediaDevices = new ArrayList<>();
- private List<MediaDevice> mDeselectableMediaDevices = new ArrayList<>();
-
- @Before
- public void setUp() {
- when(mMediaOutputController.getGroupMediaDevices()).thenReturn(mGroupMediaDevices);
- when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat);
- when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat);
- when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(mSelectableMediaDevices);
- when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mSelectedMediaDevices);
- when(mMediaOutputController.getDeselectableMediaDevice()).thenReturn(
- mDeselectableMediaDevices);
- when(mIconCompat.toIcon(mContext)).thenReturn(mIcon);
- when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1);
- when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1);
- when(mMediaDevice2.getName()).thenReturn(TEST_DEVICE_NAME_2);
- when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_ID_2);
- mGroupMediaDevices.add(mMediaDevice1);
- mGroupMediaDevices.add(mMediaDevice2);
- mSelectedMediaDevices.add(mMediaDevice1);
- mSelectableMediaDevices.add(mMediaDevice2);
- mDeselectableMediaDevices.add(mMediaDevice1);
-
- mGroupAdapter = new MediaOutputGroupAdapter(mMediaOutputController);
- mGroupViewHolder = (MediaOutputGroupAdapter.GroupViewHolder) mGroupAdapter
- .onCreateViewHolder(new LinearLayout(mContext), 0);
- }
-
- @Test
- public void onBindViewHolder_verifyGroupItem() {
- mGroupAdapter.onBindViewHolder(mGroupViewHolder, 0);
-
- assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(mContext.getText(
- R.string.media_output_dialog_group));
- }
-
- @Test
- public void onBindViewHolder_singleSelectedDevice_verifyView() {
- mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
-
- assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
- assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
- // Disabled checkBox
- assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isFalse();
- }
-
- @Test
- public void onBindViewHolder_multipleSelectedDevice_verifyView() {
- mSelectedMediaDevices.clear();
- mSelectedMediaDevices.add(mMediaDevice1);
- mSelectedMediaDevices.add(mMediaDevice2);
- mDeselectableMediaDevices.clear();
- mDeselectableMediaDevices.add(mMediaDevice1);
- mDeselectableMediaDevices.add(mMediaDevice2);
- mSelectableMediaDevices.clear();
-
- mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
-
- assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
- assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
- // Enabled checkBox
- assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isTrue();
- }
-
- @Test
- public void onBindViewHolder_notDeselectedDevice_verifyView() {
- mSelectedMediaDevices.clear();
- mSelectedMediaDevices.add(mMediaDevice1);
- mSelectedMediaDevices.add(mMediaDevice2);
- mDeselectableMediaDevices.clear();
- mDeselectableMediaDevices.add(mMediaDevice1);
- mSelectableMediaDevices.clear();
-
- mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);
-
- assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
- assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue();
- // Disabled checkBox
- assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isFalse();
- }
-
- @Test
- public void onBindViewHolder_selectableDevice_verifyCheckBox() {
- mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);
-
- assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
- assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mGroupViewHolder.mCheckBox.isChecked()).isFalse();
- // Enabled checkBox
- assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isTrue();
- }
-
- @Test
- public void onBindViewHolder_verifyDeviceVolume() {
- when(mMediaDevice1.getCurrentVolume()).thenReturn(TEST_VOLUME);
- when(mMediaDevice1.getMaxVolume()).thenReturn(TEST_MAX_VOLUME);
- mGroupViewHolder.mSeekBar.setVisibility(View.VISIBLE);
-
- mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
-
- assertThat(mGroupViewHolder.mSeekBar.getVolume()).isEqualTo(TEST_VOLUME);
- }
-
- @Test
- public void clickSelectedDevice_verifyRemoveDeviceFromPlayMedia() {
- mSelectedMediaDevices.clear();
- mSelectedMediaDevices.add(mMediaDevice1);
- mSelectedMediaDevices.add(mMediaDevice2);
- mDeselectableMediaDevices.clear();
- mDeselectableMediaDevices.add(mMediaDevice1);
- mDeselectableMediaDevices.add(mMediaDevice2);
- mSelectableMediaDevices.clear();
-
- mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1);
- mGroupViewHolder.mCheckBox.performClick();
-
- verify(mMediaOutputController).removeDeviceFromPlayMedia(mMediaDevice1);
- }
-
- @Test
- public void clickSelectabelDevice_verifyAddDeviceToPlayMedia() {
- mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2);
-
- mGroupViewHolder.mCheckBox.performClick();
-
- verify(mMediaOutputController).addDeviceToPlayMedia(mMediaDevice2);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
deleted file mode 100644
index 4534ae6448ec..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.media.dialog;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.media.AudioManager;
-import android.media.session.MediaSessionManager;
-import android.os.PowerExemptionManager;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.media.LocalMediaManager;
-import com.android.settingslib.media.MediaDevice;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.DialogLaunchAnimator;
-import com.android.systemui.broadcast.BroadcastSender;
-import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class MediaOutputGroupDialogTest extends SysuiTestCase {
-
- private static final String TEST_PACKAGE = "test_package";
-
- // Mock
- private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
- private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
- private ActivityStarter mStarter = mock(ActivityStarter.class);
- private BroadcastSender mBroadcastSender = mock(BroadcastSender.class);
- private LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
- private MediaDevice mMediaDevice = mock(MediaDevice.class);
- private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
- private NotificationEntryManager mNotificationEntryManager =
- mock(NotificationEntryManager.class);
- private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
- private NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
- NearbyMediaDevicesManager.class);
- private final AudioManager mAudioManager = mock(AudioManager.class);
- private PowerExemptionManager mPowerExemptionManager = mock(PowerExemptionManager.class);
-
- private MediaOutputGroupDialog mMediaOutputGroupDialog;
- private MediaOutputController mMediaOutputController;
- private List<MediaDevice> mMediaDevices = new ArrayList<>();
-
- @Before
- public void setUp() {
- mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
- mMediaSessionManager, mLocalBluetoothManager, mStarter,
- mNotificationEntryManager, mDialogLaunchAnimator,
- Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager);
- mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
- mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false, mBroadcastSender,
- mMediaOutputController);
- mMediaOutputGroupDialog.show();
- when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices);
- }
-
- @After
- public void tearDown() {
- mMediaOutputGroupDialog.dismissDialog();
- }
-
- @Test
- public void getStopButtonVisibility_returnVisible() {
- assertThat(mMediaOutputGroupDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
- }
-
- @Test
- public void getHeaderSubtitle_singleDevice_verifyTitle() {
- mMediaDevices.add(mMediaDevice);
-
- assertThat(mMediaOutputGroupDialog.getHeaderSubtitle()).isEqualTo(
- mContext.getText(R.string.media_output_dialog_single_device));
- }
-
- @Test
- public void getHeaderSubtitle_multipleDevices_verifyTitle() {
- mMediaDevices.add(mMediaDevice);
- mMediaDevices.add(mMediaDevice1);
-
- assertThat(mMediaOutputGroupDialog.getHeaderSubtitle()).isEqualTo(mContext.getString(
- R.string.media_output_dialog_multiple_devices, mMediaDevices.size()));
- }
-
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
index b9a69bb8641a..2eb478303cb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
@@ -25,6 +25,7 @@ import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
+import android.view.accessibility.AccessibilityManager
import android.widget.ImageView
import androidx.test.filters.SmallTest
import com.android.systemui.R
@@ -65,6 +66,8 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
@Mock
private lateinit var logger: MediaTttLogger
@Mock
+ private lateinit var accessibilityManager: AccessibilityManager
+ @Mock
private lateinit var windowManager: WindowManager
@Mock
private lateinit var viewUtil: ViewUtil
@@ -88,11 +91,21 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
)).thenReturn(applicationInfo)
context.setMockPackageManager(packageManager)
+ whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any()))
+ .thenReturn(TIMEOUT_MS.toInt())
+
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
controllerCommon = TestControllerCommon(
- context, logger, windowManager, viewUtil, fakeExecutor, tapGestureDetector, powerManager
+ context,
+ logger,
+ windowManager,
+ viewUtil,
+ fakeExecutor,
+ accessibilityManager,
+ tapGestureDetector,
+ powerManager
)
}
@@ -344,6 +357,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
windowManager: WindowManager,
viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
+ accessibilityManager: AccessibilityManager,
tapGestureDetector: TapGestureDetector,
powerManager: PowerManager
) : MediaTttChipControllerCommon<ChipInfo>(
@@ -352,23 +366,22 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
windowManager,
viewUtil,
mainExecutor,
+ accessibilityManager,
tapGestureDetector,
powerManager,
R.layout.media_ttt_chip
) {
- override fun updateChipView(chipInfo: ChipInfo, currentChipView: ViewGroup) {
-
- }
-
- override fun getIconSize(isAppIcon: Boolean): Int? = ICON_SIZE
+ override val windowLayoutParams = commonWindowLayoutParams
+ override fun updateChipView(chipInfo: ChipInfo, currentChipView: ViewGroup) {}
+ override fun getIconSize(isAppIcon: Boolean): Int = ICON_SIZE
}
inner class ChipInfo : ChipInfoCommon {
- override fun getTimeoutMs() = TIMEOUT_MS
+ override fun getTimeoutMs() = 1L
}
}
private const val PACKAGE_NAME = "com.android.systemui"
private const val APP_NAME = "Fake App Name"
private const val TIMEOUT_MS = 10000L
-private const val ICON_SIZE = 47 \ No newline at end of file
+private const val ICON_SIZE = 47
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 9edc4f4c71c3..bbc564193080 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -28,6 +28,7 @@ import android.testing.TestableLooper
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
+import android.view.accessibility.AccessibilityManager
import android.widget.ImageView
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
@@ -65,6 +66,8 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Mock
private lateinit var logger: MediaTttLogger
@Mock
+ private lateinit var accessibilityManager: AccessibilityManager
+ @Mock
private lateinit var powerManager: PowerManager
@Mock
private lateinit var windowManager: WindowManager
@@ -99,6 +102,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
windowManager,
viewUtil,
FakeExecutor(FakeSystemClock()),
+ accessibilityManager,
TapGestureDetector(context),
powerManager,
Handler.getMain(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index a8c72ddfd5d7..7ca0cd34ab26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -27,6 +27,7 @@ import android.testing.TestableLooper
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
+import android.view.accessibility.AccessibilityManager
import android.widget.ImageView
import android.widget.TextView
import androidx.test.filters.SmallTest
@@ -67,6 +68,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Mock
private lateinit var logger: MediaTttLogger
@Mock
+ private lateinit var accessibilityManager: AccessibilityManager
+ @Mock
private lateinit var powerManager: PowerManager
@Mock
private lateinit var windowManager: WindowManager
@@ -95,9 +98,12 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
+
uiEventLoggerFake = UiEventLoggerFake()
senderUiEventLogger = MediaTttSenderUiEventLogger(uiEventLoggerFake)
+ whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(TIMEOUT)
+
controllerSender = MediaTttChipControllerSender(
commandQueue,
context,
@@ -105,6 +111,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
windowManager,
viewUtil,
fakeExecutor,
+ accessibilityManager,
TapGestureDetector(context),
powerManager,
senderUiEventLogger
@@ -592,7 +599,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
fakeClock.advanceTime(1000L)
controllerSender.removeChip("fakeRemovalReason")
- fakeClock.advanceTime(state.state.timeout + 1)
+ fakeClock.advanceTime(TIMEOUT + 1L)
verify(windowManager).removeView(any())
}
@@ -615,7 +622,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
fakeClock.advanceTime(1000L)
controllerSender.removeChip("fakeRemovalReason")
- fakeClock.advanceTime(state.state.timeout + 1)
+ fakeClock.advanceTime(TIMEOUT + 1L)
verify(windowManager).removeView(any())
}
@@ -674,6 +681,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
private const val APP_NAME = "Fake app name"
private const val OTHER_DEVICE_NAME = "My Tablet"
private const val PACKAGE_NAME = "com.android.systemui"
+private const val TIMEOUT = 10000
private val routeInfo = MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME)
.addFeature("feature")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 1cfa3b2a08ff..8fa5c9324416 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -89,6 +89,7 @@ import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -97,7 +98,6 @@ import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
index 99f21ad4d508..c8ebd1240149 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
@@ -48,6 +48,8 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.bubbles.Bubble;
@@ -106,6 +108,9 @@ public class LaunchConversationActivityTest extends SysuiTestCase {
private Intent mIntent;
+ private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+ private FakeExecutor mBgExecutor = new FakeExecutor(mFakeSystemClock);
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -114,7 +119,8 @@ public class LaunchConversationActivityTest extends SysuiTestCase {
mNotifCollection,
Optional.of(mBubblesManager),
mUserManager,
- mCommandQueue
+ mCommandQueue,
+ mBgExecutor
);
verify(mCommandQueue, times(1)).addCallback(mCallbacksCaptor.capture());
@@ -193,6 +199,7 @@ public class LaunchConversationActivityTest extends SysuiTestCase {
// Ensure callback removed
verify(mCommandQueue).removeCallback(any());
// Clear the notification for bubbles.
+ FakeExecutor.exhaustExecutors(mBgExecutor);
verify(mIStatusBarService, times(1)).onNotificationClear(any(),
anyInt(), any(), anyInt(), anyInt(), mNotificationVisibilityCaptor.capture());
// Do not select the bubble.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index 26e4d9dfb003..a56990f40b90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -222,7 +222,7 @@ public class PowerNotificationWarningsTest extends SysuiTestCase {
mReceiver.onReceive(mContext, intent);
- verify(mDialogLaunchAnimator).showFromView(any(), eq(mView));
+ verify(mDialogLaunchAnimator).showFromView(any(), eq(mView), any());
mPowerNotificationWarnings.getSaverConfirmationDialog().dismiss();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index a3c353b5a666..c1c0f78ecd6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -731,7 +731,7 @@ public class QSSecurityFooterTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
- verify(mDialogLaunchAnimator).showFromView(any(), eq(mRootView));
+ verify(mDialogLaunchAnimator).showFromView(any(), eq(mRootView), any());
}
@Test
@@ -768,7 +768,7 @@ public class QSSecurityFooterTest extends SysuiTestCase {
ArgumentCaptor<AlertDialog> dialogCaptor = ArgumentCaptor.forClass(AlertDialog.class);
mTestableLooper.processAllMessages();
- verify(mDialogLaunchAnimator).showFromView(dialogCaptor.capture(), any());
+ verify(mDialogLaunchAnimator).showFromView(dialogCaptor.capture(), any(), any());
AlertDialog dialog = dialogCaptor.getValue();
dialog.create();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
index 0f2c2647f20f..ca3182affcc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
@@ -34,6 +34,7 @@ import com.android.systemui.qs.tiles.ColorInversionTile
import com.android.systemui.qs.tiles.DataSaverTile
import com.android.systemui.qs.tiles.DeviceControlsTile
import com.android.systemui.qs.tiles.DndTile
+import com.android.systemui.qs.tiles.DreamTile
import com.android.systemui.qs.tiles.FlashlightTile
import com.android.systemui.qs.tiles.HotspotTile
import com.android.systemui.qs.tiles.InternetTile
@@ -89,7 +90,8 @@ private val specMap = mapOf(
"wallet" to QuickAccessWalletTile::class.java,
"qr_code_scanner" to QRCodeScannerTile::class.java,
"onehanded" to OneHandedModeTile::class.java,
- "color_correction" to ColorCorrectionTile::class.java
+ "color_correction" to ColorCorrectionTile::class.java,
+ "dream" to DreamTile::class.java
)
@RunWith(AndroidTestingRunner::class)
@@ -129,6 +131,7 @@ class QSFactoryImplTest : SysuiTestCase() {
@Mock private lateinit var qrCodeScannerTile: QRCodeScannerTile
@Mock private lateinit var oneHandedModeTile: OneHandedModeTile
@Mock private lateinit var colorCorrectionTile: ColorCorrectionTile
+ @Mock private lateinit var dreamTile: DreamTile
private lateinit var factory: QSFactoryImpl
@@ -171,7 +174,8 @@ class QSFactoryImplTest : SysuiTestCase() {
{ quickAccessWalletTile },
{ qrCodeScannerTile },
{ oneHandedModeTile },
- { colorCorrectionTile }
+ { colorCorrectionTile },
+ { dreamTile }
)
// When adding/removing tiles, fix also [specMap]
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
new file mode 100644
index 000000000000..73a0cbc5054d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.qs.tiles
+
+import android.net.ConnectivityManager
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.util.settings.GlobalSettings
+import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class AirplaneModeTileTest : SysuiTestCase() {
+ @Mock
+ private lateinit var mHost: QSHost
+ @Mock
+ private lateinit var mMetricsLogger: MetricsLogger
+ @Mock
+ private lateinit var mStatusBarStateController: StatusBarStateController
+ @Mock
+ private lateinit var mActivityStarter: ActivityStarter
+ @Mock
+ private lateinit var mQsLogger: QSLogger
+ @Mock
+ private lateinit var mBroadcastDispatcher: BroadcastDispatcher
+ @Mock
+ private lateinit var mConnectivityManager: Lazy<ConnectivityManager>
+ @Mock
+ private lateinit var mGlobalSettings: GlobalSettings
+ private lateinit var mTestableLooper: TestableLooper
+ private lateinit var mTile: AirplaneModeTile
+
+ private val mUiEventLogger: UiEventLogger = UiEventLoggerFake()
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mTestableLooper = TestableLooper.get(this)
+ Mockito.`when`(mHost.context).thenReturn(mContext)
+ Mockito.`when`(mHost.uiEventLogger).thenReturn(mUiEventLogger)
+ Mockito.`when`(mHost.userContext).thenReturn(mContext)
+
+ mTile = AirplaneModeTile(mHost,
+ mTestableLooper.looper,
+ Handler(mTestableLooper.looper),
+ FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQsLogger,
+ mBroadcastDispatcher,
+ mConnectivityManager,
+ mGlobalSettings)
+ }
+
+ @Test
+ fun testIcon_whenDisabled_showsOffState() {
+ val state = QSTile.BooleanState()
+
+ mTile.handleUpdateState(state, 0)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_airplane_icon_off))
+ }
+
+ @Test
+ fun testIcon_whenEnabled_showsOnState() {
+ val state = QSTile.BooleanState()
+
+ mTile.handleUpdateState(state, 1)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_airplane_icon_on))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index 3d9205ee0354..95e7ad9fad4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -24,15 +24,19 @@ import android.testing.TestableLooper.RunWithLooper
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.SecureSettings
+import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@@ -77,6 +81,7 @@ class BatterySaverTileTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
+ `when`(qsHost.context).thenReturn(mContext)
`when`(qsHost.userContext).thenReturn(userContext)
`when`(userContext.userId).thenReturn(USER)
@@ -133,4 +138,26 @@ class BatterySaverTileTest : SysuiTestCase() {
tile.handleSetListening(false)
verify(batteryController).clearLastPowerSaverStartView()
}
+
+ @Test
+ fun testIcon_whenBatterySaverDisabled_isOffState() {
+ val state = QSTile.BooleanState()
+ tile.onPowerSaveChanged(false)
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_battery_saver_icon_off))
+ }
+
+ @Test
+ fun testIcon_whenBatterySaverEnabled_isOnState() {
+ val state = QSTile.BooleanState()
+ tile.onPowerSaveChanged(true)
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_battery_saver_icon_on))
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
index cc472481ad8a..d65901777a73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
@@ -10,6 +10,7 @@ import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
@@ -18,6 +19,7 @@ import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSTileHost
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.BluetoothController
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -25,6 +27,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -84,6 +87,53 @@ class BluetoothTileTest : SysuiTestCase() {
assertThat(tile.restrictionChecked).isEqualTo(UserManager.DISALLOW_BLUETOOTH)
}
+ @Test
+ fun testIcon_whenDisabled_isOffState() {
+ val state = QSTile.BooleanState()
+ disableBluetooth()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off))
+ }
+
+ @Test
+ fun testIcon_whenDisconnected_isOffState() {
+ val state = QSTile.BooleanState()
+ enableBluetooth()
+ setBluetoothDisconnected()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off))
+ }
+
+ @Test
+ fun testIcon_whenConnected_isOnState() {
+ val state = QSTile.BooleanState()
+ enableBluetooth()
+ setBluetoothConnected()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_on))
+ }
+
+ @Test
+ fun testIcon_whenConnecting_isSearchState() {
+ val state = QSTile.BooleanState()
+ enableBluetooth()
+ setBluetoothConnecting()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_search))
+ }
+
private class FakeBluetoothTile(
qsTileHost: QSTileHost,
backgroundLooper: Looper,
@@ -114,4 +164,27 @@ class BluetoothTileTest : SysuiTestCase() {
restrictionChecked = userRestriction
}
}
+
+ fun enableBluetooth() {
+ `when`(bluetoothController.isBluetoothEnabled).thenReturn(true)
+ }
+
+ fun disableBluetooth() {
+ `when`(bluetoothController.isBluetoothEnabled).thenReturn(false)
+ }
+
+ fun setBluetoothDisconnected() {
+ `when`(bluetoothController.isBluetoothConnecting).thenReturn(false)
+ `when`(bluetoothController.isBluetoothConnected).thenReturn(false)
+ }
+
+ fun setBluetoothConnected() {
+ `when`(bluetoothController.isBluetoothConnecting).thenReturn(false)
+ `when`(bluetoothController.isBluetoothConnected).thenReturn(true)
+ }
+
+ fun setBluetoothConnecting() {
+ `when`(bluetoothController.isBluetoothConnected).thenReturn(false)
+ `when`(bluetoothController.isBluetoothConnecting).thenReturn(true)
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
new file mode 100644
index 000000000000..cfbb82f5f338
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.qs.tiles
+
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class CameraToggleTileTest : SysuiTestCase() {
+ companion object {
+ /* isBlocked */
+ const val CAMERA_TOGGLE_ENABLED: Boolean = false
+ const val CAMERA_TOGGLE_DISABLED: Boolean = true
+ }
+
+ @Mock
+ private lateinit var host: QSHost
+ @Mock
+ private lateinit var metricsLogger: MetricsLogger
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var qsLogger: QSLogger
+ @Mock
+ private lateinit var privacyController: IndividualSensorPrivacyController
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
+
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var tile: CameraToggleTile
+ private val uiEventLogger: UiEventLogger = UiEventLoggerFake()
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+ whenever(host.context).thenReturn(mContext)
+ whenever(host.uiEventLogger).thenReturn(uiEventLogger)
+
+ tile = CameraToggleTile(host,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ metricsLogger,
+ FalsingManagerFake(),
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ privacyController,
+ keyguardStateController)
+ }
+
+ @Test
+ fun testIcon_whenCameraAccessEnabled_isOnState() {
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, CAMERA_TOGGLE_ENABLED)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_camera_access_icon_on))
+ }
+
+ @Test
+ fun testIcon_whenCameraAccessDisabled_isOffState() {
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, CAMERA_TOGGLE_DISABLED)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_camera_access_icon_off))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
index 9936d4951d00..ce5edb147d87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
@@ -21,6 +21,7 @@ import android.content.ContextWrapper
import android.content.SharedPreferences
import android.os.Handler
import android.provider.Settings
+import android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
import android.provider.Settings.Global.ZEN_MODE_OFF
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -28,19 +29,24 @@ import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
+import java.io.File
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -49,9 +55,8 @@ import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import java.io.File
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -65,22 +70,31 @@ class DndTileTest : SysuiTestCase() {
@Mock
private lateinit var qsHost: QSHost
+
@Mock
private lateinit var metricsLogger: MetricsLogger
+
@Mock
private lateinit var statusBarStateController: StatusBarStateController
+
@Mock
private lateinit var activityStarter: ActivityStarter
+
@Mock
private lateinit var qsLogger: QSLogger
+
@Mock
private lateinit var uiEventLogger: UiEventLogger
+
@Mock
private lateinit var zenModeController: ZenModeController
+
@Mock
private lateinit var sharedPreferences: SharedPreferences
+
@Mock
private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+
@Mock
private lateinit var hostDialog: Dialog
@@ -173,7 +187,7 @@ class DndTileTest : SysuiTestCase() {
tile.handleClick(view)
testableLooper.processAllMessages()
- verify(dialogLaunchAnimator).showFromView(any(), eq(view), anyBoolean())
+ verify(dialogLaunchAnimator).showFromView(any(), eq(view), nullable(), anyBoolean())
}
@Test
@@ -187,6 +201,26 @@ class DndTileTest : SysuiTestCase() {
tile.handleClick(view)
testableLooper.processAllMessages()
- verify(dialogLaunchAnimator, never()).showFromView(any(), any(), anyBoolean())
+ verify(dialogLaunchAnimator, never()).showFromView(any(), any(), nullable(), anyBoolean())
+ }
+
+ @Test
+ fun testIcon_whenDndModeOff_isOffState() {
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_dnd_icon_off))
+ }
+
+ @Test
+ fun testIcon_whenDndModeOn_isOnState() {
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_NO_INTERRUPTIONS)
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_dnd_icon_on))
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
new file mode 100644
index 000000000000..a13bece4b489
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.qs.tiles;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.dreams.IDreamManager;
+import android.service.quicksettings.Tile;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.settings.SecureSettings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class DreamTileTest extends SysuiTestCase {
+
+ @Mock
+ private ActivityStarter mActivityStarter;
+ @Mock
+ private QSTileHost mHost;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private QSLogger mQSLogger;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private IDreamManager mDreamManager;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private UserTracker mUserTracker;
+
+ private TestableLooper mTestableLooper;
+
+ private DreamTile mTile;
+
+ private SecureSettings mSecureSettings;
+
+ private static final ComponentName COLORS_DREAM_COMPONENT_NAME = new ComponentName(
+ "com.android.dreams", ".Colors");
+
+ private static final int DEFAULT_USER = 0;
+
+ private final String mExpectedTileLabel = mContext.getResources().getString(
+ R.string.quick_settings_screensaver_label);
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mSecureSettings = new FakeSettings();
+ mTestableLooper = TestableLooper.get(this);
+
+ when(mHost.getUserId()).thenReturn(DEFAULT_USER);
+ when(mHost.getContext()).thenReturn(mContext);
+
+ mTile = spy(constructTileForTest(true, false));
+
+ mTestableLooper.processAllMessages();
+ mTile.initialize();
+ }
+
+ @Test
+ public void testNotAvailable() throws RemoteException {
+ // Should not be available if screensaver is disabled
+ setScreensaverEnabled(false);
+
+ mTile.refreshState();
+ mTestableLooper.processAllMessages();
+ assertEquals(Tile.STATE_UNAVAILABLE, mTile.getState().state);
+
+ // Should not be available if component is not set
+ mSecureSettings.putInt(Settings.Secure.SCREENSAVER_ENABLED, 1);
+ when(mDreamManager.getDreamComponents()).thenReturn(null);
+
+ mTestableLooper.processAllMessages();
+ assertEquals(Tile.STATE_UNAVAILABLE, mTile.getState().state);
+ assertEquals(mExpectedTileLabel, mTile.getState().contentDescription);
+ }
+
+ @Test
+ public void testInactiveWhenDreaming() throws RemoteException {
+ setScreensaverEnabled(true);
+
+ when(mDreamManager.getDreamComponents()).thenReturn(new ComponentName[]{
+ COLORS_DREAM_COMPONENT_NAME
+ });
+ when(mDreamManager.isDreaming()).thenReturn(false);
+
+ mTile.refreshState();
+ mTestableLooper.processAllMessages();
+ assertEquals(Tile.STATE_INACTIVE, mTile.getState().state);
+ }
+
+ @Test
+ public void testActive() throws RemoteException {
+ setScreensaverEnabled(true);
+
+ when(mDreamManager.getDreamComponents()).thenReturn(new ComponentName[]{
+ COLORS_DREAM_COMPONENT_NAME
+ });
+ when(mDreamManager.isDreaming()).thenReturn(true);
+
+ mTile.refreshState();
+ mTestableLooper.processAllMessages();
+ assertEquals(Tile.STATE_ACTIVE, mTile.getState().state);
+ }
+
+ @Test
+ public void testClick() throws RemoteException {
+ // Set the AOSP dream enabled as the base setup.
+ setScreensaverEnabled(true);
+ when(mDreamManager.getDreamComponents()).thenReturn(new ComponentName[]{
+ COLORS_DREAM_COMPONENT_NAME
+ });
+ when(mDreamManager.isDreaming()).thenReturn(false);
+
+ mTile.refreshState();
+ mTestableLooper.processAllMessages();
+ assertEquals(Tile.STATE_INACTIVE, mTile.getState().state);
+
+ // Now click
+ mTile.handleClick(null /* view */);
+
+ verify(mDreamManager).dream();
+
+ when(mDreamManager.isDreaming()).thenReturn(true);
+ mTile.refreshState();
+ mTestableLooper.processAllMessages();
+ assertEquals(Tile.STATE_ACTIVE, mTile.getState().state);
+
+ // Click again to see that other method is called
+ mTile.handleClick(null /* view */);
+
+ verify(mDreamManager).awaken();
+ }
+
+ @Test
+ public void testContentDescription() {
+ assertEquals(mExpectedTileLabel, mTile.getContentDescription(null));
+
+ final String testDreamName = "MyDream";
+ assertEquals(mExpectedTileLabel + ", " + testDreamName,
+ mTile.getContentDescription(testDreamName));
+ }
+
+ @Test
+ public void testUserAvailability() {
+ DreamTile unsupportedTile = constructTileForTest(false, true);
+ assertFalse(unsupportedTile.isAvailable());
+
+ DreamTile supportedTileAllUsers = constructTileForTest(true, false);
+
+ UserHandle systemUserHandle = mock(UserHandle.class);
+ when(systemUserHandle.isSystem()).thenReturn(true);
+
+ UserHandle nonSystemUserHandle = mock(UserHandle.class);
+ when(nonSystemUserHandle.isSystem()).thenReturn(false);
+
+ when(mUserTracker.getUserHandle()).thenReturn(systemUserHandle);
+ assertTrue(supportedTileAllUsers.isAvailable());
+ when(mUserTracker.getUserHandle()).thenReturn(nonSystemUserHandle);
+ assertTrue(supportedTileAllUsers.isAvailable());
+
+ DreamTile supportedTileOnlySystemUser = constructTileForTest(true, true);
+ when(mUserTracker.getUserHandle()).thenReturn(systemUserHandle);
+ assertTrue(supportedTileOnlySystemUser.isAvailable());
+ when(mUserTracker.getUserHandle()).thenReturn(nonSystemUserHandle);
+ assertFalse(supportedTileOnlySystemUser.isAvailable());
+ }
+
+ @Test
+ public void testIconDockState() {
+ final DreamTile dockedTile = constructTileForTest(true, false);
+
+ final ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass(
+ BroadcastReceiver.class);
+ dockedTile.handleSetListening(true);
+ verify(mBroadcastDispatcher).registerReceiver(receiverCaptor.capture(), any());
+ final BroadcastReceiver receiver = receiverCaptor.getValue();
+
+ Intent dockIntent = new Intent(Intent.ACTION_DOCK_EVENT);
+ dockIntent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_DESK);
+ receiver.onReceive(mContext, dockIntent);
+ mTestableLooper.processAllMessages();
+ assertEquals(QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_screen_saver),
+ dockedTile.getState().icon);
+
+ dockIntent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ receiver.onReceive(mContext, dockIntent);
+ mTestableLooper.processAllMessages();
+ assertEquals(QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_screen_saver_undocked),
+ dockedTile.getState().icon);
+ }
+
+ private void setScreensaverEnabled(boolean enabled) {
+ mSecureSettings.putIntForUser(Settings.Secure.SCREENSAVER_ENABLED, enabled ? 1 : 0,
+ DEFAULT_USER);
+ }
+
+ private DreamTile constructTileForTest(boolean dreamSupported,
+ boolean dreamOnlyEnabledForSystemUser) {
+ return new DreamTile(
+ mHost,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mDreamManager,
+ mSecureSettings,
+ mBroadcastDispatcher,
+ mUserTracker,
+ dreamSupported, dreamOnlyEnabledForSystemUser);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
new file mode 100644
index 000000000000..d2bbc8cfac39
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.qs.tiles
+
+import android.content.Context
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.LocationController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class LocationTileTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var mockContext: Context
+ @Mock
+ private lateinit var qsLogger: QSLogger
+ @Mock
+ private lateinit var qsHost: QSTileHost
+ @Mock
+ private lateinit var metricsLogger: MetricsLogger
+ private val falsingManager = FalsingManagerFake()
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var locationController: LocationController
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
+
+ private val uiEventLogger = UiEventLoggerFake()
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var tile: LocationTile
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+ `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+ `when`(qsHost.context).thenReturn(mockContext)
+
+ tile = LocationTile(qsHost,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ falsingManager,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ locationController,
+ keyguardStateController)
+ }
+
+ @Test
+ fun testIcon_whenDisabled_isOffState() {
+ val state = QSTile.BooleanState()
+ `when`(locationController.isLocationEnabled).thenReturn(false)
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_location_icon_off))
+ }
+
+ @Test
+ fun testIcon_whenEnabled_isOnState() {
+ val state = QSTile.BooleanState()
+ `when`(locationController.isLocationEnabled).thenReturn(true)
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_location_icon_on))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
new file mode 100644
index 000000000000..1ab601ce3ebe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.qs.tiles
+
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class MicrophoneToggleTileTest : SysuiTestCase() {
+ companion object {
+ /* isBlocked */
+ const val MICROPHONE_TOGGLE_ENABLED: Boolean = false
+ const val MICROPHONE_TOGGLE_DISABLED: Boolean = true
+ }
+
+ @Mock
+ private lateinit var host: QSHost
+ @Mock
+ private lateinit var metricsLogger: MetricsLogger
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var qsLogger: QSLogger
+ @Mock
+ private lateinit var privacyController: IndividualSensorPrivacyController
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
+
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var tile: MicrophoneToggleTile
+ private val uiEventLogger: UiEventLogger = UiEventLoggerFake()
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+ whenever(host.context).thenReturn(mContext)
+ whenever(host.uiEventLogger).thenReturn(uiEventLogger)
+
+ tile = MicrophoneToggleTile(host,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ metricsLogger,
+ FalsingManagerFake(),
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ privacyController,
+ keyguardStateController)
+ }
+
+ @Test
+ fun testIcon_whenMicrophoneAccessEnabled_isOnState() {
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, MICROPHONE_TOGGLE_ENABLED)
+
+ assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_mic_access_on))
+ }
+
+ @Test
+ fun testIcon_whenMicrophoneAccessDisabled_isOffState() {
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, MICROPHONE_TOGGLE_DISABLED)
+
+ assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_mic_access_off))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
index 55c51b255ac7..e9dfd3ed182b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
@@ -36,9 +36,11 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingController;
import com.android.systemui.statusbar.policy.RotationLockController;
@@ -194,6 +196,26 @@ public class RotationLockTileTest extends SysuiTestCase {
assertEquals("", mLockTile.getState().secondaryLabel.toString());
}
+ @Test
+ public void testIcon_whenDisabled_isOffState() {
+ QSTile.BooleanState state = new QSTile.BooleanState();
+ disableAutoRotation();
+
+ mLockTile.handleUpdateState(state, /* arg= */ null);
+
+ assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_off));
+ }
+
+ @Test
+ public void testIcon_whenEnabled_isOnState() {
+ QSTile.BooleanState state = new QSTile.BooleanState();
+ enableAutoRotation();
+
+ mLockTile.handleUpdateState(state, /* arg= */ null);
+
+ assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_on));
+ }
+
private void enableAutoRotation() {
when(mRotationPolicyWrapper.isRotationLocked()).thenReturn(false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index 030c65a0576a..9d908fdfb976 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -42,11 +42,12 @@ import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatcher
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.argThat
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -82,19 +83,19 @@ class UserSwitchDialogControllerTest : SysuiTestCase() {
`when`(dialog.context).thenReturn(mContext)
controller = UserSwitchDialogController(
- { userDetailViewAdapter },
- activityStarter,
- falsingManager,
- dialogLaunchAnimator,
- uiEventLogger,
- { dialog }
+ { userDetailViewAdapter },
+ activityStarter,
+ falsingManager,
+ dialogLaunchAnimator,
+ uiEventLogger,
+ { dialog }
)
}
@Test
fun showDialog_callsDialogShow() {
controller.showDialog(launchView)
- verify(dialogLaunchAnimator).showFromView(dialog, launchView)
+ verify(dialogLaunchAnimator).showFromView(eq(dialog), eq(launchView), any(), anyBoolean())
verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN)
}
@@ -140,11 +141,11 @@ class UserSwitchDialogControllerTest : SysuiTestCase() {
clickCaptor.value.onClick(dialog, DialogInterface.BUTTON_NEUTRAL)
verify(activityStarter)
- .postStartActivityDismissingKeyguard(
- argThat(IntentMatcher(Settings.ACTION_USER_SETTINGS)),
- eq(0),
- eq(null)
- )
+ .postStartActivityDismissingKeyguard(
+ argThat(IntentMatcher(Settings.ACTION_USER_SETTINGS)),
+ eq(0),
+ eq(null)
+ )
verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
}
@@ -167,4 +168,4 @@ class UserSwitchDialogControllerTest : SysuiTestCase() {
return argument?.action == action
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 91cafead596c..b05d9a31b475 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -16,18 +16,21 @@
package com.android.systemui.screenrecord;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Intent;
+import android.os.Handler;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
@@ -66,6 +69,8 @@ public class RecordingServiceTest extends SysuiTestCase {
@Mock
private Executor mExecutor;
@Mock
+ private Handler mHandler;
+ @Mock
private UserContextProvider mUserContextTracker;
private KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil() {
public void executeWhenUnlocked(ActivityStarter.OnDismissAction action,
@@ -79,8 +84,8 @@ public class RecordingServiceTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mRecordingService = Mockito.spy(new RecordingService(mController, mExecutor, mUiEventLogger,
- mNotificationManager, mUserContextTracker, mKeyguardDismissUtil));
+ mRecordingService = Mockito.spy(new RecordingService(mController, mExecutor, mHandler,
+ mUiEventLogger, mNotificationManager, mUserContextTracker, mKeyguardDismissUtil));
// Return actual context info
doReturn(mContext).when(mRecordingService).getApplicationContext();
@@ -143,4 +148,54 @@ public class RecordingServiceTest extends SysuiTestCase {
// Then the state is set to not recording
verify(mController).updateState(false);
}
+
+ @Test
+ public void testOnSystemRequestedStop_recordingInProgress_endsRecording() throws IOException {
+ doReturn(true).when(mController).isRecording();
+
+ mRecordingService.onStopped();
+
+ verify(mScreenMediaRecorder).end();
+ }
+
+ @Test
+ public void testOnSystemRequestedStop_recordingInProgress_updatesState() {
+ doReturn(true).when(mController).isRecording();
+
+ mRecordingService.onStopped();
+
+ verify(mController).updateState(false);
+ }
+
+ @Test
+ public void testOnSystemRequestedStop_recordingIsNotInProgress_doesNotEndRecording()
+ throws IOException {
+ doReturn(false).when(mController).isRecording();
+
+ mRecordingService.onStopped();
+
+ verify(mScreenMediaRecorder, never()).end();
+ }
+
+ @Test
+ public void testOnSystemRequestedStop_recorderEndThrowsRuntimeException_releasesRecording()
+ throws IOException {
+ doReturn(true).when(mController).isRecording();
+ doThrow(new RuntimeException()).when(mScreenMediaRecorder).end();
+
+ mRecordingService.onStopped();
+
+ verify(mScreenMediaRecorder).release();
+ }
+
+ @Test
+ public void testOnSystemRequestedStop_recorderEndThrowsOOMError_releasesRecording()
+ throws IOException {
+ doReturn(true).when(mController).isRecording();
+ doThrow(new OutOfMemoryError()).when(mScreenMediaRecorder).end();
+
+ assertThrows(Throwable.class, () -> mRecordingService.onStopped());
+
+ verify(mScreenMediaRecorder).release();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
index 7ab49584f0e3..e1eda117177d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
@@ -32,7 +32,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.app.PendingIntent;
-import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.testing.AndroidTestingRunner;
@@ -115,7 +114,7 @@ public class ActionProxyReceiverTest extends SysuiTestCase {
actionProxyReceiver.onReceive(mContext, mIntent);
verify(mMockScreenshotSmartActions, never())
- .notifyScreenshotAction(any(Context.class), anyString(), anyString(), anyBoolean(),
+ .notifyScreenshotAction(anyString(), anyString(), anyBoolean(),
any(Intent.class));
}
@@ -129,7 +128,7 @@ public class ActionProxyReceiverTest extends SysuiTestCase {
actionProxyReceiver.onReceive(mContext, mIntent);
verify(mMockScreenshotSmartActions).notifyScreenshotAction(
- mContext, testId, ACTION_TYPE_SHARE, false, null);
+ testId, ACTION_TYPE_SHARE, false, null);
}
private ActionProxyReceiver constructActionProxyReceiver(boolean withStatusBar) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
index 664c125533d1..d58f47a0469a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
@@ -31,7 +31,6 @@ import static org.mockito.Mockito.verify;
import android.content.ContentResolver;
import android.content.ContentValues;
-import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@@ -81,7 +80,7 @@ public class DeleteScreenshotReceiverTest extends SysuiTestCase {
verify(mMockExecutor, never()).execute(any(Runnable.class));
verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
- any(Context.class), any(String.class), any(String.class), anyBoolean(),
+ any(String.class), any(String.class), anyBoolean(),
any(Intent.class));
}
@@ -113,7 +112,7 @@ public class DeleteScreenshotReceiverTest extends SysuiTestCase {
}
// ensure smart actions not called by default
- verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(any(Context.class),
+ verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
any(String.class), any(String.class), anyBoolean(), any(Intent.class));
}
@@ -129,7 +128,7 @@ public class DeleteScreenshotReceiverTest extends SysuiTestCase {
mDeleteScreenshotReceiver.onReceive(mContext, intent);
verify(mMockExecutor).execute(any(Runnable.class));
- verify(mMockScreenshotSmartActions).notifyScreenshotAction(mContext, testId,
+ verify(mMockScreenshotSmartActions).notifyScreenshotAction(testId,
ACTION_TYPE_DELETE, false, null);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index 3d658ec8e811..69b7b88b0524 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -43,7 +43,6 @@ import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
@@ -71,7 +70,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
public void setup() {
mSmartActionsProvider = mock(
ScreenshotNotificationSmartActionsProvider.class);
- mScreenshotSmartActions = new ScreenshotSmartActions();
+ mScreenshotSmartActions = new ScreenshotSmartActions(() -> mSmartActionsProvider);
mHandler = mock(Handler.class);
}
@@ -158,8 +157,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
Bitmap bitmap = mock(Bitmap.class);
when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
ScreenshotNotificationSmartActionsProvider actionsProvider =
- SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
- mContext, null, mHandler);
+ new ScreenshotNotificationSmartActionsProvider();
CompletableFuture<List<Notification.Action>> smartActionsFuture =
mScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
actionsProvider, REGULAR_SMART_ACTIONS,
@@ -183,7 +181,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
data.mActionsReadyListener = null;
SaveImageInBackgroundTask task =
new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data,
- ActionTransition::new);
+ ActionTransition::new, mSmartActionsProvider);
Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(),
Uri.parse("Screenshot_123.png")).get().action;
@@ -211,7 +209,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
data.mActionsReadyListener = null;
SaveImageInBackgroundTask task =
new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data,
- ActionTransition::new);
+ ActionTransition::new, mSmartActionsProvider);
Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(),
Uri.parse("Screenshot_123.png")).get().action;
@@ -239,7 +237,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
data.mActionsReadyListener = null;
SaveImageInBackgroundTask task =
new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data,
- ActionTransition::new);
+ ActionTransition::new, mSmartActionsProvider);
Notification.Action deleteAction = task.createDeleteAction(mContext,
mContext.getResources(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
index 011e6b7b1071..83c949793447 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java
@@ -74,6 +74,6 @@ public class SmartActionsReceiverTest extends SysuiTestCase {
verify(mMockPendingIntent).send(
eq(mContext), eq(0), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
verify(mMockScreenshotSmartActions).notifyScreenshotAction(
- mContext, testId, testActionType, true, intent);
+ testId, testActionType, true, intent);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
new file mode 100644
index 000000000000..73226fa0b12c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.settings
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.IntentFilter
+import android.os.Environment
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class UserFileManagerImplTest : SysuiTestCase() {
+ companion object {
+ const val TEST_FILE_NAME = "abc.txt"
+ }
+
+ lateinit var userFileManager: UserFileManagerImpl
+ lateinit var backgroundExecutor: FakeExecutor
+ @Mock
+ lateinit var userManager: UserManager
+ @Mock
+ lateinit var broadcastDispatcher: BroadcastDispatcher
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ backgroundExecutor = FakeExecutor(FakeSystemClock())
+ userFileManager = UserFileManagerImpl(context, userManager,
+ broadcastDispatcher, backgroundExecutor)
+ }
+
+ @Test
+ fun testGetFile() {
+ assertThat(userFileManager.getFile(TEST_FILE_NAME, 0).path)
+ .isEqualTo("${context.filesDir}/$TEST_FILE_NAME")
+ assertThat(userFileManager.getFile(TEST_FILE_NAME, 11).path)
+ .isEqualTo("${context.filesDir}/${UserFileManagerImpl.ID}/11/files/$TEST_FILE_NAME")
+ }
+
+ @Test
+ fun testGetSharedPreferences() {
+ assertThat(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 0))
+ .isNotEqualTo(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11))
+ }
+
+ @Test
+ fun testUserFileManagerStart() {
+ val userFileManager = spy(userFileManager)
+ userFileManager.start()
+ verify(userFileManager).clearDeletedUserData()
+ verify(broadcastDispatcher).registerReceiver(any(BroadcastReceiver::class.java),
+ any(IntentFilter::class.java),
+ any(Executor::class.java), isNull(), eq(Context.RECEIVER_EXPORTED), isNull())
+ }
+
+ @Test
+ fun testClearDeletedUserData() {
+ val dir = Environment.buildPath(
+ context.filesDir,
+ UserFileManagerImpl.ID,
+ "11",
+ "files"
+ )
+ dir.mkdirs()
+ val file = Environment.buildPath(
+ context.filesDir,
+ UserFileManagerImpl.ID,
+ "11",
+ "files",
+ TEST_FILE_NAME
+ )
+ val secondaryUserDir = Environment.buildPath(
+ context.filesDir,
+ UserFileManagerImpl.ID,
+ "11",
+ )
+ file.createNewFile()
+ assertThat(secondaryUserDir.exists()).isTrue()
+ assertThat(file.exists()).isTrue()
+ userFileManager.clearDeletedUserData()
+ assertThat(backgroundExecutor.runAllReady()).isGreaterThan(0)
+ verify(userManager).aliveUsers
+ assertThat(secondaryUserDir.exists()).isFalse()
+ assertThat(file.exists()).isFalse()
+ dir.deleteRecursively()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
new file mode 100644
index 000000000000..0ce9056dc1d1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.shade
+
+import android.testing.AndroidTestingRunner
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class CombinedShadeHeaderConstraintsTest : SysuiTestCase() {
+
+ private lateinit var qqsConstraint: ConstraintSet
+ private lateinit var qsConstraint: ConstraintSet
+ private lateinit var largeScreenConstraint: ConstraintSet
+
+ @Before
+ fun setUp() {
+ qqsConstraint = ConstraintSet().apply {
+ load(context, context.resources.getXml(R.xml.qqs_header))
+ }
+ qsConstraint = ConstraintSet().apply {
+ load(context, context.resources.getXml(R.xml.qs_header_new))
+ }
+ largeScreenConstraint = ConstraintSet().apply {
+ load(context, context.resources.getXml(R.xml.large_screen_shade_header))
+ }
+ }
+
+ @Test
+ fun testEdgeElementsAlignedWithGuide_qqs() {
+ with(qqsConstraint) {
+ assertThat(getConstraint(R.id.clock).layout.startToStart).isEqualTo(R.id.begin_guide)
+ assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f)
+
+ assertThat(getConstraint(R.id.batteryRemainingIcon).layout.endToEnd)
+ .isEqualTo(R.id.end_guide)
+ assertThat(getConstraint(R.id.batteryRemainingIcon).layout.horizontalBias)
+ .isEqualTo(1f)
+
+ assertThat(getConstraint(R.id.privacy_container).layout.endToEnd)
+ .isEqualTo(R.id.end_guide)
+ assertThat(getConstraint(R.id.privacy_container).layout.horizontalBias)
+ .isEqualTo(1f)
+ }
+ }
+
+ @Test
+ fun testClockScale() {
+ with(qqsConstraint.getConstraint(R.id.clock)) {
+ assertThat(transform.scaleX).isEqualTo(1f)
+ assertThat(transform.scaleY).isEqualTo(1f)
+ }
+ with(qsConstraint.getConstraint(R.id.clock)) {
+ assertThat(transform.scaleX).isGreaterThan(1f)
+ assertThat(transform.scaleY).isGreaterThan(1f)
+ }
+ }
+
+ @Test
+ fun testEdgeElementsAlignedWithEdgeOrGuide_qs() {
+ with(qsConstraint) {
+ assertThat(getConstraint(R.id.clock).layout.startToStart).isEqualTo(PARENT_ID)
+ assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f)
+
+ assertThat(getConstraint(R.id.date).layout.startToStart).isEqualTo(PARENT_ID)
+ assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0f)
+
+ assertThat(getConstraint(R.id.batteryRemainingIcon).layout.endToEnd)
+ .isEqualTo(PARENT_ID)
+ assertThat(getConstraint(R.id.batteryRemainingIcon).layout.horizontalBias)
+ .isEqualTo(1f)
+
+ assertThat(getConstraint(R.id.privacy_container).layout.endToEnd)
+ .isEqualTo(R.id.end_guide)
+ assertThat(getConstraint(R.id.privacy_container).layout.horizontalBias).isEqualTo(1f)
+ }
+ }
+
+ @Test
+ fun testEdgeElementsAlignedWithEdge_largeScreen() {
+ with(largeScreenConstraint) {
+ assertThat(getConstraint(R.id.clock).layout.startToStart).isEqualTo(PARENT_ID)
+ assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f)
+
+ assertThat(getConstraint(R.id.privacy_container).layout.endToEnd).isEqualTo(PARENT_ID)
+ assertThat(getConstraint(R.id.privacy_container).layout.horizontalBias).isEqualTo(1f)
+ }
+ }
+
+ @Test
+ fun testCarrierAlpha() {
+ assertThat(qqsConstraint.getConstraint(R.id.carrier_group).propertySet.alpha).isEqualTo(0f)
+ assertThat(qsConstraint.getConstraint(R.id.carrier_group).propertySet.alpha).isEqualTo(1f)
+ assertThat(largeScreenConstraint.getConstraint(R.id.carrier_group).propertySet.alpha)
+ .isEqualTo(1f)
+ }
+
+ @Test
+ fun testPrivacyChipVisibilityConstraints_notVisible() {
+ val changes = CombinedShadeHeadersConstraintManagerImpl
+ .privacyChipVisibilityConstraints(false)
+ changes()
+
+ with(qqsConstraint) {
+ assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f)
+ assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f)
+ }
+
+ with(qsConstraint) {
+ assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f)
+ assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f)
+ }
+
+ with(largeScreenConstraint) {
+ assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f)
+ assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f)
+ }
+ }
+
+ @Test
+ fun testPrivacyChipVisibilityConstraints_visible() {
+ val changes = CombinedShadeHeadersConstraintManagerImpl
+ .privacyChipVisibilityConstraints(true)
+ changes()
+
+ with(qqsConstraint) {
+ assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(0f)
+ assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(0f)
+ }
+
+ with(qsConstraint) {
+ assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f)
+ assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f)
+ }
+
+ with(largeScreenConstraint) {
+ assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f)
+ assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f)
+ }
+ }
+
+ @Test
+ fun testEmptyCutoutConstraints() {
+ val changes = CombinedShadeHeadersConstraintManagerImpl.emptyCutoutConstraints()
+ changes()
+
+ // QS and Large Screen don't change with cutouts.
+ assertThat(changes.qsConstraintsChanges).isNull()
+ assertThat(changes.largeScreenConstraintsChanges).isNull()
+
+ with(qqsConstraint) {
+ // In this case, the date is constrained on the end by a Barrier determined by either
+ // privacy or statusIcons
+ assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.barrier)
+ assertThat(getConstraint(R.id.statusIcons).layout.startToEnd).isEqualTo(R.id.date)
+ assertThat(getConstraint(R.id.privacy_container).layout.startToEnd).isEqualTo(R.id.date)
+ assertThat(getConstraint(R.id.barrier).layout.mReferenceIds).asList().containsExactly(
+ R.id.statusIcons,
+ R.id.privacy_container
+ )
+ assertThat(getConstraint(R.id.barrier).layout.mBarrierDirection).isEqualTo(START)
+ }
+ }
+
+ @Test
+ fun testGuidesAreSetInCorrectPosition_largeCutoutSmallerPadding() {
+ val cutoutStart = 100
+ val padding = 10
+ val cutoutEnd = 30
+ val changes = CombinedShadeHeadersConstraintManagerImpl.edgesGuidelinesConstraints(
+ cutoutStart,
+ padding,
+ cutoutEnd,
+ padding
+ )
+ changes()
+
+ with(qqsConstraint) {
+ assertThat(getConstraint(R.id.begin_guide).layout.guideBegin)
+ .isEqualTo(cutoutStart - padding)
+ assertThat(getConstraint(R.id.end_guide).layout.guideEnd)
+ .isEqualTo(cutoutEnd - padding)
+ }
+
+ with(qsConstraint) {
+ assertThat(getConstraint(R.id.begin_guide).layout.guideBegin)
+ .isEqualTo(cutoutStart - padding)
+ assertThat(getConstraint(R.id.end_guide).layout.guideEnd)
+ .isEqualTo(cutoutEnd - padding)
+ }
+
+ assertThat(changes.largeScreenConstraintsChanges).isNull()
+ }
+
+ @Test
+ fun testGuidesAreSetInCorrectPosition_smallCutoutLargerPadding() {
+ val cutoutStart = 5
+ val padding = 10
+ val cutoutEnd = 10
+
+ val changes = CombinedShadeHeadersConstraintManagerImpl.edgesGuidelinesConstraints(
+ cutoutStart,
+ padding,
+ cutoutEnd,
+ padding
+ )
+ changes()
+
+ with(qqsConstraint) {
+ assertThat(getConstraint(R.id.begin_guide).layout.guideBegin).isEqualTo(0)
+ assertThat(getConstraint(R.id.end_guide).layout.guideEnd).isEqualTo(0)
+ }
+
+ with(qsConstraint) {
+ assertThat(getConstraint(R.id.begin_guide).layout.guideBegin).isEqualTo(0)
+ assertThat(getConstraint(R.id.end_guide).layout.guideEnd).isEqualTo(0)
+ }
+
+ assertThat(changes.largeScreenConstraintsChanges).isNull()
+ }
+
+ @Test
+ fun testCenterCutoutConstraints_ltr() {
+ val offsetFromEdge = 400
+ val rtl = false
+
+ val changes = CombinedShadeHeadersConstraintManagerImpl
+ .centerCutoutConstraints(rtl, offsetFromEdge)
+ changes()
+
+ // In LTR, center_left is towards the start and center_right is towards the end
+ with(qqsConstraint) {
+ assertThat(getConstraint(R.id.center_left).layout.guideBegin).isEqualTo(offsetFromEdge)
+ assertThat(getConstraint(R.id.center_right).layout.guideEnd).isEqualTo(offsetFromEdge)
+ assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.center_left)
+ assertThat(getConstraint(R.id.statusIcons).layout.startToEnd)
+ .isEqualTo(R.id.center_right)
+ assertThat(getConstraint(R.id.privacy_container).layout.startToEnd)
+ .isEqualTo(R.id.center_right)
+ }
+
+ with(qsConstraint) {
+ assertThat(getConstraint(R.id.center_left).layout.guideBegin).isEqualTo(offsetFromEdge)
+ assertThat(getConstraint(R.id.center_right).layout.guideEnd).isEqualTo(offsetFromEdge)
+
+ assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_left)
+ assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_right)
+
+ assertThat(getConstraint(R.id.statusIcons).layout.startToEnd)
+ .isNotEqualTo(R.id.center_left)
+ assertThat(getConstraint(R.id.statusIcons).layout.startToEnd)
+ .isNotEqualTo(R.id.center_right)
+
+ assertThat(getConstraint(R.id.privacy_container).layout.startToEnd)
+ .isEqualTo(R.id.center_right)
+ }
+
+ assertThat(changes.largeScreenConstraintsChanges).isNull()
+ }
+
+ @Test
+ fun testCenterCutoutConstraints_rtl() {
+ val offsetFromEdge = 400
+ val rtl = true
+
+ val changes = CombinedShadeHeadersConstraintManagerImpl
+ .centerCutoutConstraints(rtl, offsetFromEdge)
+ changes()
+
+ // In RTL, center_left is towards the end and center_right is towards the start
+ with(qqsConstraint) {
+ assertThat(getConstraint(R.id.center_left).layout.guideEnd).isEqualTo(offsetFromEdge)
+ assertThat(getConstraint(R.id.center_right).layout.guideBegin).isEqualTo(offsetFromEdge)
+ assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.center_right)
+ assertThat(getConstraint(R.id.statusIcons).layout.startToEnd)
+ .isEqualTo(R.id.center_left)
+ assertThat(getConstraint(R.id.privacy_container).layout.startToEnd)
+ .isEqualTo(R.id.center_left)
+ }
+
+ with(qsConstraint) {
+ assertThat(getConstraint(R.id.center_left).layout.guideEnd).isEqualTo(offsetFromEdge)
+ assertThat(getConstraint(R.id.center_right).layout.guideBegin).isEqualTo(offsetFromEdge)
+
+ assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_left)
+ assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_right)
+
+ assertThat(getConstraint(R.id.statusIcons).layout.startToEnd)
+ .isNotEqualTo(R.id.center_left)
+ assertThat(getConstraint(R.id.statusIcons).layout.startToEnd)
+ .isNotEqualTo(R.id.center_right)
+
+ assertThat(getConstraint(R.id.privacy_container).layout.startToEnd)
+ .isEqualTo(R.id.center_left)
+ }
+
+ assertThat(changes.largeScreenConstraintsChanges).isNull()
+ }
+
+ private operator fun ConstraintsChanges.invoke() {
+ qqsConstraintsChanges?.invoke(qqsConstraint)
+ qsConstraintsChanges?.invoke(qsConstraint)
+ largeScreenConstraintsChanges?.invoke(largeScreenConstraint)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangeTest.kt
new file mode 100644
index 000000000000..9b2e085560a1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangeTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.shade
+
+import android.testing.AndroidTestingRunner
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ConstraintChangeTest : SysuiTestCase() {
+
+ @Test
+ fun testSumNonNull() {
+ val mock1: ConstraintChange = mock()
+ val mock2: ConstraintChange = mock()
+
+ val constraintSet = ConstraintSet()
+
+ val sum = mock1 + mock2
+ sum?.invoke(constraintSet)
+
+ val inOrder = inOrder(mock1, mock2)
+ inOrder.verify(mock1).invoke(constraintSet)
+ inOrder.verify(mock2).invoke(constraintSet)
+ }
+
+ @Test
+ fun testSumThisNull() {
+ val mock: ConstraintChange = mock()
+ val constraintSet = ConstraintSet()
+
+ val sum = (null as? ConstraintChange?) + mock
+ sum?.invoke(constraintSet)
+
+ verify(mock).invoke(constraintSet)
+ }
+
+ @Test
+ fun testSumThisNull_notWrapped() {
+ val change: ConstraintChange = {}
+
+ val sum = (null as? ConstraintChange?) + change
+ assertThat(sum).isSameInstanceAs(change)
+ }
+
+ @Test
+ fun testSumOtherNull() {
+ val mock: ConstraintChange = mock()
+ val constraintSet = ConstraintSet()
+
+ val sum = mock + (null as? ConstraintChange?)
+ sum?.invoke(constraintSet)
+
+ verify(mock).invoke(constraintSet)
+ }
+
+ @Test
+ fun testSumOtherNull_notWrapped() {
+ val change: ConstraintChange = {}
+
+ val sum = change + (null as? ConstraintChange?)
+ assertThat(sum).isSameInstanceAs(change)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangesTest.kt
new file mode 100644
index 000000000000..0abb08427478
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ConstraintChangesTest.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.shade
+
+import android.testing.AndroidTestingRunner
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.inOrder
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ConstraintChangesTest : SysuiTestCase() {
+
+ @Test
+ fun testSumWithoutNulls() {
+ val mockQQS1: ConstraintChange = mock()
+ val mockQS1: ConstraintChange = mock()
+ val mockLS1: ConstraintChange = mock()
+ val mockQQS2: ConstraintChange = mock()
+ val mockQS2: ConstraintChange = mock()
+ val mockLS2: ConstraintChange = mock()
+
+ val changes1 = ConstraintsChanges(mockQQS1, mockQS1, mockLS1)
+ val changes2 = ConstraintsChanges(mockQQS2, mockQS2, mockLS2)
+
+ val sum = changes1 + changes2
+
+ val constraintSet = ConstraintSet()
+ sum.qqsConstraintsChanges?.invoke(constraintSet)
+ sum.qsConstraintsChanges?.invoke(constraintSet)
+ sum.largeScreenConstraintsChanges?.invoke(constraintSet)
+
+ val inOrder = inOrder(mockQQS1, mockQS1, mockLS1, mockQQS2, mockQS2, mockLS2)
+
+ inOrder.verify(mockQQS1).invoke(constraintSet)
+ inOrder.verify(mockQQS2).invoke(constraintSet)
+ inOrder.verify(mockQS1).invoke(constraintSet)
+ inOrder.verify(mockQS2).invoke(constraintSet)
+ inOrder.verify(mockLS1).invoke(constraintSet)
+ inOrder.verify(mockLS2).invoke(constraintSet)
+ }
+
+ @Test
+ fun testSumWithSomeNulls() {
+ val mockQQS: ConstraintChange = mock()
+ val mockQS: ConstraintChange = mock()
+
+ val changes1 = ConstraintsChanges(mockQQS, null, null)
+ val changes2 = ConstraintsChanges(null, mockQS, null)
+
+ val sum = changes1 + changes2
+
+ assertThat(sum.qqsConstraintsChanges).isSameInstanceAs(mockQQS)
+ assertThat(sum.qsConstraintsChanges).isSameInstanceAs(mockQS)
+ assertThat(sum.largeScreenConstraintsChanges).isNull()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
new file mode 100644
index 000000000000..ed1a13b36d6c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
@@ -0,0 +1,659 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.shade
+
+import android.content.Context
+import android.content.res.Resources
+import android.content.res.XmlResourceParser
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.view.DisplayCutout
+import android.view.View
+import android.view.WindowInsets
+import android.widget.TextView
+import androidx.constraintlayout.motion.widget.MotionLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ShadeInterpolation
+import com.android.systemui.battery.BatteryMeterView
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.qs.ChipVisibilityListener
+import com.android.systemui.qs.HeaderPrivacyIconsController
+import com.android.systemui.qs.carrier.QSCarrierGroup
+import com.android.systemui.qs.carrier.QSCarrierGroupController
+import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.HEADER_TRANSITION_ID
+import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT
+import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.LARGE_SCREEN_HEADER_TRANSITION_ID
+import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
+import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.phone.StatusBarIconController
+import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.statusbar.policy.VariableDateView
+import com.android.systemui.statusbar.policy.VariableDateViewController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Answers
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
+
+private val EMPTY_CHANGES = ConstraintsChanges()
+
+/**
+ * Tests for [LargeScreenShadeHeaderController] when [Flags.COMBINED_QS_HEADERS] is `true`.
+ *
+ * Once that flag is removed, this class will be combined with
+ * [LargeScreenShadeHeaderControllerTest].
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var statusIcons: StatusIconContainer
+ @Mock
+ private lateinit var statusBarIconController: StatusBarIconController
+ @Mock
+ private lateinit var qsCarrierGroupController: QSCarrierGroupController
+ @Mock
+ private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
+ @Mock
+ private lateinit var clock: TextView
+ @Mock
+ private lateinit var date: VariableDateView
+ @Mock
+ private lateinit var carrierGroup: QSCarrierGroup
+ @Mock
+ private lateinit var batteryMeterView: BatteryMeterView
+ @Mock
+ private lateinit var batteryMeterViewController: BatteryMeterViewController
+ @Mock
+ private lateinit var privacyIconsController: HeaderPrivacyIconsController
+ @Mock
+ private lateinit var insetsProvider: StatusBarContentInsetsProvider
+ @Mock
+ private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory
+ @Mock
+ private lateinit var variableDateViewController: VariableDateViewController
+ @Mock
+ private lateinit var dumpManager: DumpManager
+ @Mock
+ private lateinit var combinedShadeHeadersConstraintManager:
+ CombinedShadeHeadersConstraintManager
+
+ @Mock
+ private lateinit var mockedContext: Context
+ @Mock(answer = Answers.RETURNS_MOCKS)
+ private lateinit var view: MotionLayout
+
+ @Mock
+ private lateinit var qqsConstraints: ConstraintSet
+ @Mock
+ private lateinit var qsConstraints: ConstraintSet
+ @Mock
+ private lateinit var largeScreenConstraints: ConstraintSet
+
+ @JvmField @Rule
+ val mockitoRule = MockitoJUnit.rule()
+ var viewVisibility = View.GONE
+
+ private lateinit var controller: LargeScreenShadeHeaderController
+ private lateinit var carrierIconSlots: List<String>
+ private val configurationController = FakeConfigurationController()
+
+ @Before
+ fun setUp() {
+ whenever<TextView>(view.findViewById(R.id.clock)).thenReturn(clock)
+ whenever(clock.context).thenReturn(mockedContext)
+
+ whenever<TextView>(view.findViewById(R.id.date)).thenReturn(date)
+ whenever(date.context).thenReturn(mockedContext)
+ whenever(variableDateViewControllerFactory.create(any()))
+ .thenReturn(variableDateViewController)
+
+ whenever<QSCarrierGroup>(view.findViewById(R.id.carrier_group)).thenReturn(carrierGroup)
+ whenever<BatteryMeterView>(view.findViewById(R.id.batteryRemainingIcon))
+ .thenReturn(batteryMeterView)
+
+ whenever<StatusIconContainer>(view.findViewById(R.id.statusIcons)).thenReturn(statusIcons)
+ whenever(statusIcons.context).thenReturn(context)
+
+ whenever(qsCarrierGroupControllerBuilder.setQSCarrierGroup(any()))
+ .thenReturn(qsCarrierGroupControllerBuilder)
+ whenever(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
+
+ whenever(view.context).thenReturn(context)
+ whenever(view.resources).thenReturn(context.resources)
+ whenever(view.setVisibility(ArgumentMatchers.anyInt())).then {
+ viewVisibility = it.arguments[0] as Int
+ null
+ }
+ whenever(view.visibility).thenAnswer { _ -> viewVisibility }
+
+ whenever(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)).thenReturn(true)
+ whenever(featureFlags.isEnabled(Flags.NEW_HEADER)).thenReturn(true)
+
+ setUpDefaultInsets()
+ setUpMotionLayout(view)
+
+ controller = LargeScreenShadeHeaderController(
+ view,
+ statusBarIconController,
+ privacyIconsController,
+ insetsProvider,
+ configurationController,
+ variableDateViewControllerFactory,
+ batteryMeterViewController,
+ dumpManager,
+ featureFlags,
+ qsCarrierGroupControllerBuilder,
+ combinedShadeHeadersConstraintManager
+ )
+ whenever(view.isAttachedToWindow).thenReturn(true)
+ controller.init()
+ carrierIconSlots = listOf(
+ context.getString(com.android.internal.R.string.status_bar_mobile))
+ }
+
+ @Test
+ fun testCorrectConstraints() {
+ val captor = ArgumentCaptor.forClass(XmlResourceParser::class.java)
+
+ verify(qqsConstraints).load(eq(context), capture(captor))
+ assertThat(captor.value.getResId()).isEqualTo(R.xml.qqs_header)
+
+ verify(qsConstraints).load(eq(context), capture(captor))
+ assertThat(captor.value.getResId()).isEqualTo(R.xml.qs_header_new)
+
+ verify(largeScreenConstraints).load(eq(context), capture(captor))
+ assertThat(captor.value.getResId()).isEqualTo(R.xml.large_screen_shade_header)
+ }
+
+ @Test
+ fun testControllersCreatedAndInitialized() {
+ verify(variableDateViewController).init()
+
+ verify(batteryMeterViewController).init()
+ verify(batteryMeterViewController).ignoreTunerUpdates()
+ verify(batteryMeterView).setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
+
+ val inOrder = inOrder(qsCarrierGroupControllerBuilder)
+ inOrder.verify(qsCarrierGroupControllerBuilder).setQSCarrierGroup(carrierGroup)
+ inOrder.verify(qsCarrierGroupControllerBuilder).build()
+ }
+
+ @Test
+ fun testClockPivotLtr() {
+ val width = 200
+ whenever(clock.width).thenReturn(width)
+ whenever(clock.isLayoutRtl).thenReturn(false)
+
+ val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java)
+ verify(clock).addOnLayoutChangeListener(capture(captor))
+
+ captor.value.onLayoutChange(clock, 0, 1, 2, 3, 4, 5, 6, 7)
+ verify(clock).pivotX = 0f
+ }
+
+ @Test
+ fun testClockPivotRtl() {
+ val width = 200
+ whenever(clock.width).thenReturn(width)
+ whenever(clock.isLayoutRtl).thenReturn(true)
+
+ val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java)
+ verify(clock).addOnLayoutChangeListener(capture(captor))
+
+ captor.value.onLayoutChange(clock, 0, 1, 2, 3, 4, 5, 6, 7)
+ verify(clock).pivotX = width.toFloat()
+ }
+
+ @Test
+ fun testShadeExpanded_true() {
+ // When shade is expanded, view should be visible regardless of largeScreenActive
+ controller.largeScreenActive = false
+ controller.qsVisible = true
+ assertThat(viewVisibility).isEqualTo(View.VISIBLE)
+
+ controller.largeScreenActive = true
+ assertThat(viewVisibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun testShadeExpanded_false() {
+ // When shade is not expanded, view should be invisible regardless of largeScreenActive
+ controller.largeScreenActive = false
+ controller.qsVisible = false
+ assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
+
+ controller.largeScreenActive = true
+ assertThat(viewVisibility).isEqualTo(View.INVISIBLE)
+ }
+
+ @Test
+ fun testLargeScreenActive_true() {
+ controller.largeScreenActive = false // Make sure there's a change
+ clearInvocations(view)
+
+ controller.largeScreenActive = true
+
+ verify(view).setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID)
+ }
+
+ @Test
+ fun testLargeScreenActive_false() {
+ controller.largeScreenActive = true // Make sure there's a change
+ clearInvocations(view)
+
+ controller.largeScreenActive = false
+
+ verify(view).setTransition(HEADER_TRANSITION_ID)
+ }
+
+ @Test
+ fun testShadeExpandedFraction() {
+ // View needs to be visible for this to actually take effect
+ controller.qsVisible = true
+
+ clearInvocations(view)
+ controller.shadeExpandedFraction = 0.3f
+ verify(view).alpha = ShadeInterpolation.getContentAlpha(0.3f)
+
+ clearInvocations(view)
+ controller.shadeExpandedFraction = 1f
+ verify(view).alpha = ShadeInterpolation.getContentAlpha(1f)
+
+ clearInvocations(view)
+ controller.shadeExpandedFraction = 0f
+ verify(view).alpha = ShadeInterpolation.getContentAlpha(0f)
+ }
+
+ @Test
+ fun testQsExpandedFraction_headerTransition() {
+ controller.qsVisible = true
+ controller.largeScreenActive = false
+
+ clearInvocations(view)
+ controller.qsExpandedFraction = 0.3f
+ verify(view).progress = 0.3f
+ }
+
+ @Test
+ fun testQsExpandedFraction_largeScreen() {
+ controller.qsVisible = true
+ controller.largeScreenActive = true
+
+ clearInvocations(view)
+ controller.qsExpandedFraction = 0.3f
+ verify(view, never()).progress = anyFloat()
+ }
+
+ @Test
+ fun testScrollY_headerTransition() {
+ controller.largeScreenActive = false
+
+ clearInvocations(view)
+ controller.qsScrollY = 20
+ verify(view).scrollY = 20
+ }
+
+ @Test
+ fun testScrollY_largeScreen() {
+ controller.largeScreenActive = true
+
+ clearInvocations(view)
+ controller.qsScrollY = 20
+ verify(view, never()).scrollY = anyInt()
+ }
+
+ @Test
+ fun testPrivacyChipVisibilityChanged_visible_changesCorrectConstraints() {
+ val chipVisibleChanges = createMockConstraintChanges()
+ val chipNotVisibleChanges = createMockConstraintChanges()
+
+ whenever(combinedShadeHeadersConstraintManager.privacyChipVisibilityConstraints(true))
+ .thenReturn(chipVisibleChanges)
+ whenever(combinedShadeHeadersConstraintManager.privacyChipVisibilityConstraints(false))
+ .thenReturn(chipNotVisibleChanges)
+
+ val captor = ArgumentCaptor.forClass(ChipVisibilityListener::class.java)
+ verify(privacyIconsController).chipVisibilityListener = capture(captor)
+
+ captor.value.onChipVisibilityRefreshed(true)
+
+ verify(chipVisibleChanges.qqsConstraintsChanges)!!.invoke(qqsConstraints)
+ verify(chipVisibleChanges.qsConstraintsChanges)!!.invoke(qsConstraints)
+ verify(chipVisibleChanges.largeScreenConstraintsChanges)!!.invoke(largeScreenConstraints)
+
+ verify(chipNotVisibleChanges.qqsConstraintsChanges, never())!!.invoke(any())
+ verify(chipNotVisibleChanges.qsConstraintsChanges, never())!!.invoke(any())
+ verify(chipNotVisibleChanges.largeScreenConstraintsChanges, never())!!.invoke(any())
+ }
+
+ @Test
+ fun testPrivacyChipVisibilityChanged_notVisible_changesCorrectConstraints() {
+ val chipVisibleChanges = createMockConstraintChanges()
+ val chipNotVisibleChanges = createMockConstraintChanges()
+
+ whenever(combinedShadeHeadersConstraintManager.privacyChipVisibilityConstraints(true))
+ .thenReturn(chipVisibleChanges)
+ whenever(combinedShadeHeadersConstraintManager.privacyChipVisibilityConstraints(false))
+ .thenReturn(chipNotVisibleChanges)
+
+ val captor = ArgumentCaptor.forClass(ChipVisibilityListener::class.java)
+ verify(privacyIconsController).chipVisibilityListener = capture(captor)
+
+ captor.value.onChipVisibilityRefreshed(false)
+
+ verify(chipVisibleChanges.qqsConstraintsChanges, never())!!.invoke(qqsConstraints)
+ verify(chipVisibleChanges.qsConstraintsChanges, never())!!.invoke(qsConstraints)
+ verify(chipVisibleChanges.largeScreenConstraintsChanges, never())!!
+ .invoke(largeScreenConstraints)
+
+ verify(chipNotVisibleChanges.qqsConstraintsChanges)!!.invoke(any())
+ verify(chipNotVisibleChanges.qsConstraintsChanges)!!.invoke(any())
+ verify(chipNotVisibleChanges.largeScreenConstraintsChanges)!!.invoke(any())
+ }
+
+ @Test
+ fun testInsetsGuides_ltr() {
+ whenever(view.isLayoutRtl).thenReturn(false)
+ val captor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
+ verify(view).setOnApplyWindowInsetsListener(capture(captor))
+ val mockConstraintsChanges = createMockConstraintChanges()
+
+ val (insetLeft, insetRight) = 30 to 40
+ val (paddingStart, paddingEnd) = 10 to 20
+ whenever(view.paddingStart).thenReturn(paddingStart)
+ whenever(view.paddingEnd).thenReturn(paddingEnd)
+
+ mockInsetsProvider(insetLeft to insetRight, false)
+
+ whenever(combinedShadeHeadersConstraintManager
+ .edgesGuidelinesConstraints(anyInt(), anyInt(), anyInt(), anyInt())
+ ).thenReturn(mockConstraintsChanges)
+
+ captor.value.onApplyWindowInsets(view, createWindowInsets())
+
+ verify(combinedShadeHeadersConstraintManager)
+ .edgesGuidelinesConstraints(insetLeft, paddingStart, insetRight, paddingEnd)
+
+ verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.largeScreenConstraintsChanges)!!.invoke(any())
+ }
+
+ @Test
+ fun testInsetsGuides_rtl() {
+ whenever(view.isLayoutRtl).thenReturn(true)
+ val captor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
+ verify(view).setOnApplyWindowInsetsListener(capture(captor))
+ val mockConstraintsChanges = createMockConstraintChanges()
+
+ val (insetLeft, insetRight) = 30 to 40
+ val (paddingStart, paddingEnd) = 10 to 20
+ whenever(view.paddingStart).thenReturn(paddingStart)
+ whenever(view.paddingEnd).thenReturn(paddingEnd)
+
+ mockInsetsProvider(insetLeft to insetRight, false)
+
+ whenever(combinedShadeHeadersConstraintManager
+ .edgesGuidelinesConstraints(anyInt(), anyInt(), anyInt(), anyInt())
+ ).thenReturn(mockConstraintsChanges)
+
+ captor.value.onApplyWindowInsets(view, createWindowInsets())
+
+ verify(combinedShadeHeadersConstraintManager)
+ .edgesGuidelinesConstraints(insetRight, paddingStart, insetLeft, paddingEnd)
+
+ verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.largeScreenConstraintsChanges)!!.invoke(any())
+ }
+
+ @Test
+ fun testNullCutout() {
+ val captor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
+ verify(view).setOnApplyWindowInsetsListener(capture(captor))
+ val mockConstraintsChanges = createMockConstraintChanges()
+
+ whenever(combinedShadeHeadersConstraintManager.emptyCutoutConstraints())
+ .thenReturn(mockConstraintsChanges)
+
+ captor.value.onApplyWindowInsets(view, createWindowInsets(null))
+
+ verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints()
+ verify(combinedShadeHeadersConstraintManager, never())
+ .centerCutoutConstraints(anyBoolean(), anyInt())
+
+ verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.largeScreenConstraintsChanges)!!.invoke(any())
+ }
+
+ @Test
+ fun testEmptyCutout() {
+ val captor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
+ verify(view).setOnApplyWindowInsetsListener(capture(captor))
+ val mockConstraintsChanges = createMockConstraintChanges()
+
+ whenever(combinedShadeHeadersConstraintManager.emptyCutoutConstraints())
+ .thenReturn(mockConstraintsChanges)
+
+ captor.value.onApplyWindowInsets(view, createWindowInsets())
+
+ verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints()
+ verify(combinedShadeHeadersConstraintManager, never())
+ .centerCutoutConstraints(anyBoolean(), anyInt())
+
+ verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.largeScreenConstraintsChanges)!!.invoke(any())
+ }
+
+ @Test
+ fun testCornerCutout_emptyRect() {
+ val captor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
+ verify(view).setOnApplyWindowInsetsListener(capture(captor))
+ val mockConstraintsChanges = createMockConstraintChanges()
+
+ mockInsetsProvider(0 to 0, true)
+
+ whenever(combinedShadeHeadersConstraintManager.emptyCutoutConstraints())
+ .thenReturn(mockConstraintsChanges)
+
+ captor.value.onApplyWindowInsets(view, createWindowInsets())
+
+ verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints()
+ verify(combinedShadeHeadersConstraintManager, never())
+ .centerCutoutConstraints(anyBoolean(), anyInt())
+
+ verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.largeScreenConstraintsChanges)!!.invoke(any())
+ }
+
+ @Test
+ fun testCornerCutout_nonEmptyRect() {
+ val captor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
+ verify(view).setOnApplyWindowInsetsListener(capture(captor))
+ val mockConstraintsChanges = createMockConstraintChanges()
+
+ mockInsetsProvider(0 to 0, true)
+
+ whenever(combinedShadeHeadersConstraintManager.emptyCutoutConstraints())
+ .thenReturn(mockConstraintsChanges)
+
+ captor.value.onApplyWindowInsets(view, createWindowInsets(Rect(1, 2, 3, 4)))
+
+ verify(combinedShadeHeadersConstraintManager).emptyCutoutConstraints()
+ verify(combinedShadeHeadersConstraintManager, never())
+ .centerCutoutConstraints(anyBoolean(), anyInt())
+
+ verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.largeScreenConstraintsChanges)!!.invoke(any())
+ }
+
+ @Test
+ fun testTopCutout_ltr() {
+ val width = 100
+ val paddingLeft = 10
+ val paddingRight = 20
+ val cutoutWidth = 30
+
+ whenever(view.isLayoutRtl).thenReturn(false)
+ whenever(view.width).thenReturn(width)
+ whenever(view.paddingLeft).thenReturn(paddingLeft)
+ whenever(view.paddingRight).thenReturn(paddingRight)
+
+ val captor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
+ verify(view).setOnApplyWindowInsetsListener(capture(captor))
+ val mockConstraintsChanges = createMockConstraintChanges()
+
+ mockInsetsProvider(0 to 0, false)
+
+ whenever(combinedShadeHeadersConstraintManager
+ .centerCutoutConstraints(anyBoolean(), anyInt())
+ ).thenReturn(mockConstraintsChanges)
+
+ captor.value.onApplyWindowInsets(view, createWindowInsets(Rect(0, 0, cutoutWidth, 1)))
+
+ verify(combinedShadeHeadersConstraintManager, never()).emptyCutoutConstraints()
+ val offset = (width - paddingLeft - paddingRight - cutoutWidth) / 2
+ verify(combinedShadeHeadersConstraintManager).centerCutoutConstraints(false, offset)
+
+ verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.largeScreenConstraintsChanges)!!.invoke(any())
+ }
+
+ @Test
+ fun testTopCutout_rtl() {
+ val width = 100
+ val paddingLeft = 10
+ val paddingRight = 20
+ val cutoutWidth = 30
+
+ whenever(view.isLayoutRtl).thenReturn(true)
+ whenever(view.width).thenReturn(width)
+ whenever(view.paddingLeft).thenReturn(paddingLeft)
+ whenever(view.paddingRight).thenReturn(paddingRight)
+
+ val captor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java)
+ verify(view).setOnApplyWindowInsetsListener(capture(captor))
+ val mockConstraintsChanges = createMockConstraintChanges()
+
+ mockInsetsProvider(0 to 0, false)
+
+ whenever(combinedShadeHeadersConstraintManager
+ .centerCutoutConstraints(anyBoolean(), anyInt())
+ ).thenReturn(mockConstraintsChanges)
+
+ captor.value.onApplyWindowInsets(view, createWindowInsets(Rect(0, 0, cutoutWidth, 1)))
+
+ verify(combinedShadeHeadersConstraintManager, never()).emptyCutoutConstraints()
+ val offset = (width - paddingLeft - paddingRight - cutoutWidth) / 2
+ verify(combinedShadeHeadersConstraintManager).centerCutoutConstraints(true, offset)
+
+ verify(mockConstraintsChanges.qqsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.qsConstraintsChanges)!!.invoke(any())
+ verify(mockConstraintsChanges.largeScreenConstraintsChanges)!!.invoke(any())
+ }
+
+ private fun createWindowInsets(
+ topCutout: Rect? = Rect()
+ ): WindowInsets {
+ val windowInsets: WindowInsets = mock()
+ val displayCutout: DisplayCutout = mock()
+ whenever(windowInsets.displayCutout)
+ .thenReturn(if (topCutout != null) displayCutout else null)
+ whenever(displayCutout.boundingRectTop).thenReturn(topCutout)
+
+ return windowInsets
+ }
+
+ private fun mockInsetsProvider(
+ insets: Pair<Int, Int> = 0 to 0,
+ cornerCutout: Boolean = false,
+ ) {
+ whenever(insetsProvider.getStatusBarContentInsetsForCurrentRotation())
+ .thenReturn(insets.toAndroidPair())
+ whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(cornerCutout)
+ }
+
+ private fun createMockConstraintChanges(): ConstraintsChanges {
+ return ConstraintsChanges(mock(), mock(), mock())
+ }
+
+ private fun XmlResourceParser.getResId(): Int {
+ return Resources.getAttributeSetSourceResId(this)
+ }
+
+ private fun setUpMotionLayout(motionLayout: MotionLayout) {
+ whenever(motionLayout.getConstraintSet(QQS_HEADER_CONSTRAINT)).thenReturn(qqsConstraints)
+ whenever(motionLayout.getConstraintSet(QS_HEADER_CONSTRAINT)).thenReturn(qsConstraints)
+ whenever(motionLayout.getConstraintSet(LARGE_SCREEN_HEADER_CONSTRAINT))
+ .thenReturn(largeScreenConstraints)
+ }
+
+ private fun setUpDefaultInsets() {
+ whenever(combinedShadeHeadersConstraintManager
+ .edgesGuidelinesConstraints(anyInt(), anyInt(), anyInt(), anyInt())
+ ).thenReturn(EMPTY_CHANGES)
+ whenever(combinedShadeHeadersConstraintManager.emptyCutoutConstraints())
+ .thenReturn(EMPTY_CHANGES)
+ whenever(combinedShadeHeadersConstraintManager
+ .centerCutoutConstraints(anyBoolean(), anyInt())
+ ).thenReturn(EMPTY_CHANGES)
+ whenever(combinedShadeHeadersConstraintManager
+ .privacyChipVisibilityConstraints(anyBoolean())
+ ).thenReturn(EMPTY_CHANGES)
+ whenever(insetsProvider.getStatusBarContentInsetsForCurrentRotation())
+ .thenReturn(Pair(0, 0).toAndroidPair())
+ whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(false)
+ }
+
+ private fun<T, U> Pair<T, U>.toAndroidPair(): android.util.Pair<T, U> {
+ return android.util.Pair(first, second)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LargeScreenShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
index 80664013f95d..02b26dbbc32d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LargeScreenShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt
@@ -1,10 +1,8 @@
-package com.android.systemui.statusbar.phone
+package com.android.systemui.shade
import android.app.StatusBarManager
import android.content.Context
-import android.content.res.TypedArray
import android.testing.AndroidTestingRunner
-import android.util.TypedValue.COMPLEX_UNIT_PX
import android.view.View
import android.widget.TextView
import androidx.test.filters.SmallTest
@@ -19,19 +17,24 @@ import com.android.systemui.flags.Flags
import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.phone.StatusBarIconController
+import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.statusbar.policy.VariableDateViewController
+import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
+import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -49,10 +52,14 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
@Mock private lateinit var batteryMeterView: BatteryMeterView
@Mock private lateinit var batteryMeterViewController: BatteryMeterViewController
@Mock private lateinit var privacyIconsController: HeaderPrivacyIconsController
+ @Mock private lateinit var insetsProvider: StatusBarContentInsetsProvider
+ @Mock private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory
+ @Mock private lateinit var variableDateViewController: VariableDateViewController
@Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var combinedShadeHeadersConstraintManager:
+ CombinedShadeHeadersConstraintManager
@Mock private lateinit var mockedContext: Context
- @Mock private lateinit var typedArray: TypedArray
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
var viewVisibility = View.GONE
@@ -65,7 +72,6 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
fun setup() {
whenever<TextView>(view.findViewById(R.id.clock)).thenReturn(clock)
whenever(clock.context).thenReturn(mockedContext)
- whenever(mockedContext.obtainStyledAttributes(anyInt(), any())).thenReturn(typedArray)
whenever<TextView>(view.findViewById(R.id.date)).thenReturn(date)
whenever(date.context).thenReturn(mockedContext)
whenever<QSCarrierGroup>(view.findViewById(R.id.carrier_group)).thenReturn(carrierGroup)
@@ -73,6 +79,7 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
.thenReturn(batteryMeterView)
whenever<StatusIconContainer>(view.findViewById(R.id.statusIcons)).thenReturn(statusIcons)
whenever(view.context).thenReturn(context)
+ whenever(view.resources).thenReturn(context.resources)
whenever(statusIcons.context).thenReturn(context)
whenever(qsCarrierGroupControllerBuilder.setQSCarrierGroup(any()))
.thenReturn(qsCarrierGroupControllerBuilder)
@@ -82,27 +89,39 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
null
}
whenever(view.visibility).thenAnswer { _ -> viewVisibility }
+ whenever(variableDateViewControllerFactory.create(any()))
+ .thenReturn(variableDateViewController)
whenever(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)).thenReturn(false)
mLargeScreenShadeHeaderController = LargeScreenShadeHeaderController(
view,
statusBarIconController,
privacyIconsController,
+ insetsProvider,
configurationController,
- qsCarrierGroupControllerBuilder,
- featureFlags,
+ variableDateViewControllerFactory,
batteryMeterViewController,
- dumpManager
+ dumpManager,
+ featureFlags,
+ qsCarrierGroupControllerBuilder,
+ combinedShadeHeadersConstraintManager
)
+ whenever(view.isAttachedToWindow).thenReturn(true)
+ mLargeScreenShadeHeaderController.init()
carrierIconSlots = listOf(
context.getString(com.android.internal.R.string.status_bar_mobile))
}
+ @After
+ fun verifyEveryTest() {
+ verifyZeroInteractions(combinedShadeHeadersConstraintManager)
+ }
+
@Test
fun setVisible_onlyWhenActive() {
makeShadeVisible()
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
- mLargeScreenShadeHeaderController.active = false
+ mLargeScreenShadeHeaderController.largeScreenActive = false
assertThat(viewVisibility).isEqualTo(View.GONE)
}
@@ -156,41 +175,16 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
}
private fun makeShadeVisible() {
- mLargeScreenShadeHeaderController.active = true
- mLargeScreenShadeHeaderController.shadeExpanded = true
+ mLargeScreenShadeHeaderController.largeScreenActive = true
+ mLargeScreenShadeHeaderController.qsVisible = true
}
@Test
- fun updateConfig_changesFontSize() {
- val updatedTextPixelSize = 32
- setReturnTextSize(updatedTextPixelSize)
-
+ fun updateConfig_changesFontStyle() {
configurationController.notifyDensityOrFontScaleChanged()
- verify(clock).setTextSize(COMPLEX_UNIT_PX, updatedTextPixelSize.toFloat())
- verify(date).setTextSize(COMPLEX_UNIT_PX, updatedTextPixelSize.toFloat())
- verify(carrierGroup).updateTextAppearance(R.style.TextAppearance_QS_Status)
- }
-
- @Test
- fun updateConfig_changesFontSizeMultipleTimes() {
- val updatedTextPixelSize1 = 32
- setReturnTextSize(updatedTextPixelSize1)
- configurationController.notifyDensityOrFontScaleChanged()
- verify(clock).setTextSize(COMPLEX_UNIT_PX, updatedTextPixelSize1.toFloat())
- verify(date).setTextSize(COMPLEX_UNIT_PX, updatedTextPixelSize1.toFloat())
- verify(carrierGroup).updateTextAppearance(R.style.TextAppearance_QS_Status)
- clearInvocations(carrierGroup)
-
- val updatedTextPixelSize2 = 42
- setReturnTextSize(updatedTextPixelSize2)
- configurationController.notifyDensityOrFontScaleChanged()
- verify(clock).setTextSize(COMPLEX_UNIT_PX, updatedTextPixelSize2.toFloat())
- verify(date).setTextSize(COMPLEX_UNIT_PX, updatedTextPixelSize2.toFloat())
- verify(carrierGroup).updateTextAppearance(R.style.TextAppearance_QS_Status)
- }
-
- private fun setReturnTextSize(resultTextSize: Int) {
- whenever(typedArray.getDimensionPixelSize(anyInt(), anyInt())).thenReturn(resultTextSize)
+ verify(clock).setTextAppearance(R.style.TextAppearance_QS_Status)
+ verify(date).setTextAppearance(R.style.TextAppearance_QS_Status)
+ verify(carrierGroup).updateTextAppearance(R.style.TextAppearance_QS_Status_Carriers)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 8900d8ff9fda..30c1b2594ee8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.shade;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -60,6 +60,7 @@ import android.view.View;
import android.view.ViewParent;
import android.view.ViewPropertyAnimator;
import android.view.ViewStub;
+import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -86,6 +87,7 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.camera.CameraGestureHelper;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.controls.dagger.ControlsComponent;
@@ -104,8 +106,8 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -130,8 +132,27 @@ import com.android.systemui.statusbar.notification.stack.NotificationRoundnessMa
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
+import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
+import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
+import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
+import com.android.systemui.statusbar.phone.PanelViewController;
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
+import com.android.systemui.statusbar.phone.TapAgainViewController;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
-import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -172,6 +193,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
@Mock
private KeyguardBottomAreaView mKeyguardBottomArea;
@Mock
+ private KeyguardBottomAreaViewController mKeyguardBottomAreaViewController;
+ @Mock
private KeyguardBottomAreaView mQsFrame;
private KeyguardStatusView mKeyguardStatusView;
@Mock
@@ -349,6 +372,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
private View mQsHeader;
@Mock
private ViewParent mViewParent;
+ @Mock
+ private ViewTreeObserver mViewTreeObserver;
private NotificationPanelViewController.PanelEventsEmitter mPanelEventsEmitter;
private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
private SysuiStatusBarStateController mStatusBarStateController;
@@ -397,9 +422,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000);
when(mNotificationStackScrollLayoutController.getHeadsUpCallback())
.thenReturn(mHeadsUpCallback);
+ when(mKeyguardBottomAreaViewController.getView()).thenReturn(mKeyguardBottomArea);
when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea);
- when(mKeyguardBottomArea.getLeftView()).thenReturn(mock(KeyguardAffordanceView.class));
- when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class));
when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class));
when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
when(mView.findViewById(R.id.keyguard_status_view))
@@ -474,14 +498,15 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
return null;
}).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
+ when(mView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
when(mView.getParent()).thenReturn(mViewParent);
when(mQs.getHeader()).thenReturn(mQsHeader);
mMainHandler = new Handler(Looper.getMainLooper());
mPanelEventsEmitter = new NotificationPanelViewController.PanelEventsEmitter();
- mNotificationPanelViewController = new NotificationPanelViewController(mView,
- mResources,
+ mNotificationPanelViewController = new NotificationPanelViewController(
+ mView,
mMainHandler,
mLayoutInflater,
mFeatureFlags,
@@ -522,8 +547,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mQuickAccessWalletController,
mQrCodeScannerController,
mRecordingController,
- mExecutor,
- mSecureSettings,
mLargeScreenShadeHeaderController,
mScreenOffAnimationController,
mLockscreenGestureLogger,
@@ -534,13 +557,15 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mInteractionJankMonitor,
mQsFrameTranslateController,
mSysUiState,
+ () -> mKeyguardBottomAreaViewController,
mKeyguardUnlockAnimationController,
mNotificationListContainer,
mPanelEventsEmitter,
mNotificationStackSizeCalculator,
mUnlockedScreenOffAnimationController,
mShadeTransitionController,
- mSystemClock);
+ mSystemClock,
+ mock(CameraGestureHelper.class));
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
() -> {},
@@ -558,7 +583,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
ArgumentCaptor.forClass(View.AccessibilityDelegate.class);
verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture());
mAccessibiltyDelegate = accessibilityDelegateArgumentCaptor.getValue();
- mNotificationPanelViewController.mStatusBarStateController
+ mNotificationPanelViewController.getStatusBarStateController()
.addCallback(mNotificationPanelViewController.mStatusBarStateListener);
mNotificationPanelViewController
.setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class));
@@ -993,7 +1018,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mNotificationPanelViewController.flingToHeight(
0f,
true,
- mNotificationPanelViewController.mExpandedHeight,
+ mNotificationPanelViewController.getExpandedHeight(),
1f,
false);
// Verify that the NSSL is notified that the panel is *not* flinging.
@@ -1021,6 +1046,16 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
}
@Test
+ public void testRotatingToSplitShadeWithQsExpanded_transitionsToShadeLocked() {
+ mStatusBarStateController.setState(KEYGUARD);
+ mNotificationPanelViewController.setQsExpanded(true);
+
+ enableSplitShade(true);
+
+ assertThat(mStatusBarStateController.getState()).isEqualTo(SHADE_LOCKED);
+ }
+
+ @Test
public void testSwitchesToCorrectClockInSinglePaneShade() {
mStatusBarStateController.setState(KEYGUARD);
@@ -1146,11 +1181,11 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mStatusBarStateController.setState(SHADE);
when(mResources.getBoolean(R.bool.config_use_large_screen_shade_header)).thenReturn(true);
mNotificationPanelViewController.updateResources();
- verify(mLargeScreenShadeHeaderController).setActive(true);
+ verify(mLargeScreenShadeHeaderController).setLargeScreenActive(true);
when(mResources.getBoolean(R.bool.config_use_large_screen_shade_header)).thenReturn(false);
mNotificationPanelViewController.updateResources();
- verify(mLargeScreenShadeHeaderController).setActive(false);
+ verify(mLargeScreenShadeHeaderController).setLargeScreenActive(false);
}
@Test
@@ -1177,14 +1212,14 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
when(mPowerManager.isPowerSaveMode()).thenReturn(false);
when(mAmbientState.getDozeAmount()).thenReturn(0f);
mNotificationPanelViewController.startUnlockHintAnimation();
- assertThat(mNotificationPanelViewController.mHintAnimationRunning).isTrue();
+ assertThat(mNotificationPanelViewController.isHintAnimationRunning()).isTrue();
}
@Test
public void testUnlockHintAnimation_doesNotRun_inPowerSaveMode() {
when(mPowerManager.isPowerSaveMode()).thenReturn(true);
mNotificationPanelViewController.startUnlockHintAnimation();
- assertThat(mNotificationPanelViewController.mHintAnimationRunning).isFalse();
+ assertThat(mNotificationPanelViewController.isHintAnimationRunning()).isFalse();
}
@Test
@@ -1192,7 +1227,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
when(mPowerManager.isPowerSaveMode()).thenReturn(false);
when(mAmbientState.getDozeAmount()).thenReturn(0.5f);
mNotificationPanelViewController.startUnlockHintAnimation();
- assertThat(mNotificationPanelViewController.mHintAnimationRunning).isFalse();
+ assertThat(mNotificationPanelViewController.isHintAnimationRunning()).isFalse();
}
@Test
@@ -1301,6 +1336,43 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
false/*goingToFullShade*/, SHADE/*oldStatusBarState*/);
}
+ @Test
+ public void getMaxPanelHeight_expanding_inSplitShade_returnsSplitShadeFullTransitionDistance() {
+ int splitShadeFullTransitionDistance = 123456;
+ enableSplitShade(true);
+ setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance);
+ mNotificationPanelViewController.expandWithQs();
+
+ int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight();
+
+ assertThat(maxPanelHeight).isEqualTo(splitShadeFullTransitionDistance);
+ }
+
+ @Test
+ public void getMaxPanelHeight_expandingSplitShade_keyguard_returnsNonSplitShadeValue() {
+ mStatusBarStateController.setState(KEYGUARD);
+ int splitShadeFullTransitionDistance = 123456;
+ enableSplitShade(true);
+ setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance);
+ mNotificationPanelViewController.expandWithQs();
+
+ int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight();
+
+ assertThat(maxPanelHeight).isNotEqualTo(splitShadeFullTransitionDistance);
+ }
+
+ @Test
+ public void getMaxPanelHeight_expanding_notSplitShade_returnsNonSplitShadeValue() {
+ int splitShadeFullTransitionDistance = 123456;
+ enableSplitShade(false);
+ setSplitShadeFullTransitionDistance(splitShadeFullTransitionDistance);
+ mNotificationPanelViewController.expandWithQs();
+
+ int maxPanelHeight = mNotificationPanelViewController.getMaxPanelHeight();
+
+ assertThat(maxPanelHeight).isNotEqualTo(splitShadeFullTransitionDistance);
+ }
+
private static MotionEvent createMotionEvent(int x, int y, int action) {
return MotionEvent.obtain(
/* downTime= */ 0, /* eventTime= */ 0, action, x, y, /* metaState= */ 0);
@@ -1360,4 +1432,10 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
private void onTouchEvent(MotionEvent ev) {
mTouchHandler.onTouch(mView, ev);
}
+
+ private void setSplitShadeFullTransitionDistance(int splitShadeFullTransitionDistance) {
+ when(mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance))
+ .thenReturn(splitShadeFullTransitionDistance);
+ mNotificationPanelViewController.updateResources();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
index de40b7fd0a13..0c6a6a98052f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
@@ -1,4 +1,4 @@
-package com.android.systemui.statusbar.phone
+package com.android.systemui.shade
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index c402d2e47cf3..1dfd7c26ecbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.shade;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -28,8 +28,10 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@@ -42,6 +44,8 @@ import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.view.WindowManager;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.ViewTreeLifecycleOwner;
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
@@ -51,6 +55,11 @@ import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
+import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -61,6 +70,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -69,7 +79,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
@Mock private WindowManager mWindowManager;
@Mock private DozeParameters mDozeParameters;
- @Mock private NotificationShadeWindowView mNotificationShadeWindowView;
+ @Spy private final NotificationShadeWindowView mNotificationShadeWindowView = spy(
+ new NotificationShadeWindowView(mContext, null));
@Mock private IActivityManager mActivityManager;
@Mock private SysuiStatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
@@ -85,6 +96,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -177,6 +189,24 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
}
@Test
+ public void attach_setsUpLifecycleOwner() {
+ mNotificationShadeWindowController.attach();
+
+ assertThat(ViewTreeLifecycleOwner.get(mNotificationShadeWindowView)).isNotNull();
+ }
+
+ @Test
+ public void attach_doesNotSetUpLifecycleOwnerIfAlreadySet() {
+ final LifecycleOwner previouslySet = mock(LifecycleOwner.class);
+ ViewTreeLifecycleOwner.set(mNotificationShadeWindowView, previouslySet);
+
+ mNotificationShadeWindowController.attach();
+
+ assertThat(ViewTreeLifecycleOwner.get(mNotificationShadeWindowView))
+ .isEqualTo(previouslySet);
+ }
+
+ @Test
public void setScrimsVisibility_earlyReturn() {
clearInvocations(mWindowManager);
mNotificationShadeWindowController.setScrimsVisibility(ScrimController.TRANSPARENT);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
new file mode 100644
index 000000000000..471918cfa695
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.shade
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.MotionEvent
+import androidx.test.filters.SmallTest
+import com.android.keyguard.LockIconViewController
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.dock.DockManager
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.lowlightclock.LowLightClockController
+import com.android.systemui.statusbar.LockscreenShadeTransitionController
+import com.android.systemui.statusbar.NotificationShadeDepthController
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.notification.stack.AmbientState
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
+import com.android.systemui.statusbar.window.StatusBarWindowStateController
+import com.android.systemui.tuner.TunerService
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
+ @Mock
+ private lateinit var view: NotificationShadeWindowView
+ @Mock
+ private lateinit var tunserService: TunerService
+ @Mock
+ private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
+ @Mock
+ private lateinit var centralSurfaces: CentralSurfaces
+ @Mock
+ private lateinit var dockManager: DockManager
+ @Mock
+ private lateinit var notificationPanelViewController: NotificationPanelViewController
+ @Mock
+ private lateinit var notificationShadeDepthController: NotificationShadeDepthController
+ @Mock
+ private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock
+ private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
+ @Mock
+ private lateinit var ambientState: AmbientState
+ @Mock
+ private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
+ @Mock
+ private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock
+ private lateinit var statusBarWindowStateController: StatusBarWindowStateController
+ @Mock
+ private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
+ @Mock
+ private lateinit var lockIconViewController: LockIconViewController
+ @Mock
+ private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
+ @Mock
+ private lateinit var lowLightClockController: LowLightClockController
+
+ private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
+ private lateinit var interactionEventHandler: InteractionEventHandler
+
+ private lateinit var underTest: NotificationShadeWindowViewController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(view.bottom).thenReturn(VIEW_BOTTOM)
+
+ underTest = NotificationShadeWindowViewController(
+ lockscreenShadeTransitionController,
+ FalsingCollectorFake(),
+ tunserService,
+ sysuiStatusBarStateController,
+ dockManager,
+ notificationShadeDepthController,
+ view,
+ notificationPanelViewController,
+ PanelExpansionStateManager(),
+ stackScrollLayoutController,
+ statusBarKeyguardViewManager,
+ statusBarWindowStateController,
+ lockIconViewController,
+ Optional.of(lowLightClockController),
+ centralSurfaces,
+ notificationShadeWindowController,
+ keyguardUnlockAnimationController,
+ ambientState
+ )
+ underTest.setupExpandedStatusBar()
+
+ interactionEventHandlerCaptor =
+ ArgumentCaptor.forClass(InteractionEventHandler::class.java)
+ verify(view).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
+ interactionEventHandler = interactionEventHandlerCaptor.value
+ }
+
+ // Note: So far, these tests only cover interactions with the status bar view controller. More
+ // tests need to be added to test the rest of handleDispatchTouchEvent.
+
+ @Test
+ fun handleDispatchTouchEvent_nullStatusBarViewController_returnsFalse() {
+ underTest.setStatusBarViewController(null)
+
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
+
+ assertThat(returnVal).isFalse()
+ }
+
+ @Test
+ fun handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSb() {
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ val ev = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
+ whenever(phoneStatusBarViewController.sendTouchToView(ev)).thenReturn(true)
+
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(ev)
+
+ verify(phoneStatusBarViewController).sendTouchToView(ev)
+ assertThat(returnVal).isTrue()
+ }
+
+ @Test
+ fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() {
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ val downEvBelow = MotionEvent.obtain(
+ 0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0
+ )
+ interactionEventHandler.handleDispatchTouchEvent(downEvBelow)
+
+ val nextEvent = MotionEvent.obtain(
+ 0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0
+ )
+ whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
+
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
+
+ verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
+ assertThat(returnVal).isTrue()
+ }
+
+ @Test
+ fun handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSb() {
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
+ whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
+ whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(true)
+ whenever(phoneStatusBarViewController.sendTouchToView(downEv)).thenReturn(true)
+
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
+
+ verify(phoneStatusBarViewController).sendTouchToView(downEv)
+ assertThat(returnVal).isTrue()
+ }
+
+ @Test
+ fun handleDispatchTouchEvent_panelNotCollapsed_returnsNull() {
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
+ whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(true)
+ // Item we're testing
+ whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(false)
+
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
+
+ verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
+ assertThat(returnVal).isNull()
+ }
+
+ @Test
+ fun handleDispatchTouchEvent_touchNotInSbBounds_returnsNull() {
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
+ whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
+ // Item we're testing
+ whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(false)
+
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
+
+ verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
+ assertThat(returnVal).isNull()
+ }
+
+ @Test
+ fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() {
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
+ whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(true)
+ // Item we're testing
+ whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false)
+
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
+
+ verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
+ assertThat(returnVal).isTrue()
+ }
+
+ @Test
+ fun handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSb() {
+ underTest.setStatusBarViewController(phoneStatusBarViewController)
+ whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
+ whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
+ whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
+ .thenReturn(true)
+
+ // Down event first
+ interactionEventHandler.handleDispatchTouchEvent(downEv)
+
+ // Then another event
+ val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
+ whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
+
+ val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
+
+ verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
+ assertThat(returnVal).isTrue()
+ }
+
+ @Test
+ fun testLowLightClockAttachedWhenExpandedStatusBarSetup() {
+ verify(lowLightClockController).attachLowLightClockView(ArgumentMatchers.any())
+ }
+
+ @Test
+ fun testLowLightClockShownWhenDozing() {
+ underTest.setDozing(true)
+ verify(lowLightClockController).showLowLightClock(true)
+ }
+
+ @Test
+ fun testLowLightClockDozeTimeTickCalled() {
+ underTest.dozeTimeTick()
+ verify(lowLightClockController).dozeTimeTick()
+ }
+
+ @Test
+ fun testLowLightClockHiddenWhenNotDozing() {
+ underTest.setDozing(true)
+ verify(lowLightClockController).showLowLightClock(true)
+ underTest.setDozing(false)
+ verify(lowLightClockController).showLowLightClock(false)
+ }
+}
+
+private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+private const val VIEW_BOTTOM = 100
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index 1d86fb13a1f6..665d849e379e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.shade;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -47,6 +47,9 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.tuner.TunerService;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
new file mode 100644
index 000000000000..baaa447d53a3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
@@ -0,0 +1,151 @@
+package com.android.systemui.shade.transition
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent
+import com.android.systemui.statusbar.phone.panelstate.STATE_CLOSED
+import com.android.systemui.statusbar.phone.panelstate.STATE_OPEN
+import com.android.systemui.statusbar.phone.panelstate.STATE_OPENING
+import com.android.systemui.statusbar.policy.FakeConfigurationController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ScrimShadeTransitionControllerTest : SysuiTestCase() {
+
+ @Mock private lateinit var scrimController: ScrimController
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
+ private val configurationController = FakeConfigurationController()
+
+ private lateinit var controller: ScrimShadeTransitionController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ context.ensureTestableResources()
+ controller =
+ ScrimShadeTransitionController(
+ configurationController,
+ dumpManager,
+ scrimController,
+ context.resources,
+ statusBarStateController)
+
+ controller.onPanelStateChanged(STATE_OPENING)
+ }
+
+ @Test
+ fun onPanelExpansionChanged_inSingleShade_setsFractionEqualToEventFraction() {
+ setSplitShadeEnabled(false)
+
+ controller.onPanelExpansionChanged(EXPANSION_EVENT)
+
+ verify(scrimController).setRawPanelExpansionFraction(EXPANSION_EVENT.fraction)
+ }
+
+ @Test
+ fun onPanelExpansionChanged_inSplitShade_unlockedShade_setsFractionBasedOnDragDownAmount() {
+ whenever(statusBarStateController.currentOrUpcomingState).thenReturn(StatusBarState.SHADE)
+ val scrimShadeTransitionDistance =
+ context.resources.getDimensionPixelSize(R.dimen.split_shade_scrim_transition_distance)
+ setSplitShadeEnabled(true)
+
+ controller.onPanelExpansionChanged(EXPANSION_EVENT)
+
+ val expectedFraction = EXPANSION_EVENT.dragDownPxAmount / scrimShadeTransitionDistance
+ verify(scrimController).setRawPanelExpansionFraction(expectedFraction)
+ }
+
+ @Test
+ fun onPanelExpansionChanged_inSplitShade_largeDragDownAmount_fractionIsNotGreaterThan1() {
+ whenever(statusBarStateController.currentOrUpcomingState).thenReturn(StatusBarState.SHADE)
+ val scrimShadeTransitionDistance =
+ context.resources.getDimensionPixelSize(R.dimen.split_shade_scrim_transition_distance)
+ setSplitShadeEnabled(true)
+
+ controller.onPanelExpansionChanged(
+ EXPANSION_EVENT.copy(dragDownPxAmount = 100f * scrimShadeTransitionDistance))
+
+ verify(scrimController).setRawPanelExpansionFraction(1f)
+ }
+
+ @Test
+ fun onPanelExpansionChanged_inSplitShade_negativeDragDownAmount_fractionIsNotLessThan0() {
+ whenever(statusBarStateController.currentOrUpcomingState).thenReturn(StatusBarState.SHADE)
+ setSplitShadeEnabled(true)
+
+ controller.onPanelExpansionChanged(EXPANSION_EVENT.copy(dragDownPxAmount = -100f))
+
+ verify(scrimController).setRawPanelExpansionFraction(0f)
+ }
+
+ @Test
+ fun onPanelExpansionChanged_inSplitShade_onLockedShade_setsFractionEqualToEventFraction() {
+ whenever(statusBarStateController.currentOrUpcomingState)
+ .thenReturn(StatusBarState.SHADE_LOCKED)
+ setSplitShadeEnabled(true)
+
+ controller.onPanelExpansionChanged(EXPANSION_EVENT)
+
+ verify(scrimController).setRawPanelExpansionFraction(EXPANSION_EVENT.fraction)
+ }
+
+ @Test
+ fun onPanelExpansionChanged_inSplitShade_onKeyguard_setsFractionEqualToEventFraction() {
+ whenever(statusBarStateController.currentOrUpcomingState)
+ .thenReturn(StatusBarState.KEYGUARD)
+ setSplitShadeEnabled(true)
+
+ controller.onPanelExpansionChanged(EXPANSION_EVENT)
+
+ verify(scrimController).setRawPanelExpansionFraction(EXPANSION_EVENT.fraction)
+ }
+
+ @Test
+ fun onPanelExpansionChanged_inSplitShade_panelOpen_setsFractionEqualToEventFraction() {
+ controller.onPanelStateChanged(STATE_OPEN)
+ whenever(statusBarStateController.currentOrUpcomingState)
+ .thenReturn(StatusBarState.KEYGUARD)
+ setSplitShadeEnabled(true)
+
+ controller.onPanelExpansionChanged(EXPANSION_EVENT)
+
+ verify(scrimController).setRawPanelExpansionFraction(EXPANSION_EVENT.fraction)
+ }
+
+ @Test
+ fun onPanelExpansionChanged_inSplitShade_panelClosed_setsFractionEqualToEventFraction() {
+ controller.onPanelStateChanged(STATE_CLOSED)
+ whenever(statusBarStateController.currentOrUpcomingState)
+ .thenReturn(StatusBarState.KEYGUARD)
+ setSplitShadeEnabled(true)
+
+ controller.onPanelExpansionChanged(EXPANSION_EVENT)
+
+ verify(scrimController).setRawPanelExpansionFraction(EXPANSION_EVENT.fraction)
+ }
+
+ private fun setSplitShadeEnabled(enabled: Boolean) {
+ overrideResource(R.bool.config_use_split_notification_shade, enabled)
+ configurationController.notifyConfigurationChanged()
+ }
+
+ companion object {
+ val EXPANSION_EVENT =
+ PanelExpansionChangeEvent(
+ fraction = 0.5f, expanded = true, tracking = true, dragDownPxAmount = 10f)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt
index 39d33e86925e..b6f8326394fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt
@@ -1,12 +1,16 @@
-package com.android.systemui.statusbar.phone.shade.transition
+package com.android.systemui.shade.transition
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
+import com.android.systemui.shade.NotificationPanelViewController
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
import com.android.systemui.statusbar.phone.panelstate.STATE_OPENING
import com.android.systemui.statusbar.policy.FakeConfigurationController
@@ -17,6 +21,7 @@ import org.mockito.Mock
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -28,6 +33,9 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
@Mock private lateinit var qs: QS
@Mock private lateinit var noOpOverScroller: NoOpOverScroller
@Mock private lateinit var splitShadeOverScroller: SplitShadeOverScroller
+ @Mock private lateinit var scrimShadeTransitionController: ScrimShadeTransitionController
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
private lateinit var controller: ShadeTransitionController
@@ -42,9 +50,13 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
ShadeTransitionController(
configurationController,
panelExpansionStateManager,
+ dumpManager,
context,
splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
- noOpOverScroller)
+ noOpOverScroller,
+ scrimShadeTransitionController,
+ statusBarStateController,
+ )
// Resetting as they are notified upon initialization.
reset(noOpOverScroller, splitShadeOverScroller)
@@ -74,6 +86,45 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
}
@Test
+ fun onPanelStateChanged_inSplitShade_onKeyguard_forwardsToNoOpOverScroller() {
+ initLateProperties()
+ enableSplitShade()
+ setOnKeyguard()
+
+ startPanelExpansion()
+
+ verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING)
+ verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
+ verifyZeroInteractions(splitShadeOverScroller)
+ }
+
+ @Test
+ fun onPanelStateChanged_inSplitShade_onLockedShade_forwardsToNoOpOverScroller() {
+ initLateProperties()
+ enableSplitShade()
+ setOnLockedShade()
+
+ startPanelExpansion()
+
+ verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING)
+ verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
+ verifyZeroInteractions(splitShadeOverScroller)
+ }
+
+ @Test
+ fun onPanelExpansionChanged_inSplitShade_onUnlockedShade_forwardsToSplitShadeOverScroller() {
+ initLateProperties()
+ enableSplitShade()
+ setOnUnlockedShade()
+
+ startPanelExpansion()
+
+ verify(splitShadeOverScroller).onPanelStateChanged(STATE_OPENING)
+ verify(splitShadeOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
+ verifyZeroInteractions(noOpOverScroller)
+ }
+
+ @Test
fun onPanelStateChanged_notInSplitShade_forwardsToNoOpOverScroller() {
initLateProperties()
disableSplitShade()
@@ -85,6 +136,16 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
verifyZeroInteractions(splitShadeOverScroller)
}
+ @Test
+ fun onPanelStateChanged_forwardsToScrimTransitionController() {
+ initLateProperties()
+
+ startPanelExpansion()
+
+ verify(scrimShadeTransitionController).onPanelStateChanged(STATE_OPENING)
+ verify(scrimShadeTransitionController).onPanelExpansionChanged(DEFAULT_EXPANSION_EVENT)
+ }
+
private fun initLateProperties() {
controller.qs = qs
controller.notificationStackScrollLayoutController = nsslController
@@ -106,13 +167,37 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
private fun startPanelExpansion() {
panelExpansionStateManager.onPanelExpansionChanged(
- fraction = 0.5f,
- expanded = true,
- tracking = true,
- dragDownPxAmount = DEFAULT_DRAG_DOWN_AMOUNT)
+ DEFAULT_EXPANSION_EVENT.fraction,
+ DEFAULT_EXPANSION_EVENT.expanded,
+ DEFAULT_EXPANSION_EVENT.tracking,
+ DEFAULT_EXPANSION_EVENT.dragDownPxAmount,
+ )
+ }
+
+ private fun setOnKeyguard() {
+ setShadeState(StatusBarState.KEYGUARD)
+ }
+
+ private fun setOnLockedShade() {
+ setShadeState(StatusBarState.SHADE_LOCKED)
+ }
+
+ private fun setOnUnlockedShade() {
+ setShadeState(StatusBarState.SHADE)
+ }
+
+ private fun setShadeState(state: Int) {
+ whenever(statusBarStateController.state).thenReturn(state)
+ whenever(statusBarStateController.currentOrUpcomingState).thenReturn(state)
}
companion object {
private const val DEFAULT_DRAG_DOWN_AMOUNT = 123f
+ private val DEFAULT_EXPANSION_EVENT =
+ PanelExpansionChangeEvent(
+ fraction = 0.5f,
+ expanded = true,
+ tracking = true,
+ dragDownPxAmount = DEFAULT_DRAG_DOWN_AMOUNT)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/SplitShadeOverScrollerTest.kt
index 219737d1dfb4..aafd871f72f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/SplitShadeOverScrollerTest.kt
@@ -1,6 +1,5 @@
-package com.android.systemui.statusbar.phone.shade.transition
+package com.android.systemui.shade.transition
-import org.mockito.Mockito.`when` as whenever
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
@@ -21,6 +20,7 @@ import org.mockito.Mock
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -43,7 +43,12 @@ class SplitShadeOverScrollerTest : SysuiTestCase() {
whenever(nsslController.height).thenReturn(1000)
overScroller =
SplitShadeOverScroller(
- configurationController, dumpManager, context, scrimController, qs, nsslController)
+ configurationController,
+ dumpManager,
+ context,
+ scrimController,
+ { qs },
+ { nsslController })
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
index 32314159f865..a4a89a4c67f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
@@ -1,3 +1,17 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.shared.animation
import android.testing.AndroidTestingRunner
@@ -7,31 +21,24 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.TestUnfoldTransitionProvider
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() {
- @Mock private lateinit var progressProvider: UnfoldTransitionProgressProvider
+ private val progressProvider = TestUnfoldTransitionProvider()
@Mock private lateinit var parent: ViewGroup
- @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener>
-
private lateinit var animator: UnfoldConstantTranslateAnimator
- private lateinit var progressListener: TransitionProgressListener
private val viewsIdToRegister =
setOf(
@@ -46,17 +53,14 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() {
UnfoldConstantTranslateAnimator(viewsIdToRegister, progressProvider)
animator.init(parent, MAX_TRANSLATION)
-
- verify(progressProvider).addCallback(progressListenerCaptor.capture())
- progressListener = progressListenerCaptor.value
}
@Test
fun onTransition_noMatchingIds() {
// GIVEN no views matching any ids
// WHEN the transition starts
- progressListener.onTransitionStarted()
- progressListener.onTransitionProgress(.1f)
+ progressProvider.onTransitionStarted()
+ progressProvider.onTransitionProgress(.1f)
// THEN nothing... no exceptions
}
@@ -86,22 +90,22 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() {
// Compare values as ints because -0f != 0f
// WHEN the transition starts
- progressListener.onTransitionStarted()
- progressListener.onTransitionProgress(0f)
+ progressProvider.onTransitionStarted()
+ progressProvider.onTransitionProgress(0f)
list.forEach { (view, direction) ->
assertEquals((-MAX_TRANSLATION * direction).toInt(), view.translationX.toInt())
}
// WHEN the transition progresses, translation is updated
- progressListener.onTransitionProgress(.5f)
+ progressProvider.onTransitionProgress(.5f)
list.forEach { (view, direction) ->
assertEquals((-MAX_TRANSLATION / 2f * direction).toInt(), view.translationX.toInt())
}
// WHEN the transition ends, translation is completed
- progressListener.onTransitionProgress(1f)
- progressListener.onTransitionFinished()
+ progressProvider.onTransitionProgress(1f)
+ progressProvider.onTransitionFinished()
list.forEach { (view, _) -> assertEquals(0, view.translationX.toInt()) }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
new file mode 100644
index 000000000000..aaa2357a1c52
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.shared.clocks
+
+import org.mockito.Mockito.`when` as whenever
+import android.content.Context
+import android.content.ContentResolver
+import android.graphics.drawable.Drawable
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.PluginListener
+import com.android.systemui.shared.plugins.PluginManager
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.eq
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.fail
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ClockRegistryTest : SysuiTestCase() {
+
+ @JvmField @Rule val mockito = MockitoJUnit.rule()
+ @Mock private lateinit var mockContext: Context
+ @Mock private lateinit var mockPluginManager: PluginManager
+ @Mock private lateinit var mockClock: Clock
+ @Mock private lateinit var mockThumbnail: Drawable
+ @Mock private lateinit var mockHandler: Handler
+ @Mock private lateinit var mockContentResolver: ContentResolver
+ private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
+ private lateinit var registry: ClockRegistry
+
+ private var settingValue: String = ""
+
+ companion object {
+ private fun failFactory(): Clock {
+ fail("Unexpected call to createClock")
+ return null!!
+ }
+
+ private fun failThumbnail(): Drawable? {
+ fail("Unexpected call to getThumbnail")
+ return null
+ }
+ }
+
+ private class FakeClockPlugin : ClockProviderPlugin {
+ private val metadata = mutableListOf<ClockMetadata>()
+ private val createCallbacks = mutableMapOf<ClockId, () -> Clock>()
+ private val thumbnailCallbacks = mutableMapOf<ClockId, () -> Drawable?>()
+
+ override fun getClocks() = metadata
+ override fun createClock(id: ClockId): Clock = createCallbacks[id]!!()
+ override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!()
+
+ fun addClock(
+ id: ClockId,
+ name: String,
+ create: () -> Clock = ::failFactory,
+ getThumbnail: () -> Drawable? = ::failThumbnail
+ ) {
+ metadata.add(ClockMetadata(id, name))
+ createCallbacks[id] = create
+ thumbnailCallbacks[id] = getThumbnail
+ }
+ }
+
+ @Before
+ fun setUp() {
+ whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
+
+ val captor = argumentCaptor<PluginListener<ClockProviderPlugin>>()
+ registry = object : ClockRegistry(mockContext, mockPluginManager, mockHandler) {
+ override var currentClockId: ClockId
+ get() = settingValue
+ set(value) { settingValue = value }
+ }
+ verify(mockPluginManager).addPluginListener(captor.capture(),
+ eq(ClockProviderPlugin::class.java))
+ pluginListener = captor.value
+ }
+
+ @Test
+ fun pluginRegistration_CorrectState() {
+ val plugin1 = FakeClockPlugin()
+ plugin1.addClock("clock_1", "clock 1")
+ plugin1.addClock("clock_2", "clock 2")
+
+ val plugin2 = FakeClockPlugin()
+ plugin2.addClock("clock_3", "clock 3")
+ plugin2.addClock("clock_4", "clock 4")
+
+ pluginListener.onPluginConnected(plugin1, mockContext)
+ pluginListener.onPluginConnected(plugin2, mockContext)
+ val list = registry.getClocks()
+ assertEquals(list, listOf(
+ ClockMetadata("clock_1", "clock 1"),
+ ClockMetadata("clock_2", "clock 2"),
+ ClockMetadata("clock_3", "clock 3"),
+ ClockMetadata("clock_4", "clock 4")
+ ))
+ }
+
+ @Test
+ fun clockIdConflict_ErrorWithoutCrash() {
+ val plugin1 = FakeClockPlugin()
+ plugin1.addClock("clock_1", "clock 1", { mockClock }, { mockThumbnail })
+ plugin1.addClock("clock_2", "clock 2", { mockClock }, { mockThumbnail })
+
+ val plugin2 = FakeClockPlugin()
+ plugin2.addClock("clock_1", "clock 1")
+ plugin2.addClock("clock_2", "clock 2")
+
+ pluginListener.onPluginConnected(plugin1, mockContext)
+ pluginListener.onPluginConnected(plugin2, mockContext)
+ val list = registry.getClocks()
+ assertEquals(list, listOf(
+ ClockMetadata("clock_1", "clock 1"),
+ ClockMetadata("clock_2", "clock 2")
+ ))
+
+ assertEquals(registry.createExampleClock("clock_1"), mockClock)
+ assertEquals(registry.createExampleClock("clock_2"), mockClock)
+ assertEquals(registry.getClockThumbnail("clock_1"), mockThumbnail)
+ assertEquals(registry.getClockThumbnail("clock_2"), mockThumbnail)
+ }
+
+ @Test
+ fun createCurrentClock_pluginConnected() {
+ val plugin1 = FakeClockPlugin()
+ plugin1.addClock("clock_1", "clock 1")
+ plugin1.addClock("clock_2", "clock 2")
+
+ settingValue = "clock_3"
+ val plugin2 = FakeClockPlugin()
+ plugin2.addClock("clock_3", "clock 3", { mockClock })
+ plugin2.addClock("clock_4", "clock 4")
+
+ pluginListener.onPluginConnected(plugin1, mockContext)
+ pluginListener.onPluginConnected(plugin2, mockContext)
+
+ val clock = registry.createCurrentClock()
+ assertEquals(clock, mockClock)
+ }
+
+ @Test
+ fun createDefaultClock_pluginDisconnected() {
+ val plugin1 = FakeClockPlugin()
+ plugin1.addClock(DEFAULT_CLOCK_ID, "default", { mockClock })
+ plugin1.addClock("clock_2", "clock 2")
+
+ settingValue = "clock_3"
+ val plugin2 = FakeClockPlugin()
+ plugin2.addClock("clock_3", "clock 3")
+ plugin2.addClock("clock_4", "clock 4")
+
+ pluginListener.onPluginConnected(plugin1, mockContext)
+ pluginListener.onPluginConnected(plugin2, mockContext)
+ pluginListener.onPluginDisconnected(plugin2)
+
+ val clock = registry.createCurrentClock()
+ assertEquals(clock, mockClock)
+ }
+
+ @Test
+ fun pluginRemoved_clockChanged() {
+ val plugin1 = FakeClockPlugin()
+ plugin1.addClock("clock_1", "clock 1")
+ plugin1.addClock("clock_2", "clock 2")
+
+ settingValue = "clock_3"
+ val plugin2 = FakeClockPlugin()
+ plugin2.addClock("clock_3", "clock 3", { mockClock })
+ plugin2.addClock("clock_4", "clock 4")
+
+ pluginListener.onPluginConnected(plugin1, mockContext)
+ pluginListener.onPluginConnected(plugin2, mockContext)
+
+ var changeCallCount = 0
+ registry.registerClockChangeListener({ changeCallCount++ })
+
+ pluginListener.onPluginDisconnected(plugin1)
+ assertEquals(0, changeCallCount)
+
+ pluginListener.onPluginDisconnected(plugin2)
+ assertEquals(1, changeCallCount)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
index 2cfe6be5c6b2..37f96c8d7023 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
@@ -26,8 +26,9 @@ import android.view.View
import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dreams.smartspace.DreamsSmartspaceController
+import com.android.systemui.dreams.smartspace.DreamSmartspaceController
import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.smartspace.dagger.SmartspaceViewComponent
import com.android.systemui.util.concurrency.Execution
@@ -35,16 +36,17 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import java.util.concurrent.Executor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.Optional
-import java.util.concurrent.Executor
+import org.mockito.Spy
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -74,8 +76,8 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
@Mock
private lateinit var precondition: SmartspacePrecondition
- @Mock
- private lateinit var smartspaceView: BcSmartspaceDataPlugin.SmartspaceView
+ @Spy
+ private var smartspaceView: SmartspaceView = TestView(context)
@Mock
private lateinit var listener: BcSmartspaceDataPlugin.SmartspaceTargetListener
@@ -83,6 +85,36 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
@Mock
private lateinit var session: SmartspaceSession
+ private lateinit var controller: DreamSmartspaceController
+
+ /**
+ * A class which implements SmartspaceView and extends View. This is mocked to provide the right
+ * object inheritance and interface implementation used in DreamSmartspaceController
+ */
+ private class TestView(context: Context?) : View(context), SmartspaceView {
+ override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {}
+
+ override fun setPrimaryTextColor(color: Int) {}
+
+ override fun setIsDreaming(isDreaming: Boolean) {}
+
+ override fun setDozeAmount(amount: Float) {}
+
+ override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {}
+
+ override fun setFalsingManager(falsingManager: FalsingManager?) {}
+
+ override fun setDnd(image: Drawable?, description: String?) {}
+
+ override fun setNextAlarm(image: Drawable?, description: String?) {}
+
+ override fun setMediaTarget(target: SmartspaceTarget?) {}
+
+ override fun getSelectedPage(): Int { return 0; }
+
+ override fun getCurrentCardTopPadding(): Int { return 0; }
+ }
+
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@@ -90,6 +122,9 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
.thenReturn(viewComponent)
`when`(viewComponent.getView()).thenReturn(smartspaceView)
`when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(session)
+
+ controller = DreamSmartspaceController(context, smartspaceManager, execution, uiExecutor,
+ viewComponentFactory, precondition, Optional.of(targetFilter), Optional.of(plugin))
}
/**
@@ -97,10 +132,6 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
*/
@Test
fun testConnectOnListen() {
- val controller = DreamsSmartspaceController(context,
- smartspaceManager, execution, uiExecutor, viewComponentFactory, precondition,
- Optional.of(targetFilter), Optional.of(plugin))
-
`when`(precondition.conditionsMet()).thenReturn(true)
controller.addListener(listener)
@@ -127,44 +158,10 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
}
/**
- * A class which implements SmartspaceView and extends View. This is mocked to provide the right
- * object inheritance and interface implementation used in DreamSmartspaceController
- */
- private class TestView(context: Context?) : View(context),
- BcSmartspaceDataPlugin.SmartspaceView {
- override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {}
-
- override fun setPrimaryTextColor(color: Int) {}
-
- override fun setIsDreaming(isDreaming: Boolean) {}
-
- override fun setDozeAmount(amount: Float) {}
-
- override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {}
-
- override fun setFalsingManager(falsingManager: FalsingManager?) {}
-
- override fun setDnd(image: Drawable?, description: String?) {}
-
- override fun setNextAlarm(image: Drawable?, description: String?) {}
-
- override fun setMediaTarget(target: SmartspaceTarget?) {}
-
- override fun getSelectedPage(): Int { return 0; }
-
- override fun getCurrentCardTopPadding(): Int { return 0; }
- }
-
- /**
* Ensures session begins when a view is attached.
*/
@Test
fun testConnectOnViewCreate() {
- val controller = DreamsSmartspaceController(context,
- smartspaceManager, execution, uiExecutor, viewComponentFactory, precondition,
- Optional.of(targetFilter),
- Optional.of(plugin))
-
`when`(precondition.conditionsMet()).thenReturn(true)
controller.buildAndConnectView(Mockito.mock(ViewGroup::class.java))
@@ -183,4 +180,4 @@ class DreamSmartspaceControllerTest : SysuiTestCase() {
verify(session).close()
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index fc4d9c42eb49..cf7f8dd26647 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -41,6 +41,7 @@ import android.view.WindowInsetsController.Behavior;
import androidx.test.filters.SmallTest;
+import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.SysuiTestCase;
@@ -53,6 +54,13 @@ import org.junit.Test;
@SmallTest
public class CommandQueueTest extends SysuiTestCase {
+ private static final LetterboxDetails[] TEST_LETTERBOX_DETAILS = new LetterboxDetails[] {
+ new LetterboxDetails(
+ /* letterboxInnerBounds= */ new Rect(100, 0, 200, 500),
+ /* letterboxFullBounds= */ new Rect(0, 0, 500, 100),
+ /* appAppearance= */ 123)
+ };
+
private CommandQueue mCommandQueue;
private Callbacks mCallbacks;
private static final int SECONDARY_DISPLAY = 1;
@@ -127,25 +135,27 @@ public class CommandQueueTest extends SysuiTestCase {
public void testOnSystemBarAttributesChanged() {
doTestOnSystemBarAttributesChanged(DEFAULT_DISPLAY, 1,
new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false,
- BEHAVIOR_DEFAULT, new InsetsVisibilities(), "test");
+ BEHAVIOR_DEFAULT, new InsetsVisibilities(), "test", TEST_LETTERBOX_DETAILS);
}
@Test
public void testOnSystemBarAttributesChangedForSecondaryDisplay() {
doTestOnSystemBarAttributesChanged(SECONDARY_DISPLAY, 1,
new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false,
- BEHAVIOR_DEFAULT, new InsetsVisibilities(), "test");
+ BEHAVIOR_DEFAULT, new InsetsVisibilities(), "test", TEST_LETTERBOX_DETAILS);
}
private void doTestOnSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName) {
+ @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName,
+ LetterboxDetails[] letterboxDetails) {
mCommandQueue.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
- navbarColorManagedByIme, behavior, requestedVisibilities, packageName);
+ navbarColorManagedByIme, behavior, requestedVisibilities, packageName,
+ letterboxDetails);
waitForIdleSync();
verify(mCallbacks).onSystemBarAttributesChanged(eq(displayId), eq(appearance),
eq(appearanceRegions), eq(navbarColorManagedByIme), eq(behavior),
- eq(requestedVisibilities), eq(packageName));
+ eq(requestedVisibilities), eq(packageName), eq(letterboxDetails));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index d67e26f138f2..9c25462b7c0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -30,6 +30,7 @@ import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewCont
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED;
+import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
import static com.google.common.truth.Truth.assertThat;
@@ -212,7 +213,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
R.string.do_financed_disclosure_with_name, ORGANIZATION_NAME);
when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
- when(mScreenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_ON);
+ when(mScreenLifecycle.getScreenState()).thenReturn(SCREEN_ON);
when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true);
when(mIndicationArea.findViewById(R.id.keyguard_indication_text_bottom))
@@ -954,64 +955,170 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
@Test
- public void nonBypassFaceSuccess_touchExplorationEnabled_showsSwipeToOpen() {
- // GIVEN non bypass face auth and touch exploration is enabled
- when(mKeyguardBypassController.canBypass()).thenReturn(false);
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
+ public void coEx_faceSuccess_showsPressToOpen() {
+ // GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, no a11y enabled
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(true);
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
+ when(mAccessibilityManager.isEnabled()).thenReturn(false);
+ when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+ createController();
+ mController.setVisible(true);
+
+ // WHEN face auth succeeds
+ when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
+ mController.getKeyguardCallback().onBiometricAuthenticated(0,
+ BiometricSourceType.FACE, false);
+
+ // THEN 'face unlocked. press unlock icon to open' message shows
+ String pressToOpen = mContext.getString(R.string.keyguard_face_successful_unlock_press);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, pressToOpen);
+
+ assertThat(mTextView.getText()).isNotEqualTo(pressToOpen);
+ }
+
+
+ @Test
+ public void coEx_faceSuccess_touchExplorationEnabled_showsFaceUnlockedSwipeToOpen() {
+ // GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, a11y enabled
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(true);
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
+ when(mAccessibilityManager.isEnabled()).thenReturn(true);
+ when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
createController();
- String swipeToOpen = mContext.getString(R.string.keyguard_unlock);
mController.setVisible(true);
// WHEN face authenticated
+ when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
mController.getKeyguardCallback().onBiometricAuthenticated(0,
BiometricSourceType.FACE, false);
- // THEN show 'swipe up to open' message
- verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen);
+ // THEN show 'face unlocked. swipe up to open' message
+ String faceUnlockedSwipeToOpen =
+ mContext.getString(R.string.keyguard_face_successful_unlock_swipe);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen);
}
@Test
- public void nonBypassFaceSuccess_a11yEnabled_showsSwipeToOpen() {
- // GIVEN non bypass face auth and a11y is enabled
- when(mKeyguardBypassController.canBypass()).thenReturn(false);
+ public void coEx_faceSuccess_a11yEnabled_showsFaceUnlockedSwipeToOpen() {
+ // GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, a11y is enabled
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(true);
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
when(mAccessibilityManager.isEnabled()).thenReturn(true);
+ createController();
+ mController.setVisible(true);
+
+ // WHEN face auth is successful
+ when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
+ mController.getKeyguardCallback().onBiometricAuthenticated(0,
+ BiometricSourceType.FACE, false);
+
+ // THEN show 'swipe up to open' message
+ String faceUnlockedSwipeToOpen =
+ mContext.getString(R.string.keyguard_face_successful_unlock_swipe);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen);
+ }
+
+ @Test
+ public void faceOnly_faceSuccess_showsFaceUnlockedSwipeToOpen() {
+ // GIVEN bouncer isn't showing, can skip bouncer, no udfps supported
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(true);
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
createController();
- String swipeToOpen = mContext.getString(R.string.keyguard_unlock);
mController.setVisible(true);
// WHEN face auth is successful
+ when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
mController.getKeyguardCallback().onBiometricAuthenticated(0,
BiometricSourceType.FACE, false);
// THEN show 'swipe up to open' message
+ String faceUnlockedSwipeToOpen =
+ mContext.getString(R.string.keyguard_face_successful_unlock_swipe);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen);
+ }
+
+ @Test
+ public void udfpsOnly_a11yEnabled_showsSwipeToOpen() {
+ // GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, a11y is enabled
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(true);
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
+ when(mAccessibilityManager.isEnabled()).thenReturn(true);
+ when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
+ createController();
+ mController.setVisible(true);
+
+ // WHEN showActionToUnlock
+ mController.showActionToUnlock();
+
+ // THEN show 'swipe up to open' message
+ String swipeToOpen = mContext.getString(R.string.keyguard_unlock);
verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen);
}
@Test
- public void coEx_nonBypassFaceSuccess_showsPressLockIcon() {
- // GIVEN udfps is supported, non-bypass face auth, and no a11y enabled
+ public void udfpsOnly_showsPressToOpen() {
+ // GIVEN bouncer isn't showing, udfps is supported, a11y is NOT enabled, can skip bouncer
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(true);
when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
- when(mKeyguardBypassController.canBypass()).thenReturn(false);
- when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
when(mAccessibilityManager.isEnabled()).thenReturn(false);
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+ createController();
+ mController.setVisible(true);
+
+ // WHEN showActionToUnlock
+ mController.showActionToUnlock();
+
+ // THEN show 'press unlock icon to open' message
+ String pressToOpen = mContext.getString(R.string.keyguard_unlock_press);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, pressToOpen);
+ }
+
+ @Test
+ public void canSkipBouncer_noSecurity_showSwipeToUnlockHint() {
+ // GIVEN bouncer isn't showing, can skip bouncer, no security (udfps isn't supported,
+ // face wasn't authenticated)
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
.thenReturn(true);
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
createController();
mController.setVisible(true);
- // WHEN face auth succeeds
- mController.getKeyguardCallback().onBiometricAuthenticated(0,
- BiometricSourceType.FACE, false);
+ // WHEN showActionToUnlock
+ mController.showActionToUnlock();
- // THEN press unlock icon to open message shows
- String pressLockIcon = mContext.getString(R.string.keyguard_face_successful_unlock_press);
- verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, pressLockIcon);
+ // THEN show 'swipe up to open' message
+ String swipeToOpen = mContext.getString(R.string.keyguard_unlock);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen);
+ }
- assertThat(mTextView.getText()).isNotEqualTo(pressLockIcon);
+ @Test
+ public void cannotSkipBouncer_showSwipeToUnlockHint() {
+ // GIVEN bouncer isn't showing and cannot skip bouncer
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(false);
+ createController();
+ mController.setVisible(true);
+
+ // WHEN showActionToUnlock
+ mController.showActionToUnlock();
+
+ // THEN show 'swipe up to open' message
+ String swipeToOpen = mContext.getString(R.string.keyguard_unlock);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen);
}
private void sendUpdateDisclosureBroadcast() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 562c97017862..fe1cd978ef91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -1,6 +1,5 @@
package com.android.systemui.statusbar
-import org.mockito.Mockito.`when` as whenever
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -14,6 +13,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.media.MediaHierarchyManager
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QS
+import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.stack.AmbientState
@@ -22,7 +22,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
-import com.android.systemui.statusbar.phone.NotificationPanelViewController
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.FakeConfigurationController
import org.junit.After
@@ -45,6 +44,7 @@ import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
private fun <T> anyObject(): T {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 2691ff98f4ee..34d13c76399a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -16,12 +16,9 @@
package com.android.systemui.statusbar;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
@@ -30,19 +27,14 @@ import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.NotificationRemoteInputManager.LegacyRemoteInputLifetimeExtender.RemoteInputActiveExtender;
-import com.android.systemui.statusbar.NotificationRemoteInputManager.LegacyRemoteInputLifetimeExtender.RemoteInputHistoryExtender;
-import com.android.systemui.statusbar.NotificationRemoteInputManager.LegacyRemoteInputLifetimeExtender.SmartReplyHistoryExtender;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -52,8 +44,6 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
-import com.google.android.collect.Sets;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -76,23 +66,15 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
@Mock private NotificationRemoteInputManager.Callback mCallback;
@Mock private RemoteInputController mController;
@Mock private SmartReplyController mSmartReplyController;
- @Mock private NotificationListenerService.RankingMap mRanking;
@Mock private ExpandableNotificationRow mRow;
@Mock private StatusBarStateController mStateController;
@Mock private RemoteInputUriController mRemoteInputUriController;
@Mock private NotificationClickNotifier mClickNotifier;
-
- // Dependency mocks:
@Mock private NotificationEntryManager mEntryManager;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
private TestableNotificationRemoteInputManager mRemoteInputManager;
private NotificationEntry mEntry;
- private RemoteInputHistoryExtender mRemoteInputHistoryExtender;
- private SmartReplyHistoryExtender mSmartReplyHistoryExtender;
- private RemoteInputActiveExtender mRemoteInputActiveExtender;
- private TestableNotificationRemoteInputManager.FakeLegacyRemoteInputLifetimeExtender
- mLegacyRemoteInputLifetimeExtender;
@Before
public void setUp() {
@@ -121,21 +103,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
.build();
mEntry.setRow(mRow);
- mRemoteInputManager.setUpWithPresenterForTest(mCallback,
- mDelegate, mController);
- for (NotificationLifetimeExtender extender : mRemoteInputManager.getLifetimeExtenders()) {
- extender.setCallback(
- mock(NotificationLifetimeExtender.NotificationSafeToRemoveCallback.class));
- }
- }
-
- @Test
- public void testPerformOnRemoveNotification() {
- when(mController.isRemoteInputActive(mEntry)).thenReturn(true);
- mRemoteInputManager.onPerformRemoveNotification(mEntry, mEntry.getKey());
-
- assertFalse(mEntry.mRemoteEditImeVisible);
- verify(mController).removeRemoteInput(mEntry, null);
+ mRemoteInputManager.setUpWithPresenterForTest(mCallback, mDelegate, mController);
}
@Test
@@ -143,7 +111,6 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
when(mController.isRemoteInputActive(mEntry)).thenReturn(true);
assertTrue(mRemoteInputManager.isRemoteInputActive(mEntry));
- assertTrue(mRemoteInputActiveExtender.shouldExtendLifetime(mEntry));
}
@Test
@@ -152,7 +119,6 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
when(mController.isSpinning(mEntry.getKey())).thenReturn(true);
assertTrue(mRemoteInputManager.shouldKeepForRemoteInputHistory(mEntry));
- assertTrue(mRemoteInputHistoryExtender.shouldExtendLifetime(mEntry));
}
@Test
@@ -161,7 +127,6 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime();
assertTrue(mRemoteInputManager.shouldKeepForRemoteInputHistory(mEntry));
- assertTrue(mRemoteInputHistoryExtender.shouldExtendLifetime(mEntry));
}
@Test
@@ -170,20 +135,6 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
when(mSmartReplyController.isSendingSmartReply(mEntry.getKey())).thenReturn(true);
assertTrue(mRemoteInputManager.shouldKeepForSmartReplyHistory(mEntry));
- assertTrue(mSmartReplyHistoryExtender.shouldExtendLifetime(mEntry));
- }
-
- @Test
- public void testNotificationWithRemoteInputActiveIsRemovedOnCollapse() {
- mRemoteInputActiveExtender.setShouldManageLifetime(mEntry, true /* shouldManage */);
-
- assertEquals(mLegacyRemoteInputLifetimeExtender.getEntriesKeptForRemoteInputActive(),
- Sets.newArraySet(mEntry));
-
- mRemoteInputManager.onPanelCollapsed();
-
- assertTrue(
- mLegacyRemoteInputLifetimeExtender.getEntriesKeptForRemoteInputActive().isEmpty());
}
private class TestableNotificationRemoteInputManager extends NotificationRemoteInputManager {
@@ -227,28 +178,5 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
mRemoteInputController = controller;
}
- @NonNull
- @Override
- protected LegacyRemoteInputLifetimeExtender createLegacyRemoteInputLifetimeExtender(
- Handler mainHandler,
- NotificationEntryManager notificationEntryManager,
- SmartReplyController smartReplyController) {
- mLegacyRemoteInputLifetimeExtender = new FakeLegacyRemoteInputLifetimeExtender();
- return mLegacyRemoteInputLifetimeExtender;
- }
-
- class FakeLegacyRemoteInputLifetimeExtender extends LegacyRemoteInputLifetimeExtender {
-
- @Override
- protected void addLifetimeExtenders() {
- mRemoteInputActiveExtender = new RemoteInputActiveExtender();
- mRemoteInputHistoryExtender = new RemoteInputHistoryExtender();
- mSmartReplyHistoryExtender = new SmartReplyHistoryExtender();
- mLifetimeExtenders.add(mRemoteInputHistoryExtender);
- mLifetimeExtenders.add(mSmartReplyHistoryExtender);
- mLifetimeExtenders.add(mRemoteInputActiveExtender);
- }
- }
-
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 3500e4d5f701..c75aa81e4c43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -17,15 +17,15 @@ package com.android.systemui.statusbar;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.Notification;
import android.os.Handler;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
@@ -39,14 +39,12 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.coordinator.RemoteInputCoordinator;
+import com.android.systemui.statusbar.notification.collection.notifcollection.InternalNotifUpdater;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
-import com.android.systemui.statusbar.policy.RemoteInputUriController;
import org.junit.Before;
import org.junit.Test;
@@ -54,8 +52,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Optional;
-
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -67,57 +63,50 @@ public class SmartReplyControllerTest extends SysuiTestCase {
private static final int TEST_CHOICE_COUNT = 4;
private static final int TEST_ACTION_COUNT = 3;
- private Notification mNotification;
private NotificationEntry mEntry;
private SmartReplyController mSmartReplyController;
- private NotificationRemoteInputManager mRemoteInputManager;
@Mock private NotificationVisibilityProvider mVisibilityProvider;
- @Mock private RemoteInputController.Delegate mDelegate;
- @Mock private NotificationRemoteInputManager.Callback mCallback;
@Mock private StatusBarNotification mSbn;
- @Mock private NotificationEntryManager mNotificationEntryManager;
@Mock private IStatusBarService mIStatusBarService;
- @Mock private StatusBarStateController mStatusBarStateController;
- @Mock private RemoteInputUriController mRemoteInputUriController;
@Mock private NotificationClickNotifier mClickNotifier;
+ @Mock private InternalNotifUpdater mInternalNotifUpdater;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mDependency.injectTestDependency(NotificationEntryManager.class,
- mNotificationEntryManager);
mSmartReplyController = new SmartReplyController(
mock(DumpManager.class),
mVisibilityProvider,
mIStatusBarService,
mClickNotifier);
- mDependency.injectTestDependency(SmartReplyController.class,
+ RemoteInputCoordinator remoteInputCoordinator = new RemoteInputCoordinator(
+ mock(DumpManager.class),
+ new RemoteInputNotificationRebuilder(mContext),
+ mock(NotificationRemoteInputManager.class),
+ mock(Handler.class),
mSmartReplyController);
+ remoteInputCoordinator.setRemoteInputController(mock(RemoteInputController.class));
+ NotifPipeline notifPipeline = mock(NotifPipeline.class);
+ when(notifPipeline.getInternalNotifUpdater(anyString())).thenReturn(mInternalNotifUpdater);
+ remoteInputCoordinator.attach(notifPipeline);
- mRemoteInputManager = new NotificationRemoteInputManager(mContext,
- mock(NotifPipelineFlags.class),
- mock(NotificationLockscreenUserManager.class),
- mSmartReplyController,
- mVisibilityProvider,
- mNotificationEntryManager,
- new RemoteInputNotificationRebuilder(mContext),
- () -> Optional.of(mock(CentralSurfaces.class)),
- mStatusBarStateController,
- Handler.createAsync(Looper.myLooper()),
- mRemoteInputUriController,
- mClickNotifier,
- mock(ActionClickLogger.class),
- mock(DumpManager.class));
- mRemoteInputManager.setUpWithCallback(mCallback, mDelegate);
- mNotification = new Notification.Builder(mContext, "")
+ Notification notification = new Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text").build();
-
- mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
- 0, mNotification, new UserHandle(ActivityManager.getCurrentUser()), null, 0);
+ mSbn = new StatusBarNotification(
+ TEST_PACKAGE_NAME,
+ TEST_PACKAGE_NAME,
+ 0,
+ null,
+ TEST_UID,
+ 0,
+ notification,
+ new UserHandle(ActivityManager.getCurrentUser()),
+ null,
+ 0);
mEntry = new NotificationEntryBuilder()
.setSbn(mSbn)
.build();
@@ -128,10 +117,9 @@ public class SmartReplyControllerTest extends SysuiTestCase {
mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT,
MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */);
- // Sending smart reply should make calls to NotificationEntryManager
- // to update the notification with reply and spinner.
- verify(mNotificationEntryManager).updateNotification(
- argThat(sbn -> sbn.getKey().equals(mSbn.getKey())), isNull());
+ // Sending smart reply should update the notification with reply and spinner.
+ verify(mInternalNotifUpdater).onInternalNotificationUpdate(
+ argThat(sbn -> sbn.getKey().equals(mSbn.getKey())), anyString());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
index 9d5099cfafd0..81d5c4d52b74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
@@ -1,10 +1,10 @@
package com.android.systemui.statusbar
-import org.mockito.Mockito.`when` as whenever
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.ScrimController
@@ -19,6 +19,7 @@ import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -32,6 +33,7 @@ class SplitShadeLockScreenOverScrollerTest : SysuiTestCase() {
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var qS: QS
@Mock private lateinit var nsslController: NotificationStackScrollLayoutController
+ @Mock private lateinit var dumpManager: DumpManager
private lateinit var overScroller: SplitShadeLockScreenOverScroller
@@ -44,11 +46,12 @@ class SplitShadeLockScreenOverScrollerTest : SysuiTestCase() {
overScroller =
SplitShadeLockScreenOverScroller(
configurationController,
+ dumpManager,
context,
scrimController,
statusBarStateController,
- qS,
- nsslController)
+ { qS },
+ { nsslController })
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index e01ebbdda374..0d1879cb2593 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -72,6 +72,7 @@ import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.log.LogBuffer;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -244,7 +245,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
mWifiStatusTrackerFactory,
mMainHandler,
mFeatureFlags,
- mock(DumpManager.class)
+ mock(DumpManager.class),
+ mock(LogBuffer.class)
);
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index 3a0c203f76e0..e3dd6f4e6e40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -38,6 +38,7 @@ import android.testing.TestableLooper.RunWithLooper;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.log.LogBuffer;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.CarrierConfigTracker;
@@ -125,16 +126,29 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
public void test4gDataIcon() {
// Switch to showing 4g icon and re-initialize the NetworkController.
mConfig.show4gForLte = true;
- mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm,
- mTelephonyListenerManager, mMockWm,
- mMockSm, mConfig, Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
+ mNetworkController = new NetworkControllerImpl(
+ mContext,
+ mMockCm,
+ mMockTm,
+ mTelephonyListenerManager,
+ mMockWm,
+ mMockSm,
+ mConfig,
+ Looper.getMainLooper(),
+ mFakeExecutor,
+ mCallbackHandler,
mock(AccessPointControllerImpl.class),
- mock(DataUsageController.class), mMockSubDefaults,
- mock(DeviceProvisionedController.class), mMockBd, mDemoModeController,
+ mock(DataUsageController.class),
+ mMockSubDefaults,
+ mock(DeviceProvisionedController.class),
+ mMockBd,
+ mDemoModeController,
mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
new Handler(TestableLooper.get(this).getLooper()),
- mFeatureFlags, mock(DumpManager.class));
+ mFeatureFlags,
+ mock(DumpManager.class),
+ mock(LogBuffer.class));
setupNetworkController();
setupDefaultSignal();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index ae1b3d1e1f42..698899a8fc36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -43,6 +43,7 @@ import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.log.LogBuffer;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.CarrierConfigTracker;
@@ -86,7 +87,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mWifiStatusTrackerFactory,
mMainHandler,
mFeatureFlags,
- mock(DumpManager.class)
+ mock(DumpManager.class),
+ mock(LogBuffer.class)
);
TestableLooper.get(this).processAllMessages();
@@ -100,7 +102,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
when(mMockProvisionController.isCurrentUserSetup()).thenReturn(true);
// WHEN - a NetworkController is created
- mNetworkController = new NetworkControllerImpl(mContext,
+ mNetworkController = new NetworkControllerImpl(
+ mContext,
mMockCm,
mMockTm,
mTelephonyListenerManager,
@@ -120,8 +123,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mWifiStatusTrackerFactory,
mMainHandler,
mFeatureFlags,
- mock(DumpManager.class)
- );
+ mock(DumpManager.class),
+ mock(LogBuffer.class));
TestableLooper.get(this).processAllMessages();
// THEN - NetworkController claims the user is not setup
@@ -133,15 +136,29 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
// Turn off mobile network support.
when(mMockTm.isDataCapable()).thenReturn(false);
// Create a new NetworkController as this is currently handled in constructor.
- mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm,
- mTelephonyListenerManager, mMockWm, mMockSm, mConfig,
- Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
- mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
- mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
- mDemoModeController, mock(CarrierConfigTracker.class),
+ mNetworkController = new NetworkControllerImpl(
+ mContext,
+ mMockCm,
+ mMockTm,
+ mTelephonyListenerManager,
+ mMockWm,
+ mMockSm,
+ mConfig,
+ Looper.getMainLooper(),
+ mFakeExecutor,
+ mCallbackHandler,
+ mock(AccessPointControllerImpl.class),
+ mock(DataUsageController.class),
+ mMockSubDefaults,
+ mock(DeviceProvisionedController.class),
+ mMockBd,
+ mDemoModeController,
+ mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
- mMainHandler, mFeatureFlags,
- mock(DumpManager.class));
+ mMainHandler,
+ mFeatureFlags,
+ mock(DumpManager.class),
+ mock(LogBuffer.class));
setupNetworkController();
verifyLastMobileDataIndicators(false, -1, 0);
@@ -156,14 +173,29 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
when(mMockTm.getServiceState()).thenReturn(mServiceState);
when(mMockSm.getCompleteActiveSubscriptionInfoList()).thenReturn(Collections.emptyList());
- mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm,
- mTelephonyListenerManager, mMockWm, mMockSm, mConfig,
- Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
- mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
- mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
- mDemoModeController, mock(CarrierConfigTracker.class),
+ mNetworkController = new NetworkControllerImpl(
+ mContext,
+ mMockCm,
+ mMockTm,
+ mTelephonyListenerManager,
+ mMockWm,
+ mMockSm,
+ mConfig,
+ Looper.getMainLooper(),
+ mFakeExecutor,
+ mCallbackHandler,
+ mock(AccessPointControllerImpl.class),
+ mock(DataUsageController.class),
+ mMockSubDefaults,
+ mock(DeviceProvisionedController.class),
+ mMockBd,
+ mDemoModeController,
+ mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
- mMainHandler, mFeatureFlags, mock(DumpManager.class));
+ mMainHandler,
+ mFeatureFlags,
+ mock(DumpManager.class),
+ mock(LogBuffer.class));
mNetworkController.registerListeners();
// Wait for the main looper to execute the previous command
@@ -226,14 +258,29 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
// Turn off mobile network support.
when(mMockTm.isDataCapable()).thenReturn(false);
// Create a new NetworkController as this is currently handled in constructor.
- mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm,
- mTelephonyListenerManager, mMockWm, mMockSm, mConfig,
- Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
- mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
- mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
- mDemoModeController, mock(CarrierConfigTracker.class),
+ mNetworkController = new NetworkControllerImpl(
+ mContext,
+ mMockCm,
+ mMockTm,
+ mTelephonyListenerManager,
+ mMockWm,
+ mMockSm,
+ mConfig,
+ Looper.getMainLooper(),
+ mFakeExecutor,
+ mCallbackHandler,
+ mock(AccessPointControllerImpl.class),
+ mock(DataUsageController.class),
+ mMockSubDefaults,
+ mock(DeviceProvisionedController.class),
+ mMockBd,
+ mDemoModeController,
+ mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
- mMainHandler, mFeatureFlags, mock(DumpManager.class));
+ mMainHandler,
+ mFeatureFlags,
+ mock(DumpManager.class),
+ mock(LogBuffer.class));
setupNetworkController();
// No Subscriptions.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index b44f53c67714..20747a05a360 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -33,6 +33,8 @@ import android.view.View
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
@@ -41,8 +43,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener
import com.android.systemui.settings.UserTracker
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -54,20 +55,20 @@ import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.time.FakeSystemClock
+import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.Optional
@SmallTest
class LockscreenSmartspaceControllerTest : SysuiTestCase() {
@@ -81,36 +82,56 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var falsingManager: FalsingManager
+
@Mock
private lateinit var secureSettings: SecureSettings
+
@Mock
private lateinit var userTracker: UserTracker
+
@Mock
private lateinit var contentResolver: ContentResolver
+
@Mock
private lateinit var configurationController: ConfigurationController
+
@Mock
private lateinit var statusBarStateController: StatusBarStateController
+
+ @Mock
+ private lateinit var keyguardBypassController: KeyguardBypassController
+
@Mock
private lateinit var deviceProvisionedController: DeviceProvisionedController
+
@Mock
private lateinit var handler: Handler
@Mock
private lateinit var plugin: BcSmartspaceDataPlugin
+
@Mock
private lateinit var controllerListener: SmartspaceTargetListener
@Captor
private lateinit var sessionListenerCaptor: ArgumentCaptor<OnTargetsAvailableListener>
+
@Captor
private lateinit var userTrackerCaptor: ArgumentCaptor<UserTracker.Callback>
+
@Captor
private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
+
@Captor
private lateinit var configChangeListenerCaptor: ArgumentCaptor<ConfigurationListener>
+
@Captor
private lateinit var statusBarStateListenerCaptor: ArgumentCaptor<StateListener>
+
+ @Captor
+ private lateinit var bypassStateChangedListenerCaptor:
+ ArgumentCaptor<KeyguardBypassController.OnBypassStateChangedListener>
+
@Captor
private lateinit var deviceProvisionedCaptor: ArgumentCaptor<DeviceProvisionedListener>
@@ -119,6 +140,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
private lateinit var settingsObserver: ContentObserver
private lateinit var configChangeListener: ConfigurationListener
private lateinit var statusBarStateListener: StateListener
+ private lateinit var bypassStateChangeListener:
+ KeyguardBypassController.OnBypassStateChangedListener
private lateinit var deviceProvisionedListener: DeviceProvisionedListener
private lateinit var smartspaceView: SmartspaceView
@@ -177,11 +200,12 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
configurationController,
statusBarStateController,
deviceProvisionedController,
+ keyguardBypassController,
execution,
executor,
handler,
Optional.of(plugin)
- )
+ )
verify(deviceProvisionedController).addCallback(capture(deviceProvisionedCaptor))
deviceProvisionedListener = deviceProvisionedCaptor.value
@@ -310,6 +334,19 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
}
@Test
+ fun testKeyguardBypassEnabledUpdatesView() {
+ // GIVEN a connected smartspace session
+ connectSession()
+ `when`(keyguardBypassController.bypassEnabled).thenReturn(true)
+
+ // WHEN the doze amount changes
+ bypassStateChangeListener.onBypassStateChanged(true)
+
+ // We pass that along to the view
+ verify(smartspaceView).setKeyguardBypassEnabled(true)
+ }
+
+ @Test
fun testSensitiveTargetsAreNotFilteredIfAllowed() {
// GIVEN the active and managed users allow sensitive content
connectSession()
@@ -457,6 +494,8 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
verify(contentResolver).unregisterContentObserver(settingsObserver)
verify(configurationController).removeCallback(configChangeListener)
verify(statusBarStateController).removeCallback(statusBarStateListener)
+ verify(keyguardBypassController)
+ .unregisterOnBypassStateChangedListener(bypassStateChangeListener)
}
@Test
@@ -478,6 +517,19 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
}
@Test
+ fun testViewGetInitializedWithBypassEnabledState() {
+ // GIVEN keyguard bypass is enabled.
+ `when`(keyguardBypassController.bypassEnabled).thenReturn(true)
+
+ // WHEN the view is being built
+ val view = controller.buildAndConnectView(fakeParent)
+ smartspaceView = view as SmartspaceView
+
+ // THEN the view is initialized with the keyguard bypass enabled state.
+ verify(smartspaceView).setKeyguardBypassEnabled(true)
+ }
+
+ @Test
fun testConnectAttemptBeforeInitializationShouldNotCreateSession() {
// GIVEN an uninitalized smartspaceView
// WHEN the device is provisioned
@@ -517,6 +569,9 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
verify(statusBarStateController).addCallback(statusBarStateListenerCaptor.capture())
statusBarStateListener = statusBarStateListenerCaptor.value
+ verify(keyguardBypassController)
+ .registerOnBypassStateChangedListener(capture(bypassStateChangedListenerCaptor))
+ bypassStateChangeListener = bypassStateChangedListenerCaptor.value
verify(smartspaceSession).requestSmartspaceUpdate()
clearInvocations(smartspaceSession)
@@ -585,6 +640,9 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
override fun setDozeAmount(amount: Float) {
}
+ override fun setKeyguardBypassEnabled(enabled: Boolean) {
+ }
+
override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
index 7d06abf5cd67..3fc0c8176f2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
@@ -63,7 +63,7 @@ public class DynamicPrivacyControllerTest extends SysuiTestCase {
mock(StatusBarKeyguardViewManager.class));
mDynamicPrivacyController.addListener(mListener);
// Disable dynamic privacy by default
- allowPrivateNotificationsInPublic(true);
+ allowNotificationsInPublic(false);
}
@Test
@@ -108,24 +108,21 @@ public class DynamicPrivacyControllerTest extends SysuiTestCase {
@Test
public void dynamicPrivacyOnlyWhenHidingPrivate() {
- // Verify that when only hiding notifications, this isn't enabled
- allowPrivateNotificationsInPublic(true);
- when(mLockScreenUserManager.shouldHideNotifications(any())).thenReturn(
- false);
- assertFalse("Dynamic privacy shouldn't be enabled when only hiding notifications",
+ // Verify that when hiding notifications, this isn't enabled
+ allowNotificationsInPublic(false);
+ assertFalse("Dynamic privacy shouldn't be enabled when hiding notifications",
mDynamicPrivacyController.isDynamicPrivacyEnabled());
- allowPrivateNotificationsInPublic(false);
- assertTrue("Should be enabled when hiding notification contents",
+ allowNotificationsInPublic(true);
+ assertTrue("Should be enabled whenever notifications are visible",
mDynamicPrivacyController.isDynamicPrivacyEnabled());
}
private void enableDynamicPrivacy() {
- allowPrivateNotificationsInPublic(false);
+ allowNotificationsInPublic(true);
}
- private void allowPrivateNotificationsInPublic(boolean allow) {
- when(mLockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn(
- allow);
+ private void allowNotificationsInPublic(boolean allow) {
+ when(mLockScreenUserManager.userAllowsNotificationsInPublic(anyInt())).thenReturn(allow);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 16b0376ba1f9..aeef6b04108e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -19,6 +19,8 @@ package com.android.systemui.statusbar.notification;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
+import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
@@ -41,6 +43,7 @@ import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
@@ -50,6 +53,7 @@ import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -91,7 +95,9 @@ import com.android.systemui.statusbar.notification.row.NotificationEntryManagerI
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.leak.LeakDetector;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -138,9 +144,14 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
@Mock private NotificationMediaManager mNotificationMediaManager;
@Mock private NotificationRowBinder mNotificationRowBinder;
@Mock private NotificationListener mNotificationListener;
+ @Mock private IStatusBarService mStatusBarService;
+
+ private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+ private FakeExecutor mBgExecutor = new FakeExecutor(mFakeSystemClock);
private int mId;
private NotificationEntry mEntry;
+ private DismissedByUserStats mStats;
private StatusBarNotification mSbn;
private NotificationEntryManager mEntryManager;
@@ -191,6 +202,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
Handler.createAsync(TestableLooper.get(this).getLooper()));
mEntry = createNotification();
+ mStats = defaultStats(mEntry);
mSbn = mEntry.getSbn();
when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false);
@@ -201,9 +213,10 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
() -> mNotificationRowBinder,
() -> mRemoteInputManager,
mLeakDetector,
- mock(IStatusBarService.class),
+ mStatusBarService,
NotifLiveDataStoreMocksKt.createNotifLiveDataStoreImplMock(),
- mock(DumpManager.class)
+ mock(DumpManager.class),
+ mBgExecutor
);
mEntryManager.initialize(
mNotificationListener,
@@ -316,6 +329,31 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
}
@Test
+ public void testPerformRemoveNotification_sendRemovalToServer() throws RemoteException {
+ // GIVEN an entry manager with a notification
+ mEntryManager.addActiveNotificationForTest(mEntry);
+
+ // GIVEN interceptor that doesn't intercept
+ when(mRemoveInterceptor.onNotificationRemoveRequested(
+ eq(mEntry.getKey()), argThat(matchEntryOnKey()), anyInt()))
+ .thenReturn(false);
+
+ // WHEN the notification entry is removed
+ mEntryManager.performRemoveNotification(mSbn, mStats, REASON_CANCEL);
+
+ // THEN notification removal is sent to the server
+ FakeExecutor.exhaustExecutors(mBgExecutor);
+ verify(mStatusBarService).onNotificationClear(
+ mSbn.getPackageName(),
+ mSbn.getUser().getIdentifier(),
+ mSbn.getKey(),
+ mStats.dismissalSurface,
+ mStats.dismissalSentiment,
+ mStats.notificationVisibility);
+ verifyNoMoreInteractions(mStatusBarService);
+ }
+
+ @Test
public void testRemoveNotification_onEntryRemoveNotFiredIfEntryDoesntExist() {
mEntryManager.removeNotification("not_a_real_key", mRankingMap, UNDEFINED_DISMISS_REASON);
@@ -573,23 +611,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
any(NotificationVisibility.class), anyBoolean(), eq(UNDEFINED_DISMISS_REASON));
}
- private NotificationEntry createNotification() {
- Notification.Builder n = new Notification.Builder(mContext, "id")
- .setSmallIcon(R.drawable.ic_person)
- .setContentTitle("Title")
- .setContentText("Text");
-
- return new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setId(mId++)
- .setNotification(n.build())
- .setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
- .setUser(new UserHandle(ActivityManager.getCurrentUser()))
- .build();
- }
-
/* Tests annexed from NotificationDataTest go here */
@Test
@@ -713,4 +734,28 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
return mManagedNotifs.contains(notificationKey);
}
}
+
+ private NotificationEntry createNotification() {
+ Notification.Builder n = new Notification.Builder(mContext, "id")
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text");
+
+ return new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setId(mId++)
+ .setNotification(n.build())
+ .setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT))
+ .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .build();
+ }
+
+ private static DismissedByUserStats defaultStats(NotificationEntry entry) {
+ return new DismissedByUserStats(
+ DISMISSAL_SHADE,
+ DISMISS_SENTIMENT_NEUTRAL,
+ NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
index 9f82a5673c6e..f4458bbdc497 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
@@ -6,11 +6,11 @@ import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
+import com.android.systemui.shade.NotificationShadeWindowViewController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
-import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController
import com.android.systemui.statusbar.policy.HeadsUpUtil
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
@@ -19,8 +19,8 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
@SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java
index 4507366e3073..ee7d55864931 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java
@@ -85,6 +85,11 @@ public class NoManSimulator {
mRankings.put(key, ranking);
}
+ /** This is for testing error cases: b/216384850 */
+ public Ranking removeRankingWithoutEvent(String key) {
+ return mRankings.remove(key);
+ }
+
private RankingMap buildRankingMap() {
return new RankingMap(mRankings.values().toArray(new Ranking[0]));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 958d54230f1c..4df99be9efd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -92,6 +92,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -146,14 +147,13 @@ public class NotifCollectionTest extends SysuiTestCase {
private NoManSimulator mNoMan;
private FakeSystemClock mClock = new FakeSystemClock();
+ private FakeExecutor mBgExecutor = new FakeExecutor(mClock);
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
allowTestableLooperAsMainThread();
- when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(true);
-
when(mEulogizer.record(any(Exception.class))).thenAnswer(i -> i.getArguments()[0]);
mListenerInOrder = inOrder(mCollectionListener);
@@ -164,6 +164,7 @@ public class NotifCollectionTest extends SysuiTestCase {
mNotifPipelineFlags,
mLogger,
mMainHandler,
+ mBgExecutor,
mEulogizer,
mock(DumpManager.class));
mCollection.attach(mGroupCoalescer);
@@ -463,6 +464,8 @@ public class NotifCollectionTest extends SysuiTestCase {
DismissedByUserStats stats = defaultStats(entry2);
mCollection.dismissNotification(entry2, defaultStats(entry2));
+ FakeExecutor.exhaustExecutors(mBgExecutor);
+
// THEN we send the dismissal to system server
verify(mStatusBarService).onNotificationClear(
notif2.sbn.getPackageName(),
@@ -676,6 +679,8 @@ public class NotifCollectionTest extends SysuiTestCase {
mInterceptor1.onEndInterceptionCallback.onEndDismissInterception(mInterceptor1, entry,
stats);
+ FakeExecutor.exhaustExecutors(mBgExecutor);
+
// THEN we send the dismissal to system server
verify(mStatusBarService).onNotificationClear(
eq(notif.sbn.getPackageName()),
@@ -1213,6 +1218,7 @@ public class NotifCollectionTest extends SysuiTestCase {
new Pair<>(entry2, defaultStats(entry2))));
// THEN we send the dismissals to system server
+ FakeExecutor.exhaustExecutors(mBgExecutor);
verify(mStatusBarService).onNotificationClear(
notif1.sbn.getPackageName(),
notif1.sbn.getUser().getIdentifier(),
@@ -1492,6 +1498,80 @@ public class NotifCollectionTest extends SysuiTestCase {
}
@Test
+ public void testMissingRankingWhenRemovalFeatureIsDisabled() {
+ // GIVEN a pipeline with one two notifications
+ when(mNotifPipelineFlags.removeUnrankedNotifs()).thenReturn(false);
+ String key1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 1, "myTag")).key;
+ String key2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 2, "myTag")).key;
+ NotificationEntry entry1 = mCollectionListener.getEntry(key1);
+ NotificationEntry entry2 = mCollectionListener.getEntry(key2);
+ clearInvocations(mCollectionListener);
+
+ // GIVEN the message for removing key1 gets does not reach NotifCollection
+ Ranking ranking1 = mNoMan.removeRankingWithoutEvent(key1);
+ // WHEN the message for removing key2 arrives
+ mNoMan.retractNotif(entry2.getSbn(), REASON_APP_CANCEL);
+
+ // THEN only entry2 gets removed
+ verify(mCollectionListener).onEntryRemoved(eq(entry2), eq(REASON_APP_CANCEL));
+ verify(mCollectionListener).onEntryCleanUp(eq(entry2));
+ verify(mCollectionListener).onRankingApplied();
+ verifyNoMoreInteractions(mCollectionListener);
+ verify(mLogger).logMissingRankings(eq(List.of(entry1)), eq(1), any());
+ verify(mLogger, never()).logRecoveredRankings(any());
+ clearInvocations(mCollectionListener, mLogger);
+
+ // WHEN a ranking update includes key1 again
+ mNoMan.setRanking(key1, ranking1);
+ mNoMan.issueRankingUpdate();
+
+ // VERIFY that we do nothing but log the 'recovery'
+ verify(mCollectionListener).onRankingUpdate(any());
+ verify(mCollectionListener).onRankingApplied();
+ verifyNoMoreInteractions(mCollectionListener);
+ verify(mLogger, never()).logMissingRankings(any(), anyInt(), any());
+ verify(mLogger).logRecoveredRankings(eq(List.of(key1)));
+ }
+
+ @Test
+ public void testMissingRankingWhenRemovalFeatureIsEnabled() {
+ // GIVEN a pipeline with one two notifications
+ when(mNotifPipelineFlags.removeUnrankedNotifs()).thenReturn(true);
+ String key1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 1, "myTag")).key;
+ String key2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 2, "myTag")).key;
+ NotificationEntry entry1 = mCollectionListener.getEntry(key1);
+ NotificationEntry entry2 = mCollectionListener.getEntry(key2);
+ clearInvocations(mCollectionListener);
+
+ // GIVEN the message for removing key1 gets does not reach NotifCollection
+ Ranking ranking1 = mNoMan.removeRankingWithoutEvent(key1);
+ // WHEN the message for removing key2 arrives
+ mNoMan.retractNotif(entry2.getSbn(), REASON_APP_CANCEL);
+
+ // THEN both entry1 and entry2 get removed
+ verify(mCollectionListener).onEntryRemoved(eq(entry2), eq(REASON_APP_CANCEL));
+ verify(mCollectionListener).onEntryRemoved(eq(entry1), eq(REASON_UNKNOWN));
+ verify(mCollectionListener).onEntryCleanUp(eq(entry2));
+ verify(mCollectionListener).onEntryCleanUp(eq(entry1));
+ verify(mCollectionListener).onRankingApplied();
+ verifyNoMoreInteractions(mCollectionListener);
+ verify(mLogger).logMissingRankings(eq(List.of(entry1)), eq(1), any());
+ verify(mLogger, never()).logRecoveredRankings(any());
+ clearInvocations(mCollectionListener, mLogger);
+
+ // WHEN a ranking update includes key1 again
+ mNoMan.setRanking(key1, ranking1);
+ mNoMan.issueRankingUpdate();
+
+ // VERIFY that we do nothing but log the 'recovery'
+ verify(mCollectionListener).onRankingUpdate(any());
+ verify(mCollectionListener).onRankingApplied();
+ verifyNoMoreInteractions(mCollectionListener);
+ verify(mLogger, never()).logMissingRankings(any(), anyInt(), any());
+ verify(mLogger).logRecoveredRankings(eq(List.of(key1)));
+ }
+
+ @Test
public void testRegisterFutureDismissal() throws RemoteException {
// GIVEN a pipeline with one notification
NotifEvent notifEvent = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
@@ -1505,6 +1585,7 @@ public class NotifCollectionTest extends SysuiTestCase {
// WHEN finally dismissing
onDismiss.run();
+ FakeExecutor.exhaustExecutors(mBgExecutor);
verify(mStatusBarService).onNotificationClear(any(), anyInt(), eq(notifEvent.key),
anyInt(), anyInt(), any());
verifyNoMoreInteractions(mStatusBarService);
@@ -1610,9 +1691,9 @@ public class NotifCollectionTest extends SysuiTestCase {
return new CollectionEvent(rawEvent, requireNonNull(mEntryCaptor.getValue()));
}
- private void verifyBuiltList(Collection<NotificationEntry> list) {
- verify(mBuildListener).onBuildList(mBuildListCaptor.capture());
- assertEquals(new ArraySet<>(list), new ArraySet<>(mBuildListCaptor.getValue()));
+ private void verifyBuiltList(Collection<NotificationEntry> expectedList) {
+ verify(mBuildListener).onBuildList(mBuildListCaptor.capture(), any());
+ assertThat(mBuildListCaptor.getValue()).containsExactly(expectedList.toArray());
}
private static class RecordingCollectionListener implements NotifCollectionListener {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 769143ddbc0d..d4add7547656 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -108,6 +108,7 @@ public class NotificationEntryTest extends SysuiTestCase {
@Test
public void testBlockableEntryWhenCritical() {
doReturn(true).when(mChannel).isBlockable();
+ mEntry.setRanking(mEntry.getRanking());
assertTrue(mEntry.isBlockable());
}
@@ -117,6 +118,7 @@ public class NotificationEntryTest extends SysuiTestCase {
public void testBlockableEntryWhenCriticalAndChannelNotBlockable() {
doReturn(true).when(mChannel).isBlockable();
doReturn(true).when(mChannel).isImportanceLockedByCriticalDeviceFunction();
+ mEntry.setRanking(mEntry.getRanking());
assertTrue(mEntry.isBlockable());
}
@@ -125,6 +127,7 @@ public class NotificationEntryTest extends SysuiTestCase {
public void testNonBlockableEntryWhenCriticalAndChannelNotBlockable() {
doReturn(false).when(mChannel).isBlockable();
doReturn(true).when(mChannel).isImportanceLockedByCriticalDeviceFunction();
+ mEntry.setRanking(mEntry.getRanking());
assertFalse(mEntry.isBlockable());
}
@@ -164,6 +167,9 @@ public class NotificationEntryTest extends SysuiTestCase {
doReturn(true).when(mChannel).isImportanceLockedByCriticalDeviceFunction();
doReturn(false).when(mChannel).isBlockable();
+ mEntry.setRanking(mEntry.getRanking());
+
+ assertFalse(mEntry.isBlockable());
assertTrue(mEntry.isExemptFromDndVisualSuppression());
assertFalse(mEntry.shouldSuppressAmbient());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 4e7e79f2cb26..dfa38abc1ff8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -53,6 +53,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.NotificationInteractionTracker;
+import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder.OnRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
@@ -569,7 +570,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
assertTrue(entry.hasFinishedInitialization());
// WHEN the pipeline is kicked off
- mReadyForBuildListener.onBuildList(singletonList(entry));
+ mReadyForBuildListener.onBuildList(singletonList(entry), "test");
mPipelineChoreographer.runIfScheduled();
// THEN the entry's initialization time is reset
@@ -1027,37 +1028,37 @@ public class ShadeListBuilderTest extends SysuiTestCase {
// WHEN each pluggable is invalidated THEN the list is re-rendered
clearInvocations(mOnRenderListListener);
- packageFilter.invalidateList();
+ packageFilter.invalidateList(null);
assertTrue(mPipelineChoreographer.isScheduled());
mPipelineChoreographer.runIfScheduled();
verify(mOnRenderListListener).onRenderList(anyList());
clearInvocations(mOnRenderListListener);
- idPromoter.invalidateList();
+ idPromoter.invalidateList(null);
assertTrue(mPipelineChoreographer.isScheduled());
mPipelineChoreographer.runIfScheduled();
verify(mOnRenderListListener).onRenderList(anyList());
clearInvocations(mOnRenderListListener);
- section.invalidateList();
+ section.invalidateList(null);
assertTrue(mPipelineChoreographer.isScheduled());
mPipelineChoreographer.runIfScheduled();
verify(mOnRenderListListener).onRenderList(anyList());
clearInvocations(mOnRenderListListener);
- hypeComparator.invalidateList();
+ hypeComparator.invalidateList(null);
assertTrue(mPipelineChoreographer.isScheduled());
mPipelineChoreographer.runIfScheduled();
verify(mOnRenderListListener).onRenderList(anyList());
clearInvocations(mOnRenderListListener);
- sectionComparator.invalidateList();
+ sectionComparator.invalidateList(null);
assertTrue(mPipelineChoreographer.isScheduled());
mPipelineChoreographer.runIfScheduled();
verify(mOnRenderListListener).onRenderList(anyList());
clearInvocations(mOnRenderListListener);
- preRenderInvalidator.invalidateList();
+ preRenderInvalidator.invalidateList(null);
assertTrue(mPipelineChoreographer.isScheduled());
mPipelineChoreographer.runIfScheduled();
verify(mOnRenderListListener).onRenderList(anyList());
@@ -1583,7 +1584,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
// WHEN visual stability manager allows group changes again
mStabilityManager.setAllowGroupChanges(true);
- mStabilityManager.invalidateList();
+ mStabilityManager.invalidateList(null);
mPipelineChoreographer.runIfScheduled();
// THEN entries are grouped
@@ -1622,7 +1623,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
// WHEN section changes are allowed again
mStabilityManager.setAllowSectionChanges(true);
- mStabilityManager.invalidateList();
+ mStabilityManager.invalidateList(null);
mPipelineChoreographer.runIfScheduled();
// THEN the section updates
@@ -1718,7 +1719,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
public void testOutOfOrderPreGroupFilterInvalidationThrows() {
// GIVEN a PreGroupNotifFilter that gets invalidated during the grouping stage
NotifFilter filter = new PackageFilter(PACKAGE_5);
- OnBeforeTransformGroupsListener listener = (list) -> filter.invalidateList();
+ OnBeforeTransformGroupsListener listener = (list) -> filter.invalidateList(null);
mListBuilder.addPreGroupFilter(filter);
mListBuilder.addOnBeforeTransformGroupsListener(listener);
@@ -1734,7 +1735,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
// GIVEN a NotifPromoter that gets invalidated during the sorting stage
NotifPromoter promoter = new IdPromoter(47);
OnBeforeSortListener listener =
- (list) -> promoter.invalidateList();
+ (list) -> promoter.invalidateList(null);
mListBuilder.addPromoter(promoter);
mListBuilder.addOnBeforeSortListener(listener);
@@ -1750,7 +1751,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
// GIVEN a NotifComparator that gets invalidated during the finalizing stage
NotifComparator comparator = new HypeComparator(PACKAGE_5);
OnBeforeRenderListListener listener =
- (list) -> comparator.invalidateList();
+ (list) -> comparator.invalidateList(null);
mListBuilder.setComparators(singletonList(comparator));
mListBuilder.addOnBeforeRenderListListener(listener);
@@ -1765,7 +1766,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
public void testOutOfOrderPreRenderFilterInvalidationThrows() {
// GIVEN a PreRenderNotifFilter that gets invalidated during the finalizing stage
NotifFilter filter = new PackageFilter(PACKAGE_5);
- OnBeforeRenderListListener listener = (list) -> filter.invalidateList();
+ OnBeforeRenderListListener listener = (list) -> filter.invalidateList(null);
mListBuilder.addFinalizeFilter(filter);
mListBuilder.addOnBeforeRenderListListener(listener);
@@ -1797,6 +1798,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
@Test
public void testStableMultipleSectionOrdering() {
+ // WHEN the list is originally built with reordering disabled
mListBuilder.setSectioners(asList(
new PackageSectioner(PACKAGE_1), new PackageSectioner(PACKAGE_2)));
mStabilityManager.setAllowEntryReordering(false);
@@ -1807,19 +1809,101 @@ public class ShadeListBuilderTest extends SysuiTestCase {
addNotif(3, PACKAGE_1).setRank(3);
dispatchBuild();
+ // VERIFY the order and that entry reordering has not been suppressed
verifyBuiltList(
notif(0),
notif(1),
notif(3),
notif(2)
);
+ verify(mStabilityManager, never()).onEntryReorderSuppressed();
+
+ // WHEN the ranks change
+ setNewRank(notif(0).entry, 4);
+ dispatchBuild();
+
+ // VERIFY the order does not change that entry reordering has been suppressed
+ verifyBuiltList(
+ notif(0),
+ notif(1),
+ notif(3),
+ notif(2)
+ );
+ verify(mStabilityManager).onEntryReorderSuppressed();
+
+ // WHEN reordering is now allowed again
+ mStabilityManager.setAllowEntryReordering(true);
+ dispatchBuild();
+
+ // VERIFY that list order changes
+ verifyBuiltList(
+ notif(1),
+ notif(3),
+ notif(0),
+ notif(2)
+ );
+ }
+
+ @Test
+ public void testStableChildOrdering() {
+ // WHEN the list is originally built with reordering disabled
+ mStabilityManager.setAllowEntryReordering(false);
+ addGroupSummary(0, PACKAGE_1, GROUP_1).setRank(0);
+ addGroupChild(1, PACKAGE_1, GROUP_1).setRank(1);
+ addGroupChild(2, PACKAGE_1, GROUP_1).setRank(2);
+ addGroupChild(3, PACKAGE_1, GROUP_1).setRank(3);
+ dispatchBuild();
+
+ // VERIFY the order and that entry reordering has not been suppressed
+ verifyBuiltList(
+ group(
+ summary(0),
+ child(1),
+ child(2),
+ child(3)
+ )
+ );
+ verify(mStabilityManager, never()).onEntryReorderSuppressed();
+
+ // WHEN the ranks change
+ setNewRank(notif(2).entry, 5);
+ dispatchBuild();
+
+ // VERIFY the order does not change that entry reordering has been suppressed
+ verifyBuiltList(
+ group(
+ summary(0),
+ child(1),
+ child(2),
+ child(3)
+ )
+ );
+ verify(mStabilityManager).onEntryReorderSuppressed();
+
+ // WHEN reordering is now allowed again
+ mStabilityManager.setAllowEntryReordering(true);
+ dispatchBuild();
+
+ // VERIFY that list order changes
+ verifyBuiltList(
+ group(
+ summary(0),
+ child(1),
+ child(3),
+ child(2)
+ )
+ );
+ }
+
+ private static void setNewRank(NotificationEntry entry, int rank) {
+ entry.setRanking(new RankingBuilder(entry.getRanking()).setRank(rank).build());
}
@Test
public void testInOrderPreRenderFilter() {
// GIVEN a PreRenderFilter that gets invalidated during the grouping stage
NotifFilter filter = new PackageFilter(PACKAGE_5);
- OnBeforeTransformGroupsListener listener = (list) -> filter.invalidateList();
+ OnBeforeTransformGroupsListener listener = (list) -> filter.invalidateList(null);
mListBuilder.addFinalizeFilter(filter);
mListBuilder.addOnBeforeTransformGroupsListener(listener);
@@ -1852,8 +1936,8 @@ public class ShadeListBuilderTest extends SysuiTestCase {
mListBuilder.addFinalizeFilter(filter2);
// WHEN both filters invalidate
- filter1.invalidateList();
- filter2.invalidateList();
+ filter1.invalidateList(null);
+ filter2.invalidateList(null);
// THEN the pipeline choreographer is scheduled to evaluate, AND the pipeline hasn't
// actually run.
@@ -2008,7 +2092,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
mPendingSet.clear();
}
- mReadyForBuildListener.onBuildList(mEntrySet);
+ mReadyForBuildListener.onBuildList(mEntrySet, "test");
mPipelineChoreographer.runIfScheduled();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index 699f77f9b7bb..2ee31265ff7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -59,6 +59,7 @@ import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import java.util.ArrayList
+import java.util.function.Consumer
import org.mockito.Mockito.`when` as whenever
@SmallTest
@@ -75,6 +76,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
private lateinit var mBeforeFinalizeFilterListener: OnBeforeFinalizeFilterListener
private lateinit var mOnHeadsUpChangedListener: OnHeadsUpChangedListener
private lateinit var mNotifSectioner: NotifSectioner
+ private lateinit var mActionPressListener: Consumer<NotificationEntry>
private val mNotifPipeline: NotifPipeline = mock()
private val mLogger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true)
@@ -131,6 +133,9 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
mOnHeadsUpChangedListener = withArgCaptor {
verify(mHeadsUpManager).addListener(capture())
}
+ mActionPressListener = withArgCaptor {
+ verify(mRemoteInputManager).addActionPressListener(capture())
+ }
given(mHeadsUpManager.allEntries).willAnswer { mHuns.stream() }
given(mHeadsUpManager.isAlerting(anyString())).willAnswer { invocation ->
val key = invocation.getArgument<String>(0)
@@ -199,6 +204,19 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
}
@Test
+ fun hunExtensionCancelledWhenHunActionPressed() {
+ whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true)
+ addHUN(mEntry)
+ whenever(mHeadsUpManager.canRemoveImmediately(anyString())).thenReturn(false)
+ whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
+ assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0))
+ mActionPressListener.accept(mEntry)
+ mExecutor.advanceClockToLast()
+ mExecutor.runAllReady()
+ verify(mHeadsUpManager, times(1)).removeNotification(eq(mEntry.key), eq(true))
+ }
+
+ @Test
fun testCancelUpdatedStickyNotification() {
whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true)
addHUN(mEntry)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
index d21053bcd691..27542a462d36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
@@ -18,7 +18,9 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -51,7 +53,6 @@ public class HideNotifsForOtherUsersCoordinatorTest extends SysuiTestCase {
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private NotifPipeline mNotifPipeline;
@Mock private PluggableListener<NotifFilter> mInvalidationListener;
- @Mock private SharedCoordinatorLogger mLogger;
@Captor private ArgumentCaptor<UserChangedListener> mUserChangedListenerCaptor;
@Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
@@ -66,7 +67,7 @@ public class HideNotifsForOtherUsersCoordinatorTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
HideNotifsForOtherUsersCoordinator coordinator =
- new HideNotifsForOtherUsersCoordinator(mLockscreenUserManager, mLogger);
+ new HideNotifsForOtherUsersCoordinator(mLockscreenUserManager);
coordinator.attach(mNotifPipeline);
verify(mLockscreenUserManager).addUserChangedListener(mUserChangedListenerCaptor.capture());
@@ -102,6 +103,6 @@ public class HideNotifsForOtherUsersCoordinatorTest extends SysuiTestCase {
mCapturedUserChangeListener.onCurrentProfilesChanged(new SparseArray<>());
// THEN the filter is invalidated
- verify(mInvalidationListener).onPluggableInvalidated(mCapturedNotifFilter);
+ verify(mInvalidationListener).onPluggableInvalidated(eq(mCapturedNotifFilter), any());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
deleted file mode 100644
index d082d74078db..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.notification.collection.coordinator;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.os.Handler;
-import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
-import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
-import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * TODO(b/224771204) Create test cases
- */
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@Ignore
-public class KeyguardCoordinatorTest extends SysuiTestCase {
- private static final int NOTIF_USER_ID = 0;
- private static final int CURR_USER_ID = 1;
-
- @Mock private Handler mMainHandler;
- @Mock private KeyguardStateController mKeyguardStateController;
- @Mock private BroadcastDispatcher mBroadcastDispatcher;
- @Mock private StatusBarStateController mStatusBarStateController;
- @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock private HighPriorityProvider mHighPriorityProvider;
- @Mock private SectionHeaderVisibilityProvider mSectionHeaderVisibilityProvider;
- @Mock private NotifPipeline mNotifPipeline;
- @Mock private KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
-
- private NotificationEntry mEntry;
- private NotifFilter mKeyguardFilter;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- KeyguardCoordinator keyguardCoordinator = new KeyguardCoordinator(
- mStatusBarStateController,
- mKeyguardUpdateMonitor, mHighPriorityProvider, mSectionHeaderVisibilityProvider,
- mKeyguardNotificationVisibilityProvider, mock(SharedCoordinatorLogger.class));
-
- mEntry = new NotificationEntryBuilder()
- .setUser(new UserHandle(NOTIF_USER_ID))
- .build();
-
- ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
- keyguardCoordinator.attach(mNotifPipeline);
- verify(mNotifPipeline, times(1)).addFinalizeFilter(filterCaptor.capture());
- mKeyguardFilter = filterCaptor.getValue();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
new file mode 100644
index 000000000000..7e2e6f619946
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.notification.collection.coordinator
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
+import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
+import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.withArgCaptor
+import java.util.function.Consumer
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class KeyguardCoordinatorTest : SysuiTestCase() {
+ private val notifPipeline: NotifPipeline = mock()
+ private val keyguardNotifVisibilityProvider: KeyguardNotificationVisibilityProvider = mock()
+ private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider = mock()
+ private val statusBarStateController: StatusBarStateController = mock()
+
+ private lateinit var onStateChangeListener: Consumer<String>
+ private lateinit var keyguardFilter: NotifFilter
+
+ @Before
+ fun setup() {
+ val keyguardCoordinator = KeyguardCoordinator(
+ keyguardNotifVisibilityProvider,
+ sectionHeaderVisibilityProvider,
+ statusBarStateController
+ )
+ keyguardCoordinator.attach(notifPipeline)
+ onStateChangeListener = withArgCaptor {
+ verify(keyguardNotifVisibilityProvider).addOnStateChangedListener(capture())
+ }
+ keyguardFilter = withArgCaptor {
+ verify(notifPipeline).addFinalizeFilter(capture())
+ }
+ }
+
+ @Test
+ fun testSetSectionHeadersVisibleInShade() {
+ clearInvocations(sectionHeaderVisibilityProvider)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+ onStateChangeListener.accept("state change")
+ verify(sectionHeaderVisibilityProvider).sectionHeadersVisible = eq(true)
+ }
+
+ @Test
+ fun testSetSectionHeadersNotVisibleOnKeyguard() {
+ clearInvocations(sectionHeaderVisibilityProvider)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ onStateChangeListener.accept("state change")
+ verify(sectionHeaderVisibilityProvider).sectionHeadersVisible = eq(false)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index d327be4c436a..f4adf6927e31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.when;
import static java.util.Objects.requireNonNull;
+import android.os.Handler;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -42,7 +43,6 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.RankingBuilder;
-import com.android.systemui.statusbar.notification.SectionClassifier;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
import com.android.systemui.statusbar.notification.collection.ListEntry;
@@ -57,8 +57,10 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider;
import com.android.systemui.statusbar.notification.collection.render.NotifViewBarn;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
+import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
import org.junit.Test;
@@ -97,8 +99,10 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
@Mock private IStatusBarService mService;
@Mock private BindEventManagerImpl mBindEventManagerImpl;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+ @Mock private Handler mHandler;
+ @Mock private SecureSettings mSecureSettings;
@Spy private FakeNotifInflater mNotifInflater = new FakeNotifInflater();
- private final SectionClassifier mSectionClassifier = new SectionClassifier();
+ private final SectionStyleProvider mSectionStyleProvider = new SectionStyleProvider();
private NotifUiAdjustmentProvider mAdjustmentProvider;
@@ -110,8 +114,11 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mAdjustmentProvider =
- new NotifUiAdjustmentProvider(mLockscreenUserManager, mSectionClassifier);
+ mAdjustmentProvider = new NotifUiAdjustmentProvider(
+ mHandler,
+ mSecureSettings,
+ mLockscreenUserManager,
+ mSectionStyleProvider);
mEntry = getNotificationEntryBuilder().setParent(ROOT_ENTRY).build();
mInflationError = new Exception(TEST_MESSAGE);
mErrorManager = new NotifInflationErrorManager();
@@ -449,13 +456,15 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
mInflateCallbacks.put(entry, callback);
}
+
@Override
public void rebindViews(@NonNull NotificationEntry entry, @NonNull Params params,
@NonNull InflationCallback callback) {
}
@Override
- public void abortInflation(@NonNull NotificationEntry entry) {
+ public boolean abortInflation(@NonNull NotificationEntry entry) {
+ return false;
}
public InflationCallback getInflateCallback(NotificationEntry entry) {
@@ -465,6 +474,10 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
public void invokeInflateCallbackForEntry(NotificationEntry entry) {
getInflateCallback(entry).onInflationFinished(entry, entry.getRowController());
}
+
+ @Override
+ public void releaseViews(@NonNull NotificationEntry entry) {
+ }
}
private void fireAddEvents(List<? extends ListEntry> entries) {
@@ -490,7 +503,7 @@ public class PreparationCoordinatorTest extends SysuiTestCase {
private static final int TEST_MAX_GROUP_DELAY = 100;
private void setSectionIsLowPriority(boolean minimized) {
- mSectionClassifier.setMinimizedSections(minimized
+ mSectionStyleProvider.setMinimizedSections(minimized
? Collections.singleton(mNotifSection.getSectioner())
: Collections.emptyList());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index 15c1cb7b123a..50b3fc7e1c42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -42,7 +42,6 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
-import com.android.systemui.statusbar.notification.SectionClassifier;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -50,6 +49,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider;
import com.android.systemui.statusbar.notification.collection.render.NodeController;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
@@ -70,7 +70,7 @@ public class RankingCoordinatorTest extends SysuiTestCase {
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private HighPriorityProvider mHighPriorityProvider;
- @Mock private SectionClassifier mSectionClassifier;
+ @Mock private SectionStyleProvider mSectionStyleProvider;
@Mock private NotifPipeline mNotifPipeline;
@Mock private NodeController mAlertingHeaderController;
@Mock private NodeController mSilentNodeController;
@@ -94,7 +94,7 @@ public class RankingCoordinatorTest extends SysuiTestCase {
mRankingCoordinator = new RankingCoordinator(
mStatusBarStateController,
mHighPriorityProvider,
- mSectionClassifier,
+ mSectionStyleProvider,
mAlertingHeaderController,
mSilentHeaderController,
mSilentNodeController);
@@ -102,7 +102,7 @@ public class RankingCoordinatorTest extends SysuiTestCase {
mEntry.setRanking(getRankingForUnfilteredNotif().build());
mRankingCoordinator.attach(mNotifPipeline);
- verify(mSectionClassifier).setMinimizedSections(any());
+ verify(mSectionStyleProvider).setMinimizedSections(any());
verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
index 447ba1510e13..3f3de009fb04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
@@ -21,13 +21,13 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.AssistantFeedbackController
import com.android.systemui.statusbar.notification.FeedbackIcon
-import com.android.systemui.statusbar.notification.SectionClassifier
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
import com.android.systemui.statusbar.notification.collection.render.NotifRowController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -37,8 +37,8 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations.initMocks
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations.initMocks
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -53,7 +53,7 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() {
@Mock private lateinit var pipeline: NotifPipeline
@Mock private lateinit var assistantFeedbackController: AssistantFeedbackController
- @Mock private lateinit var sectionClassifier: SectionClassifier
+ @Mock private lateinit var sectionStyleProvider: SectionStyleProvider
@Mock private lateinit var section1: NotifSection
@Mock private lateinit var section2: NotifSection
@@ -66,7 +66,7 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() {
coordinator = RowAppearanceCoordinator(
mContext,
assistantFeedbackController,
- sectionClassifier
+ sectionStyleProvider
)
coordinator.attach(pipeline)
beforeRenderListListener = withArgCaptor {
@@ -82,8 +82,8 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() {
@Test
fun testSetSystemExpandedOnlyOnFirst() {
- whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(false)
- whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(false)
+ whenever(sectionStyleProvider.isMinimizedSection(eq(section1))).thenReturn(false)
+ whenever(sectionStyleProvider.isMinimizedSection(eq(section1))).thenReturn(false)
beforeRenderListListener.onBeforeRenderList(listOf(entry1, entry2))
afterRenderEntryListener.onAfterRenderEntry(entry1, controller1)
verify(controller1).setSystemExpanded(eq(true))
@@ -93,8 +93,8 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() {
@Test
fun testSetSystemExpandedNeverIfMinimized() {
- whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(true)
- whenever(sectionClassifier.isMinimizedSection(eq(section1))).thenReturn(true)
+ whenever(sectionStyleProvider.isMinimizedSection(eq(section1))).thenReturn(true)
+ whenever(sectionStyleProvider.isMinimizedSection(eq(section1))).thenReturn(true)
beforeRenderListListener.onBeforeRenderList(listOf(entry1, entry2))
afterRenderEntryListener.onAfterRenderEntry(entry1, controller1)
verify(controller1).setSystemExpanded(eq(false))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
index a2d8e3ddba24..27be4c802aec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
@@ -34,6 +34,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
import dagger.BindsInstance
@@ -79,7 +80,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() {
dynamicPrivacyListener.onDynamicPrivacyChanged()
- verify(invalidationListener).onPluggableInvalidated(invalidator)
+ verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any())
}
@Test
@@ -265,4 +266,4 @@ interface TestSensitiveContentCoordinatorComponent {
@BindsInstance keyguardStateController: KeyguardStateController
): TestSensitiveContentCoordinatorComponent
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
index fdff6e9a52f3..d4f05050b6dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
@@ -35,21 +35,23 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.time.FakeSystemClock
+import java.util.concurrent.TimeUnit
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyString
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.concurrent.TimeUnit
@SmallTest
class SmartspaceDedupingCoordinatorTest : SysuiTestCase() {
@@ -349,7 +351,7 @@ class SmartspaceDedupingCoordinatorTest : SysuiTestCase() {
// THEN the new pipeline is invalidated (but the old one isn't because it's not
// necessary) because the notif should no longer be filtered out
- verify(pluggableListener).onPluggableInvalidated(filter)
+ verify(pluggableListener).onPluggableInvalidated(eq(filter), any())
verify(notificationEntryManager, never()).updateNotifications(anyString())
assertFalse(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now))
}
@@ -387,7 +389,7 @@ class SmartspaceDedupingCoordinatorTest : SysuiTestCase() {
}
private fun verifyPipelinesInvalidated() {
- verify(pluggableListener).onPluggableInvalidated(filter)
+ verify(pluggableListener).onPluggableInvalidated(eq(filter), any())
verify(notificationEntryManager).updateNotifications(anyString())
}
@@ -396,7 +398,7 @@ class SmartspaceDedupingCoordinatorTest : SysuiTestCase() {
}
private fun verifyPipelinesNotInvalidated() {
- verify(pluggableListener, never()).onPluggableInvalidated(filter)
+ verify(pluggableListener, never()).onPluggableInvalidated(eq(filter), any())
verify(notificationEntryManager, never()).updateNotifications(anyString())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
new file mode 100644
index 000000000000..0830191fe035
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.notification.collection.coordinator
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.withArgCaptor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class ViewConfigCoordinatorTest : SysuiTestCase() {
+ private lateinit var coordinator: ViewConfigCoordinator
+
+ // Captured
+ private lateinit var userChangedListener: UserChangedListener
+ private lateinit var configurationListener: ConfigurationController.ConfigurationListener
+ private lateinit var keyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback
+
+ // Mocks
+ private val entry: NotificationEntry = mock()
+ private val row: ExpandableNotificationRow = mock()
+ private val pipeline: NotifPipeline = mock()
+ private val configurationController: ConfigurationController = mock()
+ private val lockscreenUserManager: NotificationLockscreenUserManager = mock()
+ private val gutsManager: NotificationGutsManager = mock()
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock()
+
+ @Before
+ fun setUp() {
+ whenever(pipeline.allNotifs).thenReturn(listOf(entry))
+ whenever(entry.row).thenReturn(row)
+ coordinator = ViewConfigCoordinator(
+ configurationController,
+ lockscreenUserManager,
+ gutsManager,
+ keyguardUpdateMonitor)
+ coordinator.attach(pipeline)
+ userChangedListener = withArgCaptor {
+ verify(lockscreenUserManager).addUserChangedListener(capture())
+ }
+ configurationListener = withArgCaptor {
+ verify(configurationController).addCallback(capture())
+ }
+ keyguardUpdateMonitorCallback = withArgCaptor {
+ verify(keyguardUpdateMonitor).registerCallback(capture())
+ }
+ }
+
+ @Test
+ fun uiModeChangePropagatesToRow() {
+ configurationListener.onUiModeChanged()
+ verify(entry).row
+ verify(row).onUiModeChanged()
+ verifyNoMoreInteractions(entry, row)
+ }
+
+ @Test
+ fun themeChangePropagatesToEntry() {
+ configurationListener.onThemeChanged()
+ verify(entry).onDensityOrFontScaleChanged()
+ verify(entry).areGutsExposed()
+ verifyNoMoreInteractions(entry, row)
+ }
+
+ @Test
+ fun densityChangePropagatesToEntry() {
+ configurationListener.onDensityOrFontScaleChanged()
+ verify(entry).onDensityOrFontScaleChanged()
+ verify(entry).areGutsExposed()
+ verifyNoMoreInteractions(entry, row)
+ }
+
+ @Test
+ fun switchingUserDefersChangesWithUserChangedEventAfter() {
+ // GIVEN switching users
+ keyguardUpdateMonitorCallback.onUserSwitching(10)
+
+ // WHEN configuration changes happen
+ configurationListener.onUiModeChanged()
+ configurationListener.onDensityOrFontScaleChanged()
+ configurationListener.onThemeChanged()
+
+ // VERIFY no changes are propagated
+ verifyNoMoreInteractions(entry, row)
+
+ // WHEN user switch completes
+ keyguardUpdateMonitorCallback.onUserSwitchComplete(10)
+
+ // VERIFY the changes propagate
+ verify(entry).row
+ verify(row).onUiModeChanged()
+ verify(entry).onDensityOrFontScaleChanged()
+ verify(entry).areGutsExposed()
+ verifyNoMoreInteractions(entry, row)
+ clearInvocations(entry, row)
+
+ // WHEN user change happens after the switching window
+ userChangedListener.onUserChanged(10)
+
+ // VERIFY user change itself does not re-trigger updates
+ verifyNoMoreInteractions(entry, row)
+ }
+
+ @Test
+ fun switchingUserDefersChangesWithUserChangedEventDuring() {
+ // GIVEN switching users
+ keyguardUpdateMonitorCallback.onUserSwitching(10)
+
+ // WHEN configuration changes happen
+ configurationListener.onUiModeChanged()
+ configurationListener.onDensityOrFontScaleChanged()
+ configurationListener.onThemeChanged()
+
+ // VERIFY no changes are propagated
+ verifyNoMoreInteractions(entry, row)
+
+ // WHEN user change happens during the switching window
+ userChangedListener.onUserChanged(10)
+
+ // VERIFY the changes propagate
+ verify(entry).row
+ verify(row).onUiModeChanged()
+ verify(entry).onDensityOrFontScaleChanged()
+ verify(entry).areGutsExposed()
+ verifyNoMoreInteractions(entry, row)
+ clearInvocations(entry, row)
+
+ // WHEN user switch completes
+ keyguardUpdateMonitorCallback.onUserSwitchComplete(10)
+
+ // VERIFY the switching window closing does not re-propagate
+ verifyNoMoreInteractions(entry, row)
+ }
+
+ @Test
+ fun switchingUserDefersChangesWithUserChangedEventBefore() {
+ // WHEN user change happens before configuration changes or switching window
+ userChangedListener.onUserChanged(10)
+
+ // VERIFY no changes happen
+ verifyNoMoreInteractions(entry, row)
+
+ // WHEN switching users then configuration changes happen
+ keyguardUpdateMonitorCallback.onUserSwitching(10)
+
+ configurationListener.onUiModeChanged()
+ configurationListener.onDensityOrFontScaleChanged()
+ configurationListener.onThemeChanged()
+
+ // VERIFY no changes happen
+ verifyNoMoreInteractions(entry, row)
+
+ // WHEN user switch completes
+ keyguardUpdateMonitorCallback.onUserSwitchComplete(10)
+
+ // VERIFY the changes propagate
+ verify(entry).row
+ verify(row).onUiModeChanged()
+ verify(entry).onDensityOrFontScaleChanged()
+ verify(entry).areGutsExposed()
+ verifyNoMoreInteractions(entry, row)
+ clearInvocations(entry, row)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index f3aa20ba4527..c961cec39208 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import static junit.framework.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
@@ -35,6 +36,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.NotifPanelEvents;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -43,7 +45,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
-import com.android.systemui.statusbar.phone.NotifPanelEvents;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -55,6 +56,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.verification.VerificationMode;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -130,13 +132,14 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
doAnswer(i -> {
mNotifStabilityManager.onBeginRun();
return null;
- }).when(mInvalidateListener).onPluggableInvalidated(eq(mNotifStabilityManager));
+ }).when(mInvalidateListener).onPluggableInvalidated(eq(mNotifStabilityManager), any());
}
@Test
public void testScreenOff_groupAndSectionChangesAllowed() {
// GIVEN screen is off, panel isn't expanded and device isn't pulsing
- setScreenOn(false);
+ setFullyDozed(true);
+ setSleepy(true);
setPanelExpanded(false);
setPulsing(false);
@@ -149,9 +152,42 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
}
@Test
+ public void testScreenTurningOff_groupAndSectionChangesNotAllowed() {
+ // GIVEN the screen is turning off (sleepy but partially dozed)
+ setFullyDozed(false);
+ setSleepy(true);
+ setPanelExpanded(true);
+ setPulsing(false);
+
+ // THEN group changes are NOT allowed
+ assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
+ assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
+
+ // THEN section changes are NOT allowed
+ assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
+ }
+
+ @Test
+ public void testScreenTurningOn_groupAndSectionChangesNotAllowed() {
+ // GIVEN the screen is turning on (still fully dozed, not sleepy)
+ setFullyDozed(true);
+ setSleepy(false);
+ setPanelExpanded(true);
+ setPulsing(false);
+
+ // THEN group changes are NOT allowed
+ assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
+ assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
+
+ // THEN section changes are NOT allowed
+ assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
+ }
+
+ @Test
public void testPanelNotExpanded_groupAndSectionChangesAllowed() {
// GIVEN screen is on but the panel isn't expanded and device isn't pulsing
- setScreenOn(true);
+ setFullyDozed(false);
+ setSleepy(false);
setPanelExpanded(false);
setPulsing(false);
@@ -166,7 +202,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
@Test
public void testPanelExpanded_groupAndSectionChangesNotAllowed() {
// GIVEN the panel true expanded and device isn't pulsing
- setScreenOn(true);
+ setFullyDozed(false);
+ setSleepy(false);
setPanelExpanded(true);
setPulsing(false);
@@ -181,7 +218,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
@Test
public void testPulsing_screenOff_groupAndSectionChangesNotAllowed() {
// GIVEN the device is pulsing and screen is off
- setScreenOn(false);
+ setFullyDozed(true);
+ setSleepy(true);
setPulsing(true);
// THEN group changes are NOT allowed
@@ -195,7 +233,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
@Test
public void testPulsing_panelNotExpanded_groupAndSectionChangesNotAllowed() {
// GIVEN the device is pulsing and screen is off with the panel not expanded
- setScreenOn(false);
+ setFullyDozed(true);
+ setSleepy(true);
setPanelExpanded(false);
setPulsing(true);
@@ -211,7 +250,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
public void testOverrideReorderingSuppression_onlySectionChangesAllowed() {
// GIVEN section changes typically wouldn't be allowed because the panel is expanded and
// we're not pulsing
- setScreenOn(true);
+ setFullyDozed(false);
+ setSleepy(false);
setPanelExpanded(true);
setPulsing(true);
@@ -233,7 +273,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
@Test
public void testTemporarilyAllowSectionChanges_callsInvalidate() {
// GIVEN section changes typically wouldn't be allowed because the panel is expanded
- setScreenOn(true);
+ setFullyDozed(false);
+ setSleepy(false);
setPanelExpanded(true);
setPulsing(false);
@@ -241,13 +282,14 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.uptimeMillis());
// THEN the notification list is invalidated
- verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+ verifyStabilityManagerWasInvalidated(times(1));
}
@Test
public void testTemporarilyAllowSectionChanges_noInvalidationCalled() {
// GIVEN section changes typically WOULD be allowed
- setScreenOn(false);
+ setFullyDozed(true);
+ setSleepy(true);
setPanelExpanded(false);
setPulsing(false);
@@ -255,13 +297,14 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
// THEN invalidate is not called because this entry was never suppressed from reordering
- verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
+ verifyStabilityManagerWasInvalidated(never());
}
@Test
public void testTemporarilyAllowSectionChangesTimeout() {
// GIVEN section changes typically WOULD be allowed
- setScreenOn(false);
+ setFullyDozed(true);
+ setSleepy(true);
setPanelExpanded(false);
setPulsing(false);
assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
@@ -271,7 +314,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
// THEN invalidate is not called because this entry was never suppressed from reordering;
// THEN section changes are allowed for this notification
- verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
+ verifyStabilityManagerWasInvalidated(never());
assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
// WHEN we're pulsing (now disallowing reordering)
@@ -292,20 +335,21 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
@Test
public void testTemporarilyAllowSectionChanges_isPulsingChangeBeforeTimeout() {
// GIVEN section changes typically wouldn't be allowed because the device is pulsing
- setScreenOn(false);
+ setFullyDozed(true);
+ setSleepy(true);
setPanelExpanded(false);
setPulsing(true);
// WHEN we temporarily allow section changes for this notification entry
mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
// can now reorder, so invalidates
- verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+ verifyStabilityManagerWasInvalidated(times(1));
// WHEN reordering is now allowed because device isn't pulsing anymore
setPulsing(false);
// THEN invalidate isn't called a second time since reordering was already allowed
- verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+ verifyStabilityManagerWasInvalidated(times(1));
}
@Test
@@ -315,22 +359,26 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
// WHEN device isn't pulsing anymore
setPulsing(false);
- // WHEN screen isn't on
- setScreenOn(false);
+ // WHEN fully dozed
+ setFullyDozed(true);
+
+ // WHEN sleepy
+ setSleepy(true);
// WHEN panel isn't expanded
setPanelExpanded(false);
// THEN we never see any calls to invalidate since there weren't any notifications that
// were being suppressed from grouping or section changes
- verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
+ verifyStabilityManagerWasInvalidated(never());
}
@Test
public void testNotSuppressingGroupChangesAnymore_invalidationCalled() {
// GIVEN visual stability is being maintained b/c panel is expanded
setPulsing(false);
- setScreenOn(true);
+ setFullyDozed(false);
+ setSleepy(false);
setPanelExpanded(true);
assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
@@ -340,7 +388,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
setPanelExpanded(false);
// invalidate is called because we were previously suppressing a group change
- verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+ verifyStabilityManagerWasInvalidated(times(1));
}
@Test
@@ -354,7 +402,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
setActivityLaunching(false);
// invalidate is called, b/c we were previously suppressing the pipeline from running
- verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+ verifyStabilityManagerWasInvalidated(times(1));
}
@Test
@@ -368,7 +416,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
setPanelCollapsing(false);
// invalidate is called, b/c we were previously suppressing the pipeline from running
- verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+ verifyStabilityManagerWasInvalidated(times(1));
}
@Test
@@ -380,7 +428,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
setPanelCollapsing(false);
// THEN invalidate is not called, b/c nothing has been suppressed
- verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
+ verifyStabilityManagerWasInvalidated(never());
}
@Test
@@ -392,14 +440,15 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
setActivityLaunching(false);
// THEN invalidate is not called, b/c nothing has been suppressed
- verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
+ verifyStabilityManagerWasInvalidated(never());
}
@Test
public void testNotSuppressingEntryReorderingAnymoreWillInvalidate() {
// GIVEN visual stability is being maintained b/c panel is expanded
setPulsing(false);
- setScreenOn(true);
+ setFullyDozed(false);
+ setSleepy(false);
setPanelExpanded(true);
assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
@@ -410,14 +459,15 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
setPanelExpanded(false);
// invalidate is called because we were previously suppressing an entry reorder
- verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+ verifyStabilityManagerWasInvalidated(times(1));
}
@Test
public void testQueryingEntryReorderingButNotReportingReorderSuppressedDoesNotInvalidate() {
// GIVEN visual stability is being maintained b/c panel is expanded
setPulsing(false);
- setScreenOn(true);
+ setFullyDozed(false);
+ setSleepy(false);
setPanelExpanded(true);
assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
@@ -426,13 +476,14 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
setPanelExpanded(false);
// invalidate is not called because we were not told that an entry reorder was suppressed
- verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
+ verifyStabilityManagerWasInvalidated(never());
}
@Test
public void testHeadsUp_allowedToChangeGroupAndSection() {
// GIVEN group + section changes disallowed
- setScreenOn(true);
+ setFullyDozed(false);
+ setSleepy(false);
setPanelExpanded(true);
setPulsing(true);
assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
@@ -450,6 +501,10 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
}
+ private void verifyStabilityManagerWasInvalidated(VerificationMode mode) {
+ verify(mInvalidateListener, mode).onPluggableInvalidated(eq(mNotifStabilityManager), any());
+ }
+
private void setActivityLaunching(boolean activityLaunching) {
mNotifPanelEventsCallback.onLaunchingActivityChanged(activityLaunching);
}
@@ -462,11 +517,16 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase {
mStatusBarStateListener.onPulsingChanged(pulsing);
}
- private void setScreenOn(boolean screenOn) {
- if (screenOn) {
- mWakefulnessObserver.onStartedWakingUp();
- } else {
+ private void setFullyDozed(boolean fullyDozed) {
+ float dozeAmount = fullyDozed ? 1 : 0;
+ mStatusBarStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
+ }
+
+ private void setSleepy(boolean sleepy) {
+ if (sleepy) {
mWakefulnessObserver.onFinishedGoingToSleep();
+ } else {
+ mWakefulnessObserver.onStartedWakingUp();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
index dd15caebf9e6..246943e3088e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
@@ -1,29 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.notification.collection.inflation
+import android.database.ContentObserver
+import android.os.Handler
+import android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationLockscreenUserManager
-import com.android.systemui.statusbar.notification.SectionClassifier
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
+import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.SecureSettings
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.inOrder
import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class NotifUiAdjustmentProviderTest : SysuiTestCase() {
private val lockscreenUserManager: NotificationLockscreenUserManager = mock()
- private val sectionClassifier: SectionClassifier = mock()
+ private val sectionStyleProvider: SectionStyleProvider = mock()
+ private val handler: Handler = mock()
+ private val secureSettings: SecureSettings = mock()
+ private val uri = FakeSettings().getUriFor(SHOW_NOTIFICATION_SNOOZE)
+ private val dirtyListener: Runnable = mock()
+
+ private val section = NotifSection(mock(), 0)
+ private val entry = NotificationEntryBuilder()
+ .setSection(section)
+ .setParent(GroupEntry.ROOT_ENTRY)
+ .build()
+
+ private lateinit var contentObserver: ContentObserver
private val adjustmentProvider = NotifUiAdjustmentProvider(
+ handler,
+ secureSettings,
lockscreenUserManager,
- sectionClassifier,
+ sectionStyleProvider,
)
+ @Before
+ fun setup() {
+ verifyNoMoreInteractions(secureSettings)
+ adjustmentProvider.addDirtyListener(dirtyListener)
+ verify(secureSettings).getInt(eq(SHOW_NOTIFICATION_SNOOZE), any())
+ contentObserver = withArgCaptor {
+ verify(secureSettings).registerContentObserverForUser(
+ eq(SHOW_NOTIFICATION_SNOOZE), capture(), any()
+ )
+ }
+ verifyNoMoreInteractions(secureSettings, dirtyListener)
+ }
+
@Test
fun notifLockscreenStateChangeWillNotifDirty() {
val dirtyListener = mock<Runnable>()
@@ -33,6 +91,35 @@ class NotifUiAdjustmentProviderTest : SysuiTestCase() {
verify(lockscreenUserManager).addNotificationStateChangedListener(capture())
}
notifLocksreenStateChangeListener.onNotificationStateChanged()
- verify(dirtyListener).run();
+ verify(dirtyListener).run()
+ }
+
+ @Test
+ fun additionalAddDoesNotRegisterAgain() {
+ clearInvocations(secureSettings)
+ adjustmentProvider.addDirtyListener(mock())
+ verifyNoMoreInteractions(secureSettings)
+ }
+
+ @Test
+ fun onChangeWillQueryThenNotifyDirty() {
+ contentObserver.onChange(false, listOf(uri), 0, 0)
+ with(inOrder(secureSettings, dirtyListener)) {
+ verify(secureSettings).getInt(eq(SHOW_NOTIFICATION_SNOOZE), any())
+ verify(dirtyListener).run()
+ }
+ }
+
+ @Test
+ fun changingSnoozeChangesProvidedAdjustment() {
+ whenever(secureSettings.getInt(eq(SHOW_NOTIFICATION_SNOOZE), any())).thenReturn(0)
+ val original = adjustmentProvider.calculateAdjustment(entry)
+ assertThat(original.isSnoozeEnabled).isFalse()
+
+ whenever(secureSettings.getInt(eq(SHOW_NOTIFICATION_SNOOZE), any())).thenReturn(1)
+ contentObserver.onChange(false, listOf(uri), 0, 0)
+ val withSnoozing = adjustmentProvider.calculateAdjustment(entry)
+ assertThat(withSnoozing.isSnoozeEnabled).isTrue()
+ assertThat(withSnoozing).isNotEqualTo(original)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLoggerTest.kt
new file mode 100644
index 000000000000..6c07174bce65
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLoggerTest.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.notification.collection.notifcollection
+
+import android.service.notification.NotificationListenerService.RankingMap
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotifCollectionLoggerTest : SysuiTestCase() {
+ private val logger: NotifCollectionLogger = mock()
+ private val entry1: NotificationEntry = NotificationEntryBuilder().setId(1).build()
+ private val entry2: NotificationEntry = NotificationEntryBuilder().setId(2).build()
+
+ private fun mapOfEntries(vararg entries: NotificationEntry): Map<String, NotificationEntry> =
+ entries.associateBy { it.key }
+
+ private fun rankingMapOf(vararg entries: NotificationEntry): RankingMap =
+ RankingMap(entries.map { it.ranking }.toTypedArray())
+
+ @Test
+ fun testMaybeLogInconsistentRankings_logsNewlyInconsistentRanking() {
+ val rankingMap = rankingMapOf(entry1)
+ maybeLogInconsistentRankings(
+ logger = logger,
+ oldKeysWithoutRankings = emptySet(),
+ newEntriesWithoutRankings = mapOfEntries(entry2),
+ rankingMap = rankingMap
+ )
+ verify(logger).logMissingRankings(
+ newlyInconsistentEntries = eq(listOf(entry2)),
+ totalInconsistent = eq(1),
+ rankingMap = eq(rankingMap),
+ )
+ verifyNoMoreInteractions(logger)
+ }
+
+ @Test
+ fun testMaybeLogInconsistentRankings_doesNotLogAlreadyInconsistentRanking() {
+ maybeLogInconsistentRankings(
+ logger = logger,
+ oldKeysWithoutRankings = setOf(entry2.key),
+ newEntriesWithoutRankings = mapOfEntries(entry2),
+ rankingMap = rankingMapOf(entry1)
+ )
+ verifyNoMoreInteractions(logger)
+ }
+
+ @Test
+ fun testMaybeLogInconsistentRankings_logsWhenRankingIsAdded() {
+ maybeLogInconsistentRankings(
+ logger = logger,
+ oldKeysWithoutRankings = setOf(entry2.key),
+ newEntriesWithoutRankings = mapOfEntries(),
+ rankingMap = rankingMapOf(entry1, entry2)
+ )
+ verify(logger).logRecoveredRankings(
+ newlyConsistentKeys = eq(listOf(entry2.key)),
+ )
+ verifyNoMoreInteractions(logger)
+ }
+
+ @Test
+ fun testMaybeLogInconsistentRankings_doesNotLogsWhenEntryIsRemoved() {
+ maybeLogInconsistentRankings(
+ logger = logger,
+ oldKeysWithoutRankings = setOf(entry2.key),
+ newEntriesWithoutRankings = mapOfEntries(),
+ rankingMap = rankingMapOf(entry1)
+ )
+ verifyNoMoreInteractions(logger)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index 0e1865861cae..ac254abe60b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.collection.render
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
-import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -28,6 +27,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.collection.getAttachState
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
@@ -413,4 +413,4 @@ private fun buildSection(
return nodeController
}
}, index)
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
index 0d5a5fe086a3..3f641df376ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
@@ -72,32 +72,32 @@ public class HeadsUpViewBinderTest extends SysuiTestCase {
});
mViewBinder.bindHeadsUpView(mEntry, null);
- verify(mLogger).startBindingHun(eq("key"));
+ verify(mLogger).startBindingHun(eq(mEntry));
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
callback.get().onBindFinished(mEntry);
- verify(mLogger).entryBoundSuccessfully(eq("key"));
+ verify(mLogger).entryBoundSuccessfully(eq(mEntry));
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
mViewBinder.bindHeadsUpView(mEntry, null);
- verify(mLogger).startBindingHun(eq("key"));
+ verify(mLogger).startBindingHun(eq(mEntry));
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
callback.get().onBindFinished(mEntry);
- verify(mLogger).entryBoundSuccessfully(eq("key"));
+ verify(mLogger).entryBoundSuccessfully(eq(mEntry));
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
mViewBinder.unbindHeadsUpView(mEntry);
- verify(mLogger).entryContentViewMarkedFreeable(eq("key"));
+ verify(mLogger).entryContentViewMarkedFreeable(eq(mEntry));
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
callback.get().onBindFinished(mEntry);
- verify(mLogger).entryUnbound(eq("key"));
+ verify(mLogger).entryUnbound(eq(mEntry));
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
}
@@ -111,12 +111,12 @@ public class HeadsUpViewBinderTest extends SysuiTestCase {
});
mViewBinder.bindHeadsUpView(mEntry, null);
- verify(mLogger).startBindingHun(eq("key"));
+ verify(mLogger).startBindingHun(eq(mEntry));
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
mViewBinder.abortBindCallback(mEntry);
- verify(mLogger).currentOngoingBindingAborted(eq("key"));
+ verify(mLogger).currentOngoingBindingAborted(eq(mEntry));
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
@@ -135,18 +135,18 @@ public class HeadsUpViewBinderTest extends SysuiTestCase {
});
mViewBinder.bindHeadsUpView(mEntry, null);
- verify(mLogger).startBindingHun(eq("key"));
+ verify(mLogger).startBindingHun(eq(mEntry));
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
mViewBinder.unbindHeadsUpView(mEntry);
- verify(mLogger).currentOngoingBindingAborted(eq("key"));
- verify(mLogger).entryContentViewMarkedFreeable(eq("key"));
+ verify(mLogger).currentOngoingBindingAborted(eq(mEntry));
+ verify(mLogger).entryContentViewMarkedFreeable(eq(mEntry));
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
callback.get().onBindFinished(mEntry);
- verify(mLogger).entryUnbound(eq("key"));
+ verify(mLogger).entryUnbound(eq(mEntry));
verifyNoMoreInteractions(mLogger);
clearInvocations(mLogger);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 90627cb0fa78..5386171eeda2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.interruption;
import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.Notification.GROUP_ALERT_SUMMARY;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
@@ -24,13 +25,16 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -58,6 +62,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -84,6 +89,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
@Mock
StatusBarStateController mStatusBarStateController;
@Mock
+ KeyguardStateController mKeyguardStateController;
+ @Mock
HeadsUpManager mHeadsUpManager;
@Mock
NotificationInterruptLogger mLogger;
@@ -95,12 +102,15 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
NotifPipelineFlags mFlags;
@Mock
KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
+ @Mock
+ PendingIntent mPendingIntent;
private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(false);
mNotifInterruptionStateProvider =
new NotificationInterruptStateProviderImpl(
@@ -111,6 +121,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
mNotificationFilter,
mBatteryController,
mStatusBarStateController,
+ mKeyguardStateController,
mHeadsUpManager,
mLogger,
mMockHandler,
@@ -196,7 +207,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
ensureStateForHeadsUpWhenAwake();
// WHEN this entry should be filtered out
- NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
+ NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
when(mNotificationFilter.shouldFilterOut(entry)).thenReturn(true);
// THEN we shouldn't heads up this entry
@@ -205,11 +216,9 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
@Test
public void testDoNotRunFilterOnNewPipeline() {
- when(mFlags.isNewPipelineEnabled()).thenReturn(true);
// WHEN this entry should be filtered out
- NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
+ NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
mNotifInterruptionStateProvider.shouldHeadsUp(entry);
- verify(mFlags, times(1)).isNewPipelineEnabled();
verify(mNotificationFilter, times(0)).shouldFilterOut(eq(entry));
}
@@ -325,7 +334,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
public void testShouldNotHeadsUp_filtered() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
// Make canAlertCommon false by saying it's filtered out
- when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true);
+ when(mKeyguardNotificationVisibilityProvider.shouldHideNotification(any()))
+ .thenReturn(true);
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
@@ -422,6 +432,224 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
+ @Test
+ public void testShouldNotFullScreen_notPendingIntent_withStrictFlag() throws Exception {
+ when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+ testShouldNotFullScreen_notPendingIntent();
+ }
+
+ @Test
+ public void testShouldNotFullScreen_notPendingIntent() throws RemoteException {
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.getState()).thenReturn(SHADE);
+
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isFalse();
+ verify(mLogger, never()).logNoFullscreen(any(), any());
+ verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+ verify(mLogger, never()).logFullscreen(any(), any());
+ }
+
+ @Test
+ public void testShouldNotFullScreen_notHighImportance_withStrictFlag() throws Exception {
+ when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+ testShouldNotFullScreen_notHighImportance();
+ }
+
+ @Test
+ public void testShouldNotFullScreen_notHighImportance() throws RemoteException {
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_DEFAULT, /* silenced */ false);
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.getState()).thenReturn(SHADE);
+
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isFalse();
+ verify(mLogger).logNoFullscreen(entry, "Not important enough");
+ verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+ verify(mLogger, never()).logFullscreen(any(), any());
+ }
+
+ @Test
+ public void testShouldNotFullScreen_isGroupAlertSilenced_withStrictFlag() throws Exception {
+ when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+ testShouldNotFullScreen_isGroupAlertSilenced();
+ }
+
+ @Test
+ public void testShouldNotFullScreen_isGroupAlertSilenced() throws RemoteException {
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ true);
+ when(mPowerManager.isInteractive()).thenReturn(false);
+ when(mDreamManager.isDreaming()).thenReturn(true);
+ when(mStatusBarStateController.getState()).thenReturn(KEYGUARD);
+
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isFalse();
+ verify(mLogger, never()).logNoFullscreen(any(), any());
+ verify(mLogger).logNoFullscreenWarning(entry, "GroupAlertBehavior will prevent HUN");
+ verify(mLogger, never()).logFullscreen(any(), any());
+ }
+
+ @Test
+ public void testShouldFullScreen_notInteractive_withStrictFlag() throws Exception {
+ when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+ testShouldFullScreen_notInteractive();
+ }
+
+ @Test
+ public void testShouldFullScreen_notInteractive() throws RemoteException {
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+ when(mPowerManager.isInteractive()).thenReturn(false);
+ when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.getState()).thenReturn(SHADE);
+
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isTrue();
+ verify(mLogger, never()).logNoFullscreen(any(), any());
+ verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+ verify(mLogger).logFullscreen(entry, "Device is not interactive");
+ }
+
+ @Test
+ public void testShouldFullScreen_isDreaming_withStrictFlag() throws Exception {
+ when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+ testShouldFullScreen_isDreaming();
+ }
+
+ @Test
+ public void testShouldFullScreen_isDreaming() throws RemoteException {
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ when(mDreamManager.isDreaming()).thenReturn(true);
+ when(mStatusBarStateController.getState()).thenReturn(SHADE);
+
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isTrue();
+ verify(mLogger, never()).logNoFullscreen(any(), any());
+ verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+ verify(mLogger).logFullscreen(entry, "Device is dreaming");
+ }
+
+ @Test
+ public void testShouldFullScreen_onKeyguard_withStrictFlag() throws Exception {
+ when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+ testShouldFullScreen_onKeyguard();
+ }
+
+ @Test
+ public void testShouldFullScreen_onKeyguard() throws RemoteException {
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.getState()).thenReturn(KEYGUARD);
+
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isTrue();
+ verify(mLogger, never()).logNoFullscreen(any(), any());
+ verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+ verify(mLogger).logFullscreen(entry, "Keyguard is showing");
+ }
+
+ @Test
+ public void testShouldNotFullScreen_willHun_withStrictFlag() throws Exception {
+ when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+ testShouldNotFullScreen_willHun();
+ }
+
+ @Test
+ public void testShouldNotFullScreen_willHun() throws RemoteException {
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ when(mPowerManager.isScreenOn()).thenReturn(true);
+ when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.getState()).thenReturn(SHADE);
+
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isFalse();
+ verify(mLogger).logNoFullscreen(entry, "Expected to HUN");
+ verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+ verify(mLogger, never()).logFullscreen(any(), any());
+ }
+
+ @Test
+ public void testShouldFullScreen_packageSnoozed() throws RemoteException {
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ when(mPowerManager.isScreenOn()).thenReturn(true);
+ when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.getState()).thenReturn(SHADE);
+ when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
+
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isTrue();
+ verify(mLogger).logNoHeadsUpPackageSnoozed(entry);
+ verify(mLogger, never()).logNoFullscreen(any(), any());
+ verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+ verify(mLogger).logFullscreen(entry, "Expected not to HUN");
+ }
+
+ @Test
+ public void testShouldFullScreen_snoozed_occluding_withStrictRules() throws Exception {
+ when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ when(mPowerManager.isScreenOn()).thenReturn(true);
+ when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.getState()).thenReturn(SHADE);
+ when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(true);
+
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isTrue();
+ verify(mLogger).logNoHeadsUpPackageSnoozed(entry);
+ verify(mLogger, never()).logNoFullscreen(any(), any());
+ verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+ verify(mLogger).logFullscreen(entry, "Expected not to HUN while keyguard occluded");
+ }
+
+ @Test
+ public void testShouldFullScreen_snoozed_lockedShade_withStrictRules() throws Exception {
+ when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ when(mPowerManager.isScreenOn()).thenReturn(true);
+ when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.getState()).thenReturn(SHADE_LOCKED);
+ when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isTrue();
+ verify(mLogger).logNoHeadsUpPackageSnoozed(entry);
+ verify(mLogger, never()).logNoFullscreen(any(), any());
+ verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+ verify(mLogger).logFullscreen(entry, "Keyguard is showing and not occluded");
+ }
+
+ @Test
+ public void testShouldNotFullScreen_snoozed_unlocked_withStrictRules() throws Exception {
+ when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+ NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ when(mPowerManager.isScreenOn()).thenReturn(true);
+ when(mDreamManager.isDreaming()).thenReturn(false);
+ when(mStatusBarStateController.getState()).thenReturn(SHADE);
+ when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isFalse();
+ verify(mLogger).logNoHeadsUpPackageSnoozed(entry);
+ verify(mLogger, never()).logNoFullscreen(any(), any());
+ verify(mLogger).logNoFullscreenWarning(entry, "Expected not to HUN while not on keyguard");
+ verify(mLogger, never()).logFullscreen(any(), any());
+ }
+
/**
* Bubbles can happen.
*/
@@ -432,6 +660,17 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
}
/**
+ * Test that notification can bubble even if it is a child in a group and group settings are
+ * set to alert only for summary notifications.
+ */
+ @Test
+ public void testShouldBubbleUp_notifInGroupWithOnlySummaryAlerts() {
+ ensureStateForBubbleUp();
+ NotificationEntry bubble = createBubble("testgroup", GROUP_ALERT_SUMMARY);
+ assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(bubble)).isTrue();
+ }
+
+ /**
* If the notification doesn't have permission to bubble, it shouldn't bubble.
*/
@Test
@@ -492,22 +731,34 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
ensureStateForBubbleUp();
// Make canAlertCommon false by saying it's filtered out
- when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true);
+ when(mKeyguardNotificationVisibilityProvider.shouldHideNotification(any()))
+ .thenReturn(true);
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isFalse();
}
private NotificationEntry createBubble() {
+ return createBubble(null, null);
+ }
+
+ private NotificationEntry createBubble(String groupKey, Integer groupAlert) {
Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder(
PendingIntent.getActivity(mContext, 0, new Intent(),
- PendingIntent.FLAG_MUTABLE),
- Icon.createWithResource(mContext.getResources(), R.drawable.android))
+ PendingIntent.FLAG_MUTABLE),
+ Icon.createWithResource(mContext.getResources(), R.drawable.android))
.build();
- Notification n = new Notification.Builder(getContext(), "a")
+ Notification.Builder nb = new Notification.Builder(getContext(), "a")
.setContentTitle("title")
.setContentText("content text")
- .setBubbleMetadata(data)
- .build();
+ .setBubbleMetadata(data);
+ if (groupKey != null) {
+ nb.setGroup(groupKey);
+ nb.setGroupSummary(false);
+ }
+ if (groupAlert != null) {
+ nb.setGroupAlertBehavior(groupAlert);
+ }
+ Notification n = nb.build();
n.flags |= FLAG_BUBBLE;
return new NotificationEntryBuilder()
@@ -526,6 +777,10 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
.setContentText("content text")
.build();
+ return createNotification(importance, n);
+ }
+
+ private NotificationEntry createNotification(int importance, Notification n) {
return new NotificationEntryBuilder()
.setPkg("a")
.setOpPkg("a")
@@ -536,45 +791,57 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
.build();
}
+ private NotificationEntry createFsiNotification(int importance, boolean silent) {
+ Notification n = new Notification.Builder(getContext(), "a")
+ .setContentTitle("title")
+ .setContentText("content text")
+ .setFullScreenIntent(mPendingIntent, true)
+ .setGroup("fsi")
+ .setGroupAlertBehavior(silent ? GROUP_ALERT_SUMMARY : Notification.GROUP_ALERT_ALL)
+ .build();
+
+ return createNotification(importance, n);
+ }
+
private final NotificationInterruptSuppressor
mSuppressAwakeHeadsUp =
new NotificationInterruptSuppressor() {
- @Override
- public String getName() {
- return "suppressAwakeHeadsUp";
- }
+ @Override
+ public String getName() {
+ return "suppressAwakeHeadsUp";
+ }
- @Override
- public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
- return true;
- }
- };
+ @Override
+ public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
+ return true;
+ }
+ };
private final NotificationInterruptSuppressor
mSuppressAwakeInterruptions =
new NotificationInterruptSuppressor() {
- @Override
- public String getName() {
- return "suppressAwakeInterruptions";
- }
+ @Override
+ public String getName() {
+ return "suppressAwakeInterruptions";
+ }
- @Override
- public boolean suppressAwakeInterruptions(NotificationEntry entry) {
- return true;
- }
- };
+ @Override
+ public boolean suppressAwakeInterruptions(NotificationEntry entry) {
+ return true;
+ }
+ };
private final NotificationInterruptSuppressor
mSuppressInterruptions =
new NotificationInterruptSuppressor() {
- @Override
- public String getName() {
- return "suppressInterruptions";
- }
-
- @Override
- public boolean suppressInterruptions(NotificationEntry entry) {
- return true;
- }
- };
+ @Override
+ public String getName() {
+ return "suppressInterruptions";
+ }
+
+ @Override
+ public boolean suppressInterruptions(NotificationEntry entry) {
+ return true;
+ }
+ };
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
deleted file mode 100644
index 429d2ed36cc7..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.notification.logging;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.InstanceId;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotifLiveData;
-import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.logging.nano.Notifications;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import com.google.android.collect.Lists;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.Executor;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class NotificationLoggerLegacyTest extends SysuiTestCase {
- private static final String TEST_PACKAGE_NAME = "test";
- private static final int TEST_UID = 0;
-
- @Mock private NotificationListContainer mListContainer;
- @Mock private IStatusBarService mBarService;
- @Mock private ExpandableNotificationRow mRow;
- @Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
-
- // Dependency mocks:
- @Mock private NotifPipelineFlags mNotifPipelineFlags;
- @Mock private NotifLiveDataStore mNotifLiveDataStore;
- @Mock private NotifLiveData<List<NotificationEntry>> mActiveNotifList;
- @Mock private NotificationVisibilityProvider mVisibilityProvider;
- @Mock private NotificationEntryManager mEntryManager;
- @Mock private NotifPipeline mNotifPipeline;
- @Mock private NotificationListener mListener;
-
- private NotificationEntry mEntry;
- private TestableNotificationLogger mLogger;
- private ConcurrentLinkedQueue<AssertionError> mErrorQueue = new ConcurrentLinkedQueue<>();
- private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
- private NotificationPanelLoggerFake mNotificationPanelLoggerFake =
- new NotificationPanelLoggerFake();
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mNotifLiveDataStore.getActiveNotifList()).thenReturn(mActiveNotifList);
-
- mEntry = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setNotification(new Notification())
- .setUser(UserHandle.CURRENT)
- .setInstanceId(InstanceId.fakeInstanceId(1))
- .build();
- mEntry.setRow(mRow);
-
- mLogger = new TestableNotificationLogger(
- mListener,
- mUiBgExecutor,
- mNotifPipelineFlags,
- mNotifLiveDataStore,
- mVisibilityProvider,
- mEntryManager,
- mNotifPipeline,
- mock(StatusBarStateControllerImpl.class),
- mBarService,
- mExpansionStateLogger
- );
- mLogger.setUpWithContainer(mListContainer);
- verify(mEntryManager).addNotificationEntryListener(any());
- verify(mNotifPipeline, never()).addCollectionListener(any());
- }
-
- @After
- public void tearDown() {
- mLogger.mHandler.removeCallbacksAndMessages(null);
- }
-
- @Test
- public void testOnChildLocationsChangedReportsVisibilityChanged() throws Exception {
- NotificationVisibility[] newlyVisibleKeys = {
- NotificationVisibility.obtain(mEntry.getKey(), 0, 1, true)
- };
- NotificationVisibility[] noLongerVisibleKeys = {};
- doAnswer(invocation -> {
- try {
- assertArrayEquals(newlyVisibleKeys,
- (NotificationVisibility[]) invocation.getArguments()[0]);
- assertArrayEquals(noLongerVisibleKeys,
- (NotificationVisibility[]) invocation.getArguments()[1]);
- } catch (AssertionError error) {
- mErrorQueue.offer(error);
- }
- return null;
- }
- ).when(mBarService).onNotificationVisibilityChanged(any(NotificationVisibility[].class),
- any(NotificationVisibility[].class));
-
- when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
- when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry));
- mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
- TestableLooper.get(this).processAllMessages();
- mUiBgExecutor.runAllReady();
-
- if (!mErrorQueue.isEmpty()) {
- throw mErrorQueue.poll();
- }
-
- // |mEntry| won't change visibility, so it shouldn't be reported again:
- Mockito.reset(mBarService);
- mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
- TestableLooper.get(this).processAllMessages();
- mUiBgExecutor.runAllReady();
-
- verify(mBarService, never()).onNotificationVisibilityChanged(any(), any());
- }
-
- @Test
- public void testStoppingNotificationLoggingReportsCurrentNotifications()
- throws Exception {
- when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
- when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry));
- mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
- TestableLooper.get(this).processAllMessages();
- mUiBgExecutor.runAllReady();
- Mockito.reset(mBarService);
-
- setStateAsleep();
- mLogger.onDozingChanged(false); // Wake to lockscreen
- mLogger.onDozingChanged(true); // And go back to sleep, turning off logging
- mUiBgExecutor.runAllReady();
- // The visibility objects are recycled by NotificationLogger, so we can't use specific
- // matchers here.
- verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any());
- }
-
- private void setStateAsleep() {
- mLogger.onPanelExpandedChanged(true);
- mLogger.onDozingChanged(true);
- mLogger.onStateChanged(StatusBarState.KEYGUARD);
- }
-
- private void setStateAwake() {
- mLogger.onPanelExpandedChanged(false);
- mLogger.onDozingChanged(false);
- mLogger.onStateChanged(StatusBarState.SHADE);
- }
-
- @Test
- public void testLogPanelShownOnWake() {
- when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry));
- setStateAsleep();
- mLogger.onDozingChanged(false); // Wake to lockscreen
- assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
- assertTrue(mNotificationPanelLoggerFake.get(0).isLockscreen);
- assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
- Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
- assertEquals(TEST_PACKAGE_NAME, n.packageName);
- assertEquals(TEST_UID, n.uid);
- assertEquals(1, n.instanceId);
- assertFalse(n.isGroupSummary);
- assertEquals(Notifications.Notification.SECTION_ALERTING, n.section);
- }
-
- @Test
- public void testLogPanelShownOnShadePull() {
- when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry));
- setStateAwake();
- // Now expand panel
- mLogger.onPanelExpandedChanged(true);
- assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
- assertFalse(mNotificationPanelLoggerFake.get(0).isLockscreen);
- assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
- Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
- assertEquals(TEST_PACKAGE_NAME, n.packageName);
- assertEquals(TEST_UID, n.uid);
- assertEquals(1, n.instanceId);
- assertFalse(n.isGroupSummary);
- assertEquals(Notifications.Notification.SECTION_ALERTING, n.section);
- }
-
-
- @Test
- public void testLogPanelShownHandlesNullInstanceIds() {
- // Construct a NotificationEntry like mEntry, but with a null instance id.
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setNotification(new Notification())
- .setUser(UserHandle.CURRENT)
- .build();
- entry.setRow(mRow);
-
- when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(entry));
- setStateAsleep();
- mLogger.onDozingChanged(false); // Wake to lockscreen
- assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
- assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
- Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
- assertEquals(0, n.instanceId);
- }
-
- private class TestableNotificationLogger extends NotificationLogger {
-
- TestableNotificationLogger(NotificationListener notificationListener,
- Executor uiBgExecutor,
- NotifPipelineFlags notifPipelineFlags,
- NotifLiveDataStore notifLiveDataStore,
- NotificationVisibilityProvider visibilityProvider,
- NotificationEntryManager entryManager,
- NotifPipeline notifPipeline,
- StatusBarStateControllerImpl statusBarStateController,
- IStatusBarService barService,
- ExpansionStateLogger expansionStateLogger) {
- super(
- notificationListener,
- uiBgExecutor,
- notifPipelineFlags,
- notifLiveDataStore,
- visibilityProvider,
- entryManager,
- notifPipeline,
- statusBarStateController,
- expansionStateLogger,
- mNotificationPanelLoggerFake
- );
- mBarService = barService;
- mHandler.removeCallbacksAndMessages(null);
- // Make this on the current thread so we can wait for it during tests.
- mHandler = Handler.createAsync(Looper.myLooper());
- }
-
- OnChildLocationsChangedListener getChildLocationsChangedListenerForTest() {
- return mNotificationLocationsChangedListener;
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index b69bd8dfca9c..8a7b9d3b6024 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -103,7 +103,6 @@ public class NotificationLoggerTest extends SysuiTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(true);
when(mNotifLiveDataStore.getActiveNotifList()).thenReturn(mActiveNotifEntries);
mEntry = new NotificationEntryBuilder()
@@ -278,10 +277,8 @@ public class NotificationLoggerTest extends SysuiTestCase {
super(
notificationListener,
uiBgExecutor,
- notifPipelineFlags,
notifLiveDataStore,
visibilityProvider,
- entryManager,
notifPipeline,
statusBarStateController,
expansionStateLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
index 64d025628754..214ba16dfc44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
@@ -98,7 +98,7 @@ class ChannelEditorDialogControllerTest : SysuiTestCase() {
@Test
fun testPrepareDialogForApp_onlyDefaultChannel() {
- group.addChannel(channelDefault)
+ group.channels = listOf(channelDefault)
controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
setOf(channelDefault), appIcon, clickListener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index b1bf971c52fc..f8b39e8cff71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -43,6 +43,7 @@ import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.NotificationChannel;
+import android.graphics.Color;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -110,6 +111,28 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
}
@Test
+ public void testUpdateBackgroundColors_isRecursive() {
+ mGroupRow.setTintColor(Color.RED);
+ mGroupRow.getChildNotificationAt(0).setTintColor(Color.GREEN);
+ mGroupRow.getChildNotificationAt(1).setTintColor(Color.BLUE);
+
+ assertThat(mGroupRow.getCurrentBackgroundTint()).isEqualTo(Color.RED);
+ assertThat(mGroupRow.getChildNotificationAt(0).getCurrentBackgroundTint())
+ .isEqualTo(Color.GREEN);
+ assertThat(mGroupRow.getChildNotificationAt(1).getCurrentBackgroundTint())
+ .isEqualTo(Color.BLUE);
+
+ mGroupRow.updateBackgroundColors();
+
+ int resetTint = mGroupRow.getCurrentBackgroundTint();
+ assertThat(resetTint).isNotEqualTo(Color.RED);
+ assertThat(mGroupRow.getChildNotificationAt(0).getCurrentBackgroundTint())
+ .isEqualTo(resetTint);
+ assertThat(mGroupRow.getChildNotificationAt(1).getCurrentBackgroundTint())
+ .isEqualTo(resetTint);
+ }
+
+ @Test
public void testSetSensitiveOnNotifRowNotifiesOfHeightChange() throws InterruptedException {
// GIVEN a sensitive notification row that's currently redacted
measureAndLayout(mNotifRow);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 251ac7d250fe..bf7549a23707 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -194,7 +194,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
mLeakDetector,
mock(IStatusBarService.class),
NotifLiveDataStoreMocksKt.createNotifLiveDataStoreImplMock(),
- mock(DumpManager.class)
+ mock(DumpManager.class),
+ mBgExecutor
);
mEntryManager.initialize(
mNotificationListener,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 94a93ad6cf33..c199147b4f79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -47,10 +47,12 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
@@ -74,7 +76,6 @@ import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -113,6 +114,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private SysuiColorExtractor mColorExtractor;
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@Mock private MetricsLogger mMetricsLogger;
+ @Mock private DumpManager mDumpManager;
@Mock private Resources mResources;
@Mock(answer = Answers.RETURNS_SELF)
private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
@@ -148,7 +150,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
- when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false);
mController = new NotificationStackScrollLayoutController(
true,
@@ -164,9 +165,9 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mKeyguardMediaController,
mKeyguardBypassController,
mZenModeController,
- mColorExtractor,
mNotificationLockscreenUserManager,
mMetricsLogger,
+ mDumpManager,
new FalsingCollectorFake(),
new FalsingManagerFake(),
mResources,
@@ -176,15 +177,12 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mLegacyGroupManager,
mLegacyGroupManager,
mSilentHeaderController,
- mNotifPipelineFlags,
mNotifPipeline,
mNotifCollection,
mEntryManager,
mLockscreenShadeTransitionController,
mShadeTransitionController,
- mIStatusBarService,
mUiEventLogger,
- mLayoutInflater,
mRemoteInputManager,
mVisualStabilityManager,
mShadeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 275dbfd516e4..8fd6842911de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -15,6 +15,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.mock
@@ -164,4 +165,178 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
stackScrollAlgorithm.updateViewWithShelf(expandableView, expandableViewState, shelfStart)
assertFalse(expandableViewState.hidden)
}
+
+ @Test
+ fun maybeUpdateHeadsUpIsVisible_endVisible_true() {
+ val expandableViewState = ExpandableViewState()
+ expandableViewState.headsUpIsVisible = false
+
+ stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(expandableViewState,
+ /* isShadeExpanded= */ true,
+ /* mustStayOnScreen= */ true,
+ /* isViewEndVisible= */ true,
+ /* viewEnd= */ 0f,
+ /* maxHunY= */ 10f)
+
+ assertTrue(expandableViewState.headsUpIsVisible)
+ }
+
+ @Test
+ fun maybeUpdateHeadsUpIsVisible_endHidden_false() {
+ val expandableViewState = ExpandableViewState()
+ expandableViewState.headsUpIsVisible = true
+
+ stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(expandableViewState,
+ /* isShadeExpanded= */ true,
+ /* mustStayOnScreen= */ true,
+ /* isViewEndVisible= */ true,
+ /* viewEnd= */ 10f,
+ /* maxHunY= */ 0f)
+
+ assertFalse(expandableViewState.headsUpIsVisible)
+ }
+
+ @Test
+ fun maybeUpdateHeadsUpIsVisible_shadeClosed_noUpdate() {
+ val expandableViewState = ExpandableViewState()
+ expandableViewState.headsUpIsVisible = true
+
+ stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(expandableViewState,
+ /* isShadeExpanded= */ false,
+ /* mustStayOnScreen= */ true,
+ /* isViewEndVisible= */ true,
+ /* viewEnd= */ 10f,
+ /* maxHunY= */ 1f)
+
+ assertTrue(expandableViewState.headsUpIsVisible)
+ }
+
+ @Test
+ fun maybeUpdateHeadsUpIsVisible_notHUN_noUpdate() {
+ val expandableViewState = ExpandableViewState()
+ expandableViewState.headsUpIsVisible = true
+
+ stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(expandableViewState,
+ /* isShadeExpanded= */ true,
+ /* mustStayOnScreen= */ false,
+ /* isViewEndVisible= */ true,
+ /* viewEnd= */ 10f,
+ /* maxHunY= */ 1f)
+
+ assertTrue(expandableViewState.headsUpIsVisible)
+ }
+
+ @Test
+ fun maybeUpdateHeadsUpIsVisible_topHidden_noUpdate() {
+ val expandableViewState = ExpandableViewState()
+ expandableViewState.headsUpIsVisible = true
+
+ stackScrollAlgorithm.maybeUpdateHeadsUpIsVisible(expandableViewState,
+ /* isShadeExpanded= */ true,
+ /* mustStayOnScreen= */ true,
+ /* isViewEndVisible= */ false,
+ /* viewEnd= */ 10f,
+ /* maxHunY= */ 1f)
+
+ assertTrue(expandableViewState.headsUpIsVisible)
+ }
+
+ @Test
+ fun clampHunToTop_viewYGreaterThanQqs_viewYUnchanged() {
+ val expandableViewState = ExpandableViewState()
+ expandableViewState.yTranslation = 50f
+
+ stackScrollAlgorithm.clampHunToTop(/* quickQsOffsetHeight= */ 10f,
+ /* stackTranslation= */ 0f,
+ /* collapsedHeight= */ 1f, expandableViewState)
+
+ // qqs (10 + 0) < viewY (50)
+ assertEquals(50f, expandableViewState.yTranslation)
+ }
+
+ @Test
+ fun clampHunToTop_viewYLessThanQqs_viewYChanged() {
+ val expandableViewState = ExpandableViewState()
+ expandableViewState.yTranslation = -10f
+
+ stackScrollAlgorithm.clampHunToTop(/* quickQsOffsetHeight= */ 10f,
+ /* stackTranslation= */ 0f,
+ /* collapsedHeight= */ 1f, expandableViewState)
+
+ // qqs (10 + 0) > viewY (-10)
+ assertEquals(10f, expandableViewState.yTranslation)
+ }
+
+
+ @Test
+ fun clampHunToTop_viewYFarAboveVisibleStack_heightCollapsed() {
+ val expandableViewState = ExpandableViewState()
+ expandableViewState.height = 20
+ expandableViewState.yTranslation = -100f
+
+ stackScrollAlgorithm.clampHunToTop(/* quickQsOffsetHeight= */ 10f,
+ /* stackTranslation= */ 0f,
+ /* collapsedHeight= */ 10f, expandableViewState)
+
+ // newTranslation = max(10, -100) = 10
+ // distToRealY = 10 - (-100f) = 110
+ // height = max(20 - 110, 10f)
+ assertEquals(10, expandableViewState.height)
+ }
+
+ @Test
+ fun clampHunToTop_viewYNearVisibleStack_heightTallerThanCollapsed() {
+ val expandableViewState = ExpandableViewState()
+ expandableViewState.height = 20
+ expandableViewState.yTranslation = 5f
+
+ stackScrollAlgorithm.clampHunToTop(/* quickQsOffsetHeight= */ 10f,
+ /* stackTranslation= */ 0f,
+ /* collapsedHeight= */ 10f, expandableViewState)
+
+ // newTranslation = max(10, 5) = 10
+ // distToRealY = 10 - 5 = 5
+ // height = max(20 - 5, 10) = 15
+ assertEquals(15, expandableViewState.height)
+ }
+
+ @Test
+ fun computeCornerRoundnessForPinnedHun_stackBelowScreen_round() {
+ val currentRoundness = stackScrollAlgorithm.computeCornerRoundnessForPinnedHun(
+ /* hostViewHeight= */ 100f,
+ /* stackY= */ 110f,
+ /* viewMaxHeight= */ 20f,
+ /* originalCornerRoundness= */ 0f)
+ assertEquals(1f, currentRoundness)
+ }
+
+ @Test
+ fun computeCornerRoundnessForPinnedHun_stackAboveScreenBelowPinPoint_halfRound() {
+ val currentRoundness = stackScrollAlgorithm.computeCornerRoundnessForPinnedHun(
+ /* hostViewHeight= */ 100f,
+ /* stackY= */ 90f,
+ /* viewMaxHeight= */ 20f,
+ /* originalCornerRoundness= */ 0f)
+ assertEquals(0.5f, currentRoundness)
+ }
+
+ @Test
+ fun computeCornerRoundnessForPinnedHun_stackAbovePinPoint_notRound() {
+ val currentRoundness = stackScrollAlgorithm.computeCornerRoundnessForPinnedHun(
+ /* hostViewHeight= */ 100f,
+ /* stackY= */ 0f,
+ /* viewMaxHeight= */ 20f,
+ /* originalCornerRoundness= */ 0f)
+ assertEquals(0f, currentRoundness)
+ }
+
+ @Test
+ fun computeCornerRoundnessForPinnedHun_originallyRoundAndStackAbovePinPoint_round() {
+ val currentRoundness = stackScrollAlgorithm.computeCornerRoundnessForPinnedHun(
+ /* hostViewHeight= */ 100f,
+ /* stackY= */ 0f,
+ /* viewMaxHeight= */ 20f,
+ /* originalCornerRoundness= */ 1f)
+ assertEquals(1f, currentRoundness)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index e5b6286fcd7c..272ef3ddc64e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -131,6 +131,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
when(mKeyguardBypassController.onBiometricAuthenticated(any(), anyBoolean()))
.thenReturn(true);
when(mAuthController.isUdfpsFingerDown()).thenReturn(false);
+ when(mVibratorHelper.hasVibrator()).thenReturn(true);
mDependency.injectTestDependency(NotificationMediaManager.class, mMediaManager);
mBiometricUnlockController = new BiometricUnlockController(mDozeScrimController,
mKeyguardViewMediator, mScrimController, mShadeController,
@@ -423,4 +424,35 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
verify(mHandler).post(captor.capture());
captor.getValue().run();
}
+
+ @Test
+ public void onFPFailureNoHaptics_notDeviceInteractive_showBouncer() {
+ // GIVEN no vibrator and the screen is off
+ when(mVibratorHelper.hasVibrator()).thenReturn(false);
+ when(mUpdateMonitor.isDeviceInteractive()).thenReturn(false);
+ when(mUpdateMonitor.isDreaming()).thenReturn(false);
+
+ // WHEN FP fails
+ mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+
+ // after device is finished waking up
+ mBiometricUnlockController.mWakefulnessObserver.onFinishedWakingUp();
+
+ // THEN show the bouncer
+ verify(mStatusBarKeyguardViewManager).showBouncer(true);
+ }
+
+ @Test
+ public void onFPFailureNoHaptics_dreaming_showBouncer() {
+ // GIVEN no vibrator and device is dreaming
+ when(mVibratorHelper.hasVibrator()).thenReturn(false);
+ when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
+ when(mUpdateMonitor.isDreaming()).thenReturn(true);
+
+ // WHEN FP fails
+ mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+
+ // THEN show the bouncer
+ verify(mStatusBarKeyguardViewManager).showBouncer(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 9bfb2c4ce00c..d79f3361408b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -37,6 +37,8 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 2faff0ced70a..c6fb0ce34e85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -19,6 +19,8 @@ package com.android.systemui.statusbar.phone;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.fail;
@@ -44,6 +46,7 @@ import android.app.WallpaperManager;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
+import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.AmbientDisplayConfiguration;
@@ -84,11 +87,11 @@ import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenu
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.charging.WiredChargingRippleController;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentService;
@@ -102,6 +105,10 @@ import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.settings.brightness.BrightnessSliderController;
+import com.android.systemui.shade.NotificationPanelView;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.NotificationShadeWindowView;
+import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -117,8 +124,6 @@ import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
-import com.android.systemui.statusbar.charging.WiredChargingRippleController;
-import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -129,7 +134,6 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
@@ -159,7 +163,6 @@ import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.MessageRouterImpl;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.volume.VolumeComponent;
-import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.startingsurface.StartingSurface;
@@ -223,7 +226,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private AssistManager mAssistManager;
- @Mock private NotifShadeEventSource mNotifShadeEventSource;
@Mock private NotificationEntryManager mNotificationEntryManager;
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private NotificationMediaManager mNotificationMediaManager;
@@ -240,15 +242,12 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private StatusBarWindowStateController mStatusBarWindowStateController;
@Mock private NotificationViewHierarchyManager mNotificationViewHierarchyManager;
@Mock private UserSwitcherController mUserSwitcherController;
- @Mock private NetworkController mNetworkController;
- @Mock private BubblesManager mBubblesManager;
@Mock private Bubbles mBubbles;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
@Mock private DozeParameters mDozeParameters;
@Mock private Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
- @Mock private LockscreenGestureLogger mLockscreenGestureLogger;
@Mock private LockscreenWallpaper mLockscreenWallpaper;
@Mock private DozeServiceHost mDozeServiceHost;
@Mock private ViewMediatorCallback mKeyguardVieMediatorCallback;
@@ -286,7 +285,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private NotifLiveDataStore mNotifLiveDataStore;
@Mock private InteractionJankMonitor mJankMonitor;
@Mock private DeviceStateManager mDeviceStateManager;
- @Mock private DreamOverlayStateController mDreamOverlayStateController;
@Mock private WiredChargingRippleController mWiredChargingRippleController;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -308,8 +306,13 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mNotificationInterruptStateProvider =
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
mPowerManager,
- mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter,
- mStatusBarStateController, mBatteryController, mHeadsUpManager,
+ mDreamManager,
+ mAmbientDisplayConfiguration,
+ mNotificationFilter,
+ mStatusBarStateController,
+ mKeyguardStateController,
+ mBatteryController,
+ mHeadsUpManager,
mock(NotificationInterruptLogger.class),
new Handler(TestableLooper.get(this).getLooper()),
mock(NotifPipelineFlags.class),
@@ -322,10 +325,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
NotificationLogger notificationLogger = new NotificationLogger(
mNotificationListener,
mUiBgExecutor,
- mNotifPipelineFlags,
mNotifLiveDataStore,
mVisibilityProvider,
- mock(NotificationEntryManager.class),
mock(NotifPipeline.class),
mStatusBarStateController,
mExpansionStateLogger,
@@ -403,7 +404,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
new FalsingManagerFake(),
new FalsingCollectorFake(),
mBroadcastDispatcher,
- mNotifShadeEventSource,
mNotificationEntryManager,
mNotificationGutsManager,
notificationLogger,
@@ -418,13 +418,11 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mLockscreenUserManager,
mRemoteInputManager,
mUserSwitcherController,
- mNetworkController,
mBatteryController,
mColorExtractor,
new ScreenLifecycle(mDumpManager),
wakefulnessLifecycle,
mStatusBarStateController,
- Optional.of(mBubblesManager),
Optional.of(mBubbles),
mVisualStabilityManager,
mDeviceProvisionedController,
@@ -436,7 +434,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mDozeParameters,
mScrimController,
mLockscreenWallpaperLazy,
- mLockscreenGestureLogger,
mBiometricUnlockControllerLazy,
mDozeServiceHost,
mPowerManager, mScreenPinningRequest,
@@ -468,7 +465,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mLockscreenTransitionController,
mFeatureFlags,
mKeyguardUnlockAnimationController,
- new Handler(TestableLooper.get(this).getLooper()),
mMainExecutor,
new MessageRouterImpl(mMainExecutor),
mWallpaperManager,
@@ -477,7 +473,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mNotifPipelineFlags,
mJankMonitor,
mDeviceStateManager,
- mDreamOverlayStateController,
mWiredChargingRippleController, mDreamManager);
when(mKeyguardViewMediator.registerCentralSurfaces(
any(CentralSurfacesImpl.class),
@@ -848,7 +843,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mCentralSurfaces.showKeyguardImpl();
// Starting a pulse should change the scrim controller to the pulsing state
- when(mNotificationPanelViewController.isLaunchTransitionRunning()).thenReturn(true);
when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(true);
mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
@@ -885,7 +879,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mCentralSurfaces.showKeyguardImpl();
// Starting a pulse should change the scrim controller to the pulsing state
- when(mNotificationPanelViewController.isLaunchTransitionRunning()).thenReturn(true);
when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(false);
mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
@@ -1003,6 +996,22 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
verify(mStatusBarStateController, never()).setLeaveOpenOnKeyguardHide(true);
}
+ @Test
+ public void startActivityDismissingKeyguard_isShowingandIsOccluded() {
+ when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(true);
+ mCentralSurfaces.startActivityDismissingKeyguard(
+ new Intent(),
+ /* onlyProvisioned = */false,
+ /* dismissShade = */false);
+ verify(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any(Runnable.class));
+ ArgumentCaptor<OnDismissAction> onDismissActionCaptor =
+ ArgumentCaptor.forClass(OnDismissAction.class);
+ verify(mStatusBarKeyguardViewManager)
+ .dismissWithAction(onDismissActionCaptor.capture(), any(Runnable.class), eq(true));
+ assertThat(onDismissActionCaptor.getValue().onDismiss()).isFalse();
+ }
+
private void setDeviceState(int state) {
ArgumentCaptor<DeviceStateManager.DeviceStateCallback> callbackCaptor =
ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class);
@@ -1032,15 +1041,28 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
AmbientDisplayConfiguration ambientDisplayConfiguration,
NotificationFilter filter,
StatusBarStateController controller,
+ KeyguardStateController keyguardStateController,
BatteryController batteryController,
HeadsUpManager headsUpManager,
NotificationInterruptLogger logger,
Handler mainHandler,
NotifPipelineFlags flags,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider) {
- super(contentResolver, powerManager, dreamManager, ambientDisplayConfiguration, filter,
- batteryController, controller, headsUpManager, logger, mainHandler,
- flags, keyguardNotificationVisibilityProvider);
+ super(
+ contentResolver,
+ powerManager,
+ dreamManager,
+ ambientDisplayConfiguration,
+ filter,
+ batteryController,
+ controller,
+ keyguardStateController,
+ headsUpManager,
+ logger,
+ mainHandler,
+ flags,
+ keyguardNotificationVisibilityProvider
+ );
mUseHeadsUp = true;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 26ac70c70e7f..5c9871a01536 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -41,6 +41,8 @@ import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index ed22cd3c55fc..103b7b4268de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -34,6 +34,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
index 31465f45af42..3440fa5ac9b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
@@ -12,12 +12,12 @@ import com.android.systemui.statusbar.policy.AccessibilityController
import com.android.systemui.statusbar.policy.FlashlightController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.tuner.TunerService
+import java.util.concurrent.Executor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -42,7 +42,6 @@ class KeyguardBottomAreaTest : SysuiTestCase() {
mKeyguardBottomArea = LayoutInflater.from(mContext).inflate(
R.layout.keyguard_bottom_area, null, false) as KeyguardBottomAreaView
- mKeyguardBottomArea.setCentralSurfaces(mCentralSurfaces)
}
@Test
@@ -51,6 +50,5 @@ class KeyguardBottomAreaTest : SysuiTestCase() {
null, false) as KeyguardBottomAreaView
other.initFrom(mKeyguardBottomArea)
- other.launchVoiceAssist()
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 39021d8732d3..60a3d95e24f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -230,6 +230,15 @@ public class KeyguardBouncerTest extends SysuiTestCase {
}
@Test
+ public void show_notifiesKeyguardViewController() {
+ mBouncer.ensureView();
+
+ mBouncer.show(/* resetSecuritySelection= */ false);
+
+ verify(mKeyguardHostViewController).onBouncerVisibilityChanged(View.VISIBLE);
+ }
+
+ @Test
public void testHide_notifiesFalsingManager() {
mBouncer.hide(false);
verify(mFalsingCollector).onBouncerHidden();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index ec20271ea43b..ed3f7100ecc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -266,7 +266,6 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
@Test
public void clockPositionedDependingOnMarginInSplitShade() {
setSplitShadeTopMargin(400);
- mClockPositionAlgorithm.loadDimens(mResources);
givenLockScreen();
mIsSplitShade = true;
// WHEN the position algorithm is run
@@ -294,7 +293,6 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
public void notifPaddingAccountsForMultiUserSwitcherInSplitShade() {
setSplitShadeTopMargin(100);
mUserSwitchHeight = 150;
- mClockPositionAlgorithm.loadDimens(mResources);
givenLockScreen();
mIsSplitShade = true;
// WHEN the position algorithm is run
@@ -307,7 +305,6 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
public void clockDoesntAccountForMultiUserSwitcherInSplitShade() {
setSplitShadeTopMargin(100);
mUserSwitchHeight = 150;
- mClockPositionAlgorithm.loadDimens(mResources);
givenLockScreen();
mIsSplitShade = true;
// WHEN the position algorithm is run
@@ -382,11 +379,25 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
mQsExpansion = 1;
// WHEN the clock position algorithm is run
positionClock();
- // THEN the clock Y position is the middle of the screen (SCREEN_HEIGHT / 2).
+ // THEN the clock is transparent.
assertThat(mClockPosition.clockAlpha).isEqualTo(TRANSPARENT);
}
@Test
+ public void clockNotHiddenWhenQsIsExpandedInSplitShade() {
+ // GIVEN on the split lock screen with QS expansion
+ givenLockScreen();
+ mIsSplitShade = true;
+ setSplitShadeTopMargin(100);
+ mQsExpansion = 1;
+
+ // WHEN the clock position algorithm is run
+ positionClock();
+
+ assertThat(mClockPosition.clockAlpha).isEqualTo(1);
+ }
+
+ @Test
public void clockPositionMinimizesBurnInMovementToAvoidUdfpsOnAOD() {
// GIVEN a center aligned clock
mClockTopAligned = false;
@@ -524,6 +535,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
private void setSplitShadeTopMargin(int value) {
when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
.thenReturn(value);
+ mClockPositionAlgorithm.loadDimens(mResources);
}
private void givenHighestBurnInOffset() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 4e1a7088b17f..11e502fc79bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -49,6 +49,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserInfoTracker;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
index 9664035e1e1b..fca9771648ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
@@ -108,7 +108,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
null /* requestedVisibilities */,
- null /* packageName */);
+ null /* packageName */,
+ null /* letterboxDetails */);
assertTrue(mLightsOutNotifController.areLightsOut());
}
@@ -121,7 +122,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
null /* requestedVisibilities */,
- null /* packageName */);
+ null /* packageName */,
+ null /* letterboxDetails */);
assertFalse(mLightsOutNotifController.areLightsOut());
}
@@ -152,7 +154,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
null /* requestedVisibilities */,
- null /* packageName */);
+ null /* packageName */,
+ null /* letterboxDetails */);
// THEN we should show dot
assertTrue(mLightsOutNotifController.shouldShowDot());
@@ -172,7 +175,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
null /* requestedVisibilities */,
- null /* packageName */);
+ null /* packageName */,
+ null /* letterboxDetails */);
// THEN we shouldn't show the dot
assertFalse(mLightsOutNotifController.shouldShowDot());
@@ -192,7 +196,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
null /* requestedVisibilities */,
- null /* packageName */);
+ null /* packageName */,
+ null /* letterboxDetails */);
// THEN we shouldn't show the dot
assertFalse(mLightsOutNotifController.shouldShowDot());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt
deleted file mode 100644
index 7e245fcea22d..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.testing.AndroidTestingRunner
-import android.testing.TestableLooper.RunWithLooper
-import android.view.MotionEvent
-import androidx.test.filters.SmallTest
-import com.android.keyguard.LockIconViewController
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.classifier.FalsingCollectorFake
-import com.android.systemui.dock.DockManager
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.lowlightclock.LowLightClockController
-import com.android.systemui.statusbar.LockscreenShadeTransitionController
-import com.android.systemui.statusbar.NotificationShadeDepthController
-import com.android.systemui.statusbar.NotificationShadeWindowController
-import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.notification.stack.AmbientState
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView.InteractionEventHandler
-import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
-import com.android.systemui.statusbar.window.StatusBarWindowStateController
-import com.android.systemui.tuner.TunerService
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.anyFloat
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import java.util.Optional
-import org.mockito.Mockito.`when` as whenever
-
-@RunWith(AndroidTestingRunner::class)
-@RunWithLooper(setAsMainLooper = true)
-@SmallTest
-class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
- private lateinit var mController: NotificationShadeWindowViewController
-
- @Mock
- private lateinit var mView: NotificationShadeWindowView
- @Mock
- private lateinit var mTunerService: TunerService
- @Mock
- private lateinit var mStatusBarStateController: SysuiStatusBarStateController
- @Mock
- private lateinit var mCentralSurfaces: CentralSurfaces
- @Mock
- private lateinit var mDockManager: DockManager
- @Mock
- private lateinit var mNotificationPanelViewController: NotificationPanelViewController
- @Mock
- private lateinit var mNotificationShadeDepthController: NotificationShadeDepthController
- @Mock
- private lateinit var mNotificationShadeWindowController: NotificationShadeWindowController
- @Mock
- private lateinit var mKeyguardUnlockAnimationController: KeyguardUnlockAnimationController
- @Mock
- private lateinit var mAmbientState: AmbientState
- @Mock
- private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
- @Mock
- private lateinit var mStatusBarKeyguardViewManager: StatusBarKeyguardViewManager
- @Mock
- private lateinit var mStatusBarWindowStateController: StatusBarWindowStateController
- @Mock
- private lateinit var mLockscreenShadeTransitionController: LockscreenShadeTransitionController
- @Mock
- private lateinit var mLockIconViewController: LockIconViewController
- @Mock
- private lateinit var mPhoneStatusBarViewController: PhoneStatusBarViewController
- @Mock
- private lateinit var mLowLightClockController: LowLightClockController
-
- private lateinit var mInteractionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
- private lateinit var mInteractionEventHandler: InteractionEventHandler
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- whenever(mView.bottom).thenReturn(VIEW_BOTTOM)
-
- mController = NotificationShadeWindowViewController(
- mLockscreenShadeTransitionController,
- FalsingCollectorFake(),
- mTunerService,
- mStatusBarStateController,
- mDockManager,
- mNotificationShadeDepthController,
- mView,
- mNotificationPanelViewController,
- PanelExpansionStateManager(),
- stackScrollLayoutController,
- mStatusBarKeyguardViewManager,
- mStatusBarWindowStateController,
- mLockIconViewController,
- Optional.of(mLowLightClockController),
- mCentralSurfaces,
- mNotificationShadeWindowController,
- mKeyguardUnlockAnimationController,
- mAmbientState
- )
- mController.setupExpandedStatusBar()
-
- mInteractionEventHandlerCaptor =
- ArgumentCaptor.forClass(InteractionEventHandler::class.java)
- verify(mView).setInteractionEventHandler(mInteractionEventHandlerCaptor.capture())
- mInteractionEventHandler = mInteractionEventHandlerCaptor.value
- }
-
- // Note: So far, these tests only cover interactions with the status bar view controller. More
- // tests need to be added to test the rest of handleDispatchTouchEvent.
-
- @Test
- fun handleDispatchTouchEvent_nullStatusBarViewController_returnsFalse() {
- mController.setStatusBarViewController(null)
-
- val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
-
- assertThat(returnVal).isFalse()
- }
-
- @Test
- fun handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSb() {
- mController.setStatusBarViewController(mPhoneStatusBarViewController)
- val ev = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
- whenever(mPhoneStatusBarViewController.sendTouchToView(ev)).thenReturn(true)
-
- val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(ev)
-
- verify(mPhoneStatusBarViewController).sendTouchToView(ev)
- assertThat(returnVal).isTrue()
- }
-
- @Test
- fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() {
- mController.setStatusBarViewController(mPhoneStatusBarViewController)
- val downEvBelow = MotionEvent.obtain(
- 0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0
- )
- mInteractionEventHandler.handleDispatchTouchEvent(downEvBelow)
-
- val nextEvent = MotionEvent.obtain(
- 0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0
- )
- whenever(mPhoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
-
- val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(nextEvent)
-
- verify(mPhoneStatusBarViewController).sendTouchToView(nextEvent)
- assertThat(returnVal).isTrue()
- }
-
- @Test
- fun handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSb() {
- mController.setStatusBarViewController(mPhoneStatusBarViewController)
- whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true)
- whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true)
- whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(true)
- whenever(mPhoneStatusBarViewController.sendTouchToView(downEv)).thenReturn(true)
-
- val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
-
- verify(mPhoneStatusBarViewController).sendTouchToView(downEv)
- assertThat(returnVal).isTrue()
- }
-
- @Test
- fun handleDispatchTouchEvent_panelNotCollapsed_returnsNull() {
- mController.setStatusBarViewController(mPhoneStatusBarViewController)
- whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true)
- whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(true)
- // Item we're testing
- whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(false)
-
- val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
-
- verify(mPhoneStatusBarViewController, never()).sendTouchToView(downEv)
- assertThat(returnVal).isNull()
- }
-
- @Test
- fun handleDispatchTouchEvent_touchNotInSbBounds_returnsNull() {
- mController.setStatusBarViewController(mPhoneStatusBarViewController)
- whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true)
- whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true)
- // Item we're testing
- whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(false)
-
- val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
-
- verify(mPhoneStatusBarViewController, never()).sendTouchToView(downEv)
- assertThat(returnVal).isNull()
- }
-
- @Test
- fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() {
- mController.setStatusBarViewController(mPhoneStatusBarViewController)
- whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true)
- whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(true)
- // Item we're testing
- whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(false)
-
- val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv)
-
- verify(mPhoneStatusBarViewController, never()).sendTouchToView(downEv)
- assertThat(returnVal).isTrue()
- }
-
- @Test
- fun handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSb() {
- mController.setStatusBarViewController(mPhoneStatusBarViewController)
- whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true)
- whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true)
- whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(true)
-
- // Down event first
- mInteractionEventHandler.handleDispatchTouchEvent(downEv)
-
- // Then another event
- val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
- whenever(mPhoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
-
- val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(nextEvent)
-
- verify(mPhoneStatusBarViewController).sendTouchToView(nextEvent)
- assertThat(returnVal).isTrue()
- }
-
- @Test
- fun testLowLightClockAttachedWhenExpandedStatusBarSetup() {
- verify(mLowLightClockController).attachLowLightClockView(ArgumentMatchers.any())
- }
-
- @Test
- fun testLowLightClockShownWhenDozing() {
- mController.setDozing(true)
- verify(mLowLightClockController).showLowLightClock(true)
- }
-
- @Test
- fun testLowLightClockDozeTimeTickCalled() {
- mController.dozeTimeTick()
- verify(mLowLightClockController).dozeTimeTick()
- }
-
- @Test
- fun testLowLightClockHiddenWhenNotDozing() {
- mController.setDozing(true)
- verify(mLowLightClockController).showLowLightClock(true)
- mController.setDozing(false)
- verify(mLowLightClockController).showLowLightClock(false)
- }
-}
-
-private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
-private const val VIEW_BOTTOM = 100
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 9ab88dc2d764..ba29e953c73d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -136,6 +136,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
private class UnfoldConfig : UnfoldTransitionConfig {
override var isEnabled: Boolean = false
override var isHingeAngleEnabled: Boolean = false
+ override val halfFoldedTimeoutMillis: Int = 0
}
private class TestTouchEventHandler : PhoneStatusBarView.TouchEventHandler {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 32df2d790bd1..7cd275db1ed6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -59,7 +59,6 @@ import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.scrim.ScrimView;
-import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -122,8 +121,6 @@ public class ScrimControllerTest extends SysuiTestCase {
// TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
// event-dispatch-on-registration pattern caused some of these unit tests to fail.)
@Mock
- private PanelExpansionStateManager mPanelExpansionStateManager;
- @Mock
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private static class AnimatorListener implements Animator.AnimatorListener {
@@ -237,7 +234,6 @@ public class ScrimControllerTest extends SysuiTestCase {
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mScreenOffAnimationController,
- mPanelExpansionStateManager,
mKeyguardUnlockAnimationController,
mStatusBarKeyguardViewManager);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
@@ -860,7 +856,6 @@ public class ScrimControllerTest extends SysuiTestCase {
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mScreenOffAnimationController,
- mPanelExpansionStateManager,
mKeyguardUnlockAnimationController,
mStatusBarKeyguardViewManager);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
@@ -1558,6 +1553,15 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimInFront.shouldBlendWithMainColor());
}
+ @Test
+ public void applyState_unlocked_bouncerShowing() {
+ mScrimController.transitionTo(ScrimState.UNLOCKED);
+ mScrimController.setBouncerHiddenFraction(0.99f);
+ mScrimController.setRawPanelExpansionFraction(0f);
+ finishAnimationsImmediately();
+ assertScrimAlpha(mScrimBehind, 0);
+ }
+
private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) {
mScrimController.setRawPanelExpansionFraction(expansion);
finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt
new file mode 100644
index 000000000000..d84010dc7771
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.View
+import android.widget.FrameLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.StatusBarBoundsProvider.BoundsChangeListener
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class StatusBarBoundsProviderTest : SysuiTestCase() {
+
+ companion object {
+ private val START_SIDE_BOUNDS = Rect(50, 100, 150, 200)
+ private val END_SIDE_BOUNDS = Rect(250, 300, 350, 400)
+ }
+
+ @Mock private lateinit var boundsChangeListener: BoundsChangeListener
+
+ private lateinit var boundsProvider: StatusBarBoundsProvider
+
+ private lateinit var startSideContent: View
+ private lateinit var endSideContent: View
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ startSideContent = spy(FrameLayout(context)).apply { setBoundsOnScreen(START_SIDE_BOUNDS) }
+ endSideContent = spy(FrameLayout(context)).apply { setBoundsOnScreen(END_SIDE_BOUNDS) }
+
+ boundsProvider =
+ StatusBarBoundsProvider(setOf(boundsChangeListener), startSideContent, endSideContent)
+ }
+
+ @Test
+ fun visibleStartSideBounds_returnsBoundsFromStartSideContentView() {
+ assertThat(boundsProvider.visibleStartSideBounds).isEqualTo(START_SIDE_BOUNDS)
+ }
+
+ @Test
+ fun visibleEndSideBounds_returnsBoundsFromEndSideContentView() {
+ assertThat(boundsProvider.visibleEndSideBounds).isEqualTo(END_SIDE_BOUNDS)
+ }
+
+ @Test
+ fun startBoundsChange_afterStart_notifiesListener() {
+ boundsProvider.start()
+ val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 }
+
+ startSideContent.setBoundsOnScreen(newBounds)
+
+ verify(boundsChangeListener).onStatusBarBoundsChanged()
+ }
+
+ @Test
+ fun startBoundsChange_beforeStart_doesNotNotifyListener() {
+ val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 }
+
+ startSideContent.setBoundsOnScreen(newBounds)
+
+ verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
+ }
+
+ @Test
+ fun startBoundsChange_afterStop_doesNotNotifyListener() {
+ boundsProvider.start()
+ boundsProvider.stop()
+ val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 }
+
+ startSideContent.setBoundsOnScreen(newBounds)
+
+ verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
+ }
+
+ @Test
+ fun startLayoutChange_afterStart_boundsOnScreenSame_doesNotNotifyListener() {
+ boundsProvider.start()
+ val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 }
+
+ startSideContent.layout(newBounds)
+
+ verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
+ }
+
+ @Test
+ fun endBoundsChange_afterStart_notifiesListener() {
+ boundsProvider.start()
+ val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 }
+
+ endSideContent.setBoundsOnScreen(newBounds)
+
+ verify(boundsChangeListener).onStatusBarBoundsChanged()
+ }
+
+ @Test
+ fun endBoundsChange_beforeStart_doesNotNotifyListener() {
+ val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 }
+
+ endSideContent.setBoundsOnScreen(newBounds)
+
+ verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
+ }
+
+ @Test
+ fun endBoundsChange_afterStop_doesNotNotifyListener() {
+ boundsProvider.start()
+ boundsProvider.stop()
+ val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 }
+
+ endSideContent.setBoundsOnScreen(newBounds)
+
+ verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
+ }
+
+ @Test
+ fun endLayoutChange_afterStart_boundsOnScreenSame_doesNotNotifyListener() {
+ boundsProvider.start()
+ val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 }
+
+ endSideContent.layout(newBounds)
+
+ verify(boundsChangeListener, never()).onStatusBarBoundsChanged()
+ }
+}
+
+private fun View.setBoundsOnScreen(bounds: Rect) {
+ doAnswer { invocation ->
+ val boundsOutput = invocation.arguments[0] as Rect
+ boundsOutput.set(bounds)
+ return@doAnswer Unit
+ }
+ .`when`(this)
+ .getBoundsOnScreen(any())
+ layout(bounds.left, bounds.top, bounds.right, bounds.bottom)
+}
+
+private fun View.layout(rect: Rect) {
+ layout(rect.left, rect.top, rect.right, rect.bottom)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconListTest.java
index 4c20b61083f7..f0a4f3f2bf7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconListTest.java
@@ -1,4 +1,20 @@
-package com.android.systemui.statusbar;
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY;
@@ -9,13 +25,10 @@ import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import android.test.suitebuilder.annotation.SmallTest;
-
+import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.phone.StatusBarIconHolder;
-import com.android.systemui.statusbar.phone.StatusBarIconList;
import com.android.systemui.statusbar.phone.StatusBarIconList.Slot;
import org.junit.Test;
@@ -33,28 +46,39 @@ public class StatusBarIconListTest extends SysuiTestCase {
@Test
public void testGetExistingSlot() {
StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
- assertEquals(1, statusBarIconList.getSlotIndex("bbb"));
- assertEquals(2, statusBarIconList.getSlotIndex("ccc"));
+
+ List<Slot> slots = statusBarIconList.getSlots();
+ assertEquals(3, slots.size());
+ assertEquals("aaa", slots.get(0).getName());
+ assertEquals("bbb", slots.get(1).getName());
+ assertEquals("ccc", slots.get(2).getName());
}
@Test
public void testGetNonexistingSlot() {
StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
- assertEquals(0, statusBarIconList.getSlotIndex("aaa"));
- assertEquals(3, statusBarIconList.size());
- assertEquals(0, statusBarIconList.getSlotIndex("zzz")); // new content added in front
- assertEquals(1, statusBarIconList.getSlotIndex("aaa")); // slid back
- assertEquals(4, statusBarIconList.size());
+
+ statusBarIconList.getSlot("zzz");
+
+ List<Slot> slots = statusBarIconList.getSlots();
+ assertEquals(4, slots.size());
+ // new content added in front, so zzz should be first and aaa should slide back to second
+ assertEquals("zzz", slots.get(0).getName());
+ assertEquals("aaa", slots.get(1).getName());
}
@Test
public void testAddSlotSlidesIcons() {
StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class);
- statusBarIconList.setIcon(0, sbHolder);
- statusBarIconList.getSlotIndex("zzz"); // new content added in front
- assertNull(statusBarIconList.getIcon(0, TAG_PRIMARY));
- assertEquals(sbHolder, statusBarIconList.getIcon(1, TAG_PRIMARY));
+ statusBarIconList.setIcon("aaa", sbHolder);
+
+ statusBarIconList.getSlot("zzz");
+
+ List<Slot> slots = statusBarIconList.getSlots();
+ // new content added in front, so the holder we set on "aaa" should show up at index 1
+ assertNull(slots.get(0).getHolderForTag(TAG_PRIMARY));
+ assertEquals(sbHolder, slots.get(1).getHolderForTag(TAG_PRIMARY));
}
@Test
@@ -62,11 +86,13 @@ public class StatusBarIconListTest extends SysuiTestCase {
StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
StatusBarIconHolder sbHolderA = mock(StatusBarIconHolder.class);
StatusBarIconHolder sbHolderB = mock(StatusBarIconHolder.class);
- statusBarIconList.setIcon(0, sbHolderA);
- statusBarIconList.setIcon(1, sbHolderB);
- assertEquals(sbHolderA, statusBarIconList.getIcon(0, TAG_PRIMARY));
- assertEquals(sbHolderB, statusBarIconList.getIcon(1, TAG_PRIMARY));
- assertNull(statusBarIconList.getIcon(2, TAG_PRIMARY)); // icon not set
+
+ statusBarIconList.setIcon("aaa", sbHolderA);
+ statusBarIconList.setIcon("bbb", sbHolderB);
+
+ assertEquals(sbHolderA, statusBarIconList.getIconHolder("aaa", TAG_PRIMARY));
+ assertEquals(sbHolderB, statusBarIconList.getIconHolder("bbb", TAG_PRIMARY));
+ assertNull(statusBarIconList.getIconHolder("ccc", TAG_PRIMARY)); // icon not set
}
@Test
@@ -74,24 +100,31 @@ public class StatusBarIconListTest extends SysuiTestCase {
StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
StatusBarIconHolder sbHolderA = mock(StatusBarIconHolder.class);
StatusBarIconHolder sbHolderB = mock(StatusBarIconHolder.class);
- statusBarIconList.setIcon(0, sbHolderA);
- statusBarIconList.setIcon(1, sbHolderB);
- statusBarIconList.removeIcon(0, TAG_PRIMARY);
- assertNull(statusBarIconList.getIcon(0, TAG_PRIMARY)); // icon not set
+
+ statusBarIconList.setIcon("aaa", sbHolderA);
+ statusBarIconList.setIcon("bbb", sbHolderB);
+
+ statusBarIconList.removeIcon("aaa", TAG_PRIMARY);
+
+ assertNull(statusBarIconList.getIconHolder("aaa", TAG_PRIMARY)); // icon not set
}
@Test
public void testGetViewIndex_NoMultiples() {
StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class);
- statusBarIconList.setIcon(2, sbHolder);
- // Icon for item 2 is 0th child view.
- assertEquals(0, statusBarIconList.getViewIndex(2, TAG_PRIMARY));
- statusBarIconList.setIcon(0, sbHolder);
- // Icon for item 0 is 0th child view,
- assertEquals(0, statusBarIconList.getViewIndex(0, TAG_PRIMARY));
- // and item 2 is now 1st child view.
- assertEquals(1, statusBarIconList.getViewIndex(2, TAG_PRIMARY));
+
+ statusBarIconList.setIcon("ccc", sbHolder);
+
+ // Since only "ccc" has a holder set, it should be first
+ assertEquals(0, statusBarIconList.getViewIndex("ccc", TAG_PRIMARY));
+
+ // Now, also set a holder for "aaa"
+ statusBarIconList.setIcon("aaa", sbHolder);
+
+ // Then "aaa" gets the first view index and "ccc" gets the second
+ assertEquals(0, statusBarIconList.getViewIndex("aaa", TAG_PRIMARY));
+ assertEquals(1, statusBarIconList.getViewIndex("ccc", TAG_PRIMARY));
}
@Test
@@ -99,7 +132,7 @@ public class StatusBarIconListTest extends SysuiTestCase {
StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class);
- statusBarIconList.setIcon(2, sbHolder); // item 2, one icon 0th child
+ statusBarIconList.setIcon("ccc", sbHolder);
// All of these can be added to the same slot
// no tag bc it defaults to 0
@@ -111,23 +144,23 @@ public class StatusBarIconListTest extends SysuiTestCase {
int sb4Tag = 2;
when(sbHolder4.getTag()).thenReturn(sb4Tag);
- // Put a holder at slot 1, verify that it is first
- statusBarIconList.setIcon(1, sbHolder2);
- assertEquals(0, statusBarIconList.getViewIndex(1, TAG_PRIMARY));
-
- // Put another holder at slot 1, verify it's index 0 and the rest come after
- statusBarIconList.setIcon(1, sbHolder3);
- assertEquals(0, statusBarIconList.getViewIndex(1, sb3Tag));
- assertEquals(1, statusBarIconList.getViewIndex(1, TAG_PRIMARY));
- // First icon should be at the end
- assertEquals(2, statusBarIconList.getViewIndex(2, TAG_PRIMARY));
-
- // Put another one in there just for good measure
- statusBarIconList.setIcon(1, sbHolder4);
- assertEquals(0, statusBarIconList.getViewIndex(1, sb4Tag));
- assertEquals(1, statusBarIconList.getViewIndex(1, sb3Tag));
- assertEquals(2, statusBarIconList.getViewIndex(1, TAG_PRIMARY));
- assertEquals(3, statusBarIconList.getViewIndex(2, TAG_PRIMARY));
+ // Put a holder for "bbb", verify that it is first
+ statusBarIconList.setIcon("bbb", sbHolder2);
+ assertEquals(0, statusBarIconList.getViewIndex("bbb", TAG_PRIMARY));
+
+ // Put another holder for "bbb" at slot 1, verify its index 0 and the rest come after
+ statusBarIconList.setIcon("bbb", sbHolder3);
+ assertEquals(0, statusBarIconList.getViewIndex("bbb", sb3Tag));
+ assertEquals(1, statusBarIconList.getViewIndex("bbb", TAG_PRIMARY));
+ // "ccc" should appear at the end
+ assertEquals(2, statusBarIconList.getViewIndex("ccc", TAG_PRIMARY));
+
+ // Put another one in "bbb" just for good measure
+ statusBarIconList.setIcon("bbb", sbHolder4);
+ assertEquals(0, statusBarIconList.getViewIndex("bbb", sb4Tag));
+ assertEquals(1, statusBarIconList.getViewIndex("bbb", sb3Tag));
+ assertEquals(2, statusBarIconList.getViewIndex("bbb", TAG_PRIMARY));
+ assertEquals(3, statusBarIconList.getViewIndex("ccc", TAG_PRIMARY));
}
/**
@@ -172,4 +205,4 @@ public class StatusBarIconListTest extends SysuiTestCase {
return true;
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index cd081e526c80..79fce82a1bcd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -47,6 +47,7 @@ import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -61,7 +62,9 @@ import com.google.common.truth.Truth;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.Optional;
@@ -97,6 +100,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Mock private LatencyTracker mLatencyTracker;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback;
@Before
public void setUp() {
@@ -136,6 +140,11 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mBypassController);
when(mKeyguardStateController.isOccluded()).thenReturn(false);
mStatusBarKeyguardViewManager.show(null);
+ ArgumentCaptor<KeyguardBouncer.BouncerExpansionCallback> callbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardBouncer.BouncerExpansionCallback.class);
+ verify(mKeyguardBouncerFactory).create(any(ViewGroup.class),
+ callbackArgumentCaptor.capture());
+ mBouncerExpansionCallback = callbackArgumentCaptor.getValue();
}
@Test
@@ -267,21 +276,18 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
// Should be false to start, so no invocations
mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, false /* animated */);
- verify(mKeyguardUpdateMonitor, never()).onKeyguardOccludedChanged(anyBoolean());
verify(mKeyguardStateController, never()).notifyKeyguardState(anyBoolean(), anyBoolean());
clearInvocations(mKeyguardUpdateMonitor);
clearInvocations(mKeyguardStateController);
mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
- verify(mKeyguardUpdateMonitor).onKeyguardOccludedChanged(true);
verify(mKeyguardStateController).notifyKeyguardState(true, true);
clearInvocations(mKeyguardUpdateMonitor);
clearInvocations(mKeyguardStateController);
mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
- verify(mKeyguardUpdateMonitor, never()).onKeyguardOccludedChanged(anyBoolean());
verify(mKeyguardStateController, never()).notifyKeyguardState(anyBoolean(), anyBoolean());
}
@@ -291,7 +297,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager.show(null);
mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
- verify(mKeyguardUpdateMonitor).onKeyguardOccludedChanged(true);
verify(mKeyguardStateController).notifyKeyguardState(true, true);
}
@@ -301,7 +306,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager.show(null);
mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
- verify(mKeyguardUpdateMonitor).onKeyguardOccludedChanged(true);
verify(mKeyguardStateController).notifyKeyguardState(true, true);
}
@@ -451,4 +455,24 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
return new PanelExpansionChangeEvent(
fraction, expanded, tracking, /* dragDownPxAmount= */ 0f);
}
+
+ @Test
+ public void testReportBouncerOnDreamWhenVisible() {
+ mBouncerExpansionCallback.onVisibilityChanged(true);
+ verify(mCentralSurfaces).setBouncerShowingOverDream(false);
+ Mockito.clearInvocations(mCentralSurfaces);
+ when(mDreamOverlayStateController.isOverlayActive()).thenReturn(true);
+ mBouncerExpansionCallback.onVisibilityChanged(true);
+ verify(mCentralSurfaces).setBouncerShowingOverDream(true);
+ }
+
+ @Test
+ public void testReportBouncerOnDreamWhenNotVisible() {
+ mBouncerExpansionCallback.onVisibilityChanged(false);
+ verify(mCentralSurfaces).setBouncerShowingOverDream(false);
+ Mockito.clearInvocations(mCentralSurfaces);
+ when(mDreamOverlayStateController.isOverlayActive()).thenReturn(true);
+ mBouncerExpansionCallback.onVisibilityChanged(false);
+ verify(mCentralSurfaces).setBouncerShowingOverDream(false);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index ecea14c6a522..7046150ba373 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -59,18 +59,17 @@ import com.android.systemui.assist.AssistManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.statusbar.NotificationClickNotifier;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -127,8 +126,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Mock
private ShadeControllerImpl mShadeController;
@Mock
- private NotifPipelineFlags mNotifPipelineFlags;
- @Mock
private NotifPipeline mNotifPipeline;
@Mock
private NotificationVisibilityProvider mVisibilityProvider;
@@ -148,15 +145,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock
private InteractionJankMonitor mJankMonitor;
- private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
- private NotificationTestHelper mNotificationTestHelper;
+ private final FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private ExpandableNotificationRow mNotificationRow;
private ExpandableNotificationRow mBubbleNotificationRow;
private final Answer<Void> mCallOnDismiss = answerVoid(
(OnDismissAction dismissAction, Runnable cancel,
Boolean afterKeyguardGone) -> dismissAction.onDismiss());
- private ArrayList<NotificationEntry> mActiveNotifications;
@Before
public void setUp() throws Exception {
@@ -165,29 +160,28 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
when(mContentIntent.getCreatorUserHandle()).thenReturn(UserHandle.of(1));
when(mContentIntent.getIntent()).thenReturn(mContentIntentInner);
- mNotificationTestHelper = new NotificationTestHelper(
+ NotificationTestHelper notificationTestHelper = new NotificationTestHelper(
mContext,
mDependency,
TestableLooper.get(this));
// Create standard notification with contentIntent
- mNotificationRow = mNotificationTestHelper.createRow();
+ mNotificationRow = notificationTestHelper.createRow();
StatusBarNotification sbn = mNotificationRow.getEntry().getSbn();
sbn.getNotification().contentIntent = mContentIntent;
sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
// Create bubble notification row with contentIntent
- mBubbleNotificationRow = mNotificationTestHelper.createBubble();
+ mBubbleNotificationRow = notificationTestHelper.createBubble();
StatusBarNotification bubbleSbn = mBubbleNotificationRow.getEntry().getSbn();
bubbleSbn.getNotification().contentIntent = mContentIntent;
bubbleSbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
- mActiveNotifications = new ArrayList<>();
- mActiveNotifications.add(mNotificationRow.getEntry());
- mActiveNotifications.add(mBubbleNotificationRow.getEntry());
- when(mEntryManager.getVisibleNotifications()).thenReturn(mActiveNotifications);
+ ArrayList<NotificationEntry> activeNotifications = new ArrayList<>();
+ activeNotifications.add(mNotificationRow.getEntry());
+ activeNotifications.add(mBubbleNotificationRow.getEntry());
+ when(mEntryManager.getVisibleNotifications()).thenReturn(activeNotifications);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
- when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false);
when(mOnUserInteractionCallback.registerFutureDismissal(eq(mNotificationRow.getEntry()),
anyInt())).thenReturn(mFutureDismissalRunnable);
when(mVisibilityProvider.obtain(anyString(), anyBoolean()))
@@ -207,23 +201,19 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mNotificationActivityStarter =
new StatusBarNotificationActivityStarter(
getContext(),
- mock(CommandQueue.class),
mHandler,
mUiBgExecutor,
- mEntryManager,
mNotifPipeline,
mVisibilityProvider,
headsUpManager,
mActivityStarter,
mClickNotifier,
- mock(StatusBarStateController.class),
mStatusBarKeyguardViewManager,
mock(KeyguardManager.class),
mock(IDreamManager.class),
Optional.of(mBubblesManager),
() -> mAssistManager,
mRemoteInputManager,
- mock(NotificationGroupManagerLegacy.class),
mock(NotificationLockscreenUserManager.class),
mShadeController,
mKeyguardStateController,
@@ -231,7 +221,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
mock(LockPatternUtils.class),
mock(StatusBarRemoteInputCallback.class),
mActivityIntentHelper,
- mNotifPipelineFlags,
mock(MetricsLogger.class),
mock(StatusBarNotificationActivityStarterLogger.class),
mOnUserInteractionCallback,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 1a3dd3a7a2a5..fdb29770cbb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -16,12 +16,14 @@ package com.android.systemui.statusbar.phone;
import static android.view.Display.DEFAULT_DISPLAY;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
+import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.metrics.LogMaker;
import android.support.test.metricshelper.MetricsAsserts;
@@ -33,12 +35,13 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.FakeMetricsLogger;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.ForegroundServiceNotificationListener;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -50,7 +53,6 @@ import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
@@ -61,7 +63,6 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
@@ -81,6 +82,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
private FakeMetricsLogger mMetricsLogger;
private ShadeController mShadeController = mock(ShadeController.class);
private CentralSurfaces mCentralSurfaces = mock(CentralSurfaces.class);
+ private KeyguardStateController mKeyguardStateController = mock(KeyguardStateController.class);
+ private NotifPipelineFlags mNotifPipelineFlags = mock(NotifPipelineFlags.class);
private InitController mInitController = new InitController();
@Before
@@ -115,7 +118,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mock(ScrimController.class),
mock(NotificationShadeWindowController.class),
mock(DynamicPrivacyController.class),
- mock(KeyguardStateController.class),
+ mKeyguardStateController,
mock(KeyguardIndicationController.class),
mCentralSurfaces,
mock(ShadeControllerImpl.class),
@@ -125,16 +128,13 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mock(NotificationLockscreenUserManager.class),
mock(SysuiStatusBarStateController.class),
mock(NotifShadeEventSource.class),
- mock(NotificationEntryManager.class),
mock(NotificationMediaManager.class),
mock(NotificationGutsManager.class),
- mock(KeyguardUpdateMonitor.class),
lockscreenGestureLogger,
mInitController,
mNotificationInterruptStateProvider,
mock(NotificationRemoteInputManager.class),
- mock(ConfigurationController.class),
- mock(NotifPipelineFlags.class),
+ mNotifPipelineFlags,
mock(NotificationRemoteInputManager.Callback.class),
mock(NotificationListContainer.class));
mInitController.executePostInitTasks();
@@ -145,6 +145,19 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
}
@Test
+ public void testNoSuppressHeadsUp_default() {
+ Notification n = new Notification.Builder(getContext(), "a").build();
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .build();
+
+ assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
+ }
+
+ @Test
public void testSuppressHeadsUp_disabledStatusBar() {
Notification n = new Notification.Builder(getContext(), "a").build();
NotificationEntry entry = new NotificationEntryBuilder()
@@ -180,6 +193,63 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
}
@Test
+ public void testNoSuppressHeadsUp_FSI_occludedKeygaurd() {
+ when(mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()).thenReturn(false);
+ Notification n = new Notification.Builder(getContext(), "a")
+ .setFullScreenIntent(mock(PendingIntent.class), true)
+ .build();
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .build();
+
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(true);
+ when(mCentralSurfaces.isOccluded()).thenReturn(true);
+ assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
+ }
+
+ @Test
+ public void testSuppressHeadsUp_FSI_nonOccludedKeygaurd() {
+ when(mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()).thenReturn(false);
+ Notification n = new Notification.Builder(getContext(), "a")
+ .setFullScreenIntent(mock(PendingIntent.class), true)
+ .build();
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .build();
+
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+ when(mCentralSurfaces.isOccluded()).thenReturn(false);
+ assertTrue(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
+ }
+
+ @Test
+ public void testNoSuppressHeadsUp_FSI_nonOccludedKeygaurd_withNewFlag() {
+ when(mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+ Notification n = new Notification.Builder(getContext(), "a")
+ .setFullScreenIntent(mock(PendingIntent.class), true)
+ .build();
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .build();
+
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+ when(mCentralSurfaces.isOccluded()).thenReturn(false);
+ assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
+ }
+
+ @Test
public void testSuppressInterruptions_vrMode() {
Notification n = new Notification.Builder(getContext(), "a").build();
NotificationEntry entry = new NotificationEntryBuilder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
index 011279721fd2..746c92e485b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
@@ -27,6 +27,7 @@ import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.StatusBarStateControllerImpl
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -39,12 +40,12 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -184,4 +185,4 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
controller.startAnimation()
assertFalse(controller.isAnimationPlaying())
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 6abc687f0ebb..56804d58d241 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -41,10 +41,13 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogcatEchoTracker;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.OperatorNameViewController;
@@ -52,7 +55,6 @@ import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
@@ -106,6 +108,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
private NotificationPanelViewController mNotificationPanelViewController;
@Mock
private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
+ @Mock
+ private DumpManager mDumpManager;
public CollapsedStatusBarFragmentTest() {
super(CollapsedStatusBarFragment.class);
@@ -114,6 +118,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Before
public void setup() {
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
+ mDependency.injectMockDependency(DarkIconDispatcher.class);
}
@Test
@@ -122,7 +127,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@@ -132,11 +137,11 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
- assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@Test
@@ -232,7 +237,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@@ -244,7 +249,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@@ -255,12 +260,12 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
// Make sure they start out as visible
- assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertEquals(View.VISIBLE, getClockView().getVisibility());
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
assertEquals(View.GONE, getClockView().getVisibility());
}
@@ -271,14 +276,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
// Make sure they start out as visible
- assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertEquals(View.VISIBLE, getClockView().getVisibility());
fragment.onDozingChanged(true);
// When this callback is triggered, we want to make sure the clock and system info
// visibilities are recalculated. Since dozing=true, they shouldn't be visible.
- assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
assertEquals(View.GONE, getClockView().getVisibility());
}
@@ -384,7 +389,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
),
mOperatorNameViewControllerFactory,
mSecureSettings,
- mExecutor);
+ mExecutor,
+ mDumpManager);
}
private void setUpDaggerComponent() {
@@ -417,7 +423,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
return mFragment.getView().findViewById(R.id.clock);
}
- private View getSystemIconAreaView() {
- return mFragment.getView().findViewById(R.id.system_icon_area);
+ private View getEndSideContentView() {
+ return mFragment.getView().findViewById(R.id.status_bar_end_side_content);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
index fec2123b304a..fda80a2f9ed8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Intent;
+import android.os.BatteryManager;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerSaveState;
@@ -196,4 +197,26 @@ public class BatteryControllerTest extends SysuiTestCase {
TestableLooper.get(this).processAllMessages();
// Should not throw an exception
}
+
+ @Test
+ public void batteryStateChanged_withChargingSourceDock_isChargingSourceDockTrue() {
+ Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING);
+ intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_DOCK);
+
+ mBatteryController.onReceive(getContext(), intent);
+
+ Assert.assertTrue(mBatteryController.isChargingSourceDock());
+ }
+
+ @Test
+ public void batteryStateChanged_withChargingSourceNotDock_isChargingSourceDockFalse() {
+ Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_DISCHARGING);
+ intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_WIRELESS);
+
+ mBatteryController.onReceive(getContext(), intent);
+
+ Assert.assertFalse(mBatteryController.isChargingSourceDock());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt
new file mode 100644
index 000000000000..db0029af4ee2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import android.content.pm.PackageManager
+import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CameraManager
+import android.hardware.camera2.impl.CameraMetadataNative
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastSender
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.time.FakeSystemClock
+import java.util.concurrent.Executor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class FlashlightControllerImplTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var dumpManager: DumpManager
+
+ @Mock
+ private lateinit var cameraManager: CameraManager
+
+ @Mock
+ private lateinit var broadcastSender: BroadcastSender
+
+ @Mock
+ private lateinit var packageManager: PackageManager
+
+ private lateinit var fakeSettings: FakeSettings
+ private lateinit var fakeSystemClock: FakeSystemClock
+ private lateinit var backgroundExecutor: FakeExecutor
+ private lateinit var controller: FlashlightControllerImpl
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ fakeSystemClock = FakeSystemClock()
+ backgroundExecutor = FakeExecutor(fakeSystemClock)
+ fakeSettings = FakeSettings()
+
+ `when`(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH))
+ .thenReturn(true)
+
+ controller = FlashlightControllerImpl(
+ dumpManager,
+ cameraManager,
+ backgroundExecutor,
+ fakeSettings,
+ broadcastSender,
+ packageManager
+ )
+ }
+
+ @Test
+ fun testNoCameraManagerInteractionDirectlyOnConstructor() {
+ verifyZeroInteractions(cameraManager)
+ }
+
+ @Test
+ fun testCameraManagerInitAfterConstructionOnExecutor() {
+ injectCamera()
+ backgroundExecutor.runAllReady()
+
+ verify(cameraManager).registerTorchCallback(eq(backgroundExecutor), any())
+ }
+
+ @Test
+ fun testNoCallbackIfNoFlashCamera() {
+ injectCamera(flash = false)
+ backgroundExecutor.runAllReady()
+
+ verify(cameraManager, never()).registerTorchCallback(any<Executor>(), any())
+ }
+
+ @Test
+ fun testNoCallbackIfNoBackCamera() {
+ injectCamera(facing = CameraCharacteristics.LENS_FACING_FRONT)
+ backgroundExecutor.runAllReady()
+
+ verify(cameraManager, never()).registerTorchCallback(any<Executor>(), any())
+ }
+
+ @Test
+ fun testSetFlashlightInBackgroundExecutor() {
+ val id = injectCamera()
+ backgroundExecutor.runAllReady()
+
+ clearInvocations(cameraManager)
+ val enable = !controller.isEnabled
+ controller.setFlashlight(enable)
+ verifyNoMoreInteractions(cameraManager)
+
+ backgroundExecutor.runAllReady()
+ verify(cameraManager).setTorchMode(id, enable)
+ }
+
+ private fun injectCamera(
+ flash: Boolean = true,
+ facing: Int = CameraCharacteristics.LENS_FACING_BACK
+ ): String {
+ val cameraID = "ID"
+ val camera = CameraCharacteristics(CameraMetadataNative().apply {
+ set(CameraCharacteristics.FLASH_INFO_AVAILABLE, flash)
+ set(CameraCharacteristics.LENS_FACING, facing)
+ })
+ `when`(cameraManager.cameraIdList).thenReturn(arrayOf(cameraID))
+ `when`(cameraManager.getCameraCharacteristics(cameraID)).thenReturn(camera)
+ return cameraID
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index 424a40058997..b8e25ab43691 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -106,7 +106,7 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest {
public void testHunRemovedLogging() {
mAlertEntry.mEntry = mEntry;
mHeadsUpManager.onAlertEntryRemoved(mAlertEntry);
- verify(mLogger, times(1)).logNotificationActuallyRemoved(eq(mEntry.getKey()));
+ verify(mLogger, times(1)).logNotificationActuallyRemoved(eq(mEntry));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index 6bd8b98f70e1..09d7c03c2091 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy
import android.app.IActivityManager
+import android.app.NotificationManager
import android.app.admin.DevicePolicyManager
import android.content.BroadcastReceiver
import android.content.Context
@@ -38,9 +39,12 @@ import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.util.LatencyTracker
import com.android.internal.util.UserIcons
+import com.android.systemui.GuestResetOrExitSessionReceiver
import com.android.systemui.GuestResumeSessionReceiver
+import com.android.systemui.GuestSessionNotification
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.broadcast.BroadcastSender
@@ -50,7 +54,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.QSUserSwitcherEvent
import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView
+import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.telephony.TelephonyListenerManager
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
@@ -69,12 +73,12 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -102,6 +106,11 @@ class UserSwitcherControllerTest : SysuiTestCase() {
@Mock private lateinit var threadedRenderer: ThreadedRenderer
@Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
@Mock private lateinit var globalSettings: GlobalSettings
+ @Mock private lateinit var guestSessionNotification: GuestSessionNotification
+ @Mock private lateinit var guestResetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
+ private lateinit var resetSessionDialogFactory:
+ GuestResumeSessionReceiver.ResetSessionDialog.Factory
+ private lateinit var guestResumeSessionReceiver: GuestResumeSessionReceiver
private lateinit var testableLooper: TestableLooper
private lateinit var bgExecutor: FakeExecutor
private lateinit var longRunningExecutor: FakeExecutor
@@ -133,9 +142,28 @@ class UserSwitcherControllerTest : SysuiTestCase() {
com.android.internal.R.bool.config_guestUserAutoCreated, false)
mContext.addMockSystemService(Context.FACE_SERVICE, mock(FaceManager::class.java))
+ mContext.addMockSystemService(Context.NOTIFICATION_SERVICE,
+ mock(NotificationManager::class.java))
mContext.addMockSystemService(Context.FINGERPRINT_SERVICE,
mock(FingerprintManager::class.java))
+ resetSessionDialogFactory = object : GuestResumeSessionReceiver.ResetSessionDialog.Factory {
+ override fun create(userId: Int): GuestResumeSessionReceiver.ResetSessionDialog {
+ return GuestResumeSessionReceiver.ResetSessionDialog(
+ mContext,
+ mock(UserSwitcherController::class.java),
+ uiEventLogger,
+ userId
+ )
+ }
+ }
+
+ guestResumeSessionReceiver = GuestResumeSessionReceiver(userTracker,
+ secureSettings,
+ broadcastDispatcher,
+ guestSessionNotification,
+ resetSessionDialogFactory)
+
`when`(userManager.canAddMoreUsers(eq(UserManager.USER_TYPE_FULL_SECONDARY)))
.thenReturn(true)
`when`(notificationShadeWindowView.context).thenReturn(context)
@@ -198,7 +226,9 @@ class UserSwitcherControllerTest : SysuiTestCase() {
interactionJankMonitor,
latencyTracker,
dumpManager,
- dialogLaunchAnimator)
+ dialogLaunchAnimator,
+ guestResumeSessionReceiver,
+ guestResetOrExitSessionReceiver)
userSwitcherController.init(notificationShadeWindowView)
}
@@ -288,7 +318,10 @@ class UserSwitcherControllerTest : SysuiTestCase() {
.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
testableLooper.processAllMessages()
assertEquals(1, uiEventLogger.numLogs())
- assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id, uiEventLogger.eventId(0))
+ assertTrue(
+ QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id == uiEventLogger.eventId(0) ||
+ QSUserSwitcherEvent.QS_USER_SWITCH.id == uiEventLogger.eventId(0)
+ )
}
@Test
@@ -330,7 +363,10 @@ class UserSwitcherControllerTest : SysuiTestCase() {
userSwitcherController.onUserListItemClicked(currentGuestUserRecord, dialogShower)
assertNotNull(userSwitcherController.mExitGuestDialog)
testableLooper.processAllMessages()
- verify(dialogShower).showDialog(userSwitcherController.mExitGuestDialog)
+ verify(dialogShower)
+ .showDialog(
+ userSwitcherController.mExitGuestDialog,
+ DialogCuj(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN, "exit_guest_mode"))
}
@Test
@@ -350,7 +386,7 @@ class UserSwitcherControllerTest : SysuiTestCase() {
userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null)
assertNotNull(userSwitcherController.mExitGuestDialog)
userSwitcherController.mExitGuestDialog
- .getButton(DialogInterface.BUTTON_NEGATIVE).performClick()
+ .getButton(DialogInterface.BUTTON_NEUTRAL).performClick()
testableLooper.processAllMessages()
assertEquals(0, uiEventLogger.numLogs())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
index 8076b4eda883..39e4e6446d02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
@@ -25,28 +25,20 @@ import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
-import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
+import com.android.systemui.unfold.util.TestFoldStateProvider
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@SmallTest
class FoldStateLoggingProviderTest : SysuiTestCase() {
- @Captor
- private lateinit var foldUpdatesListener: ArgumentCaptor<FoldStateProvider.FoldUpdatesListener>
-
- @Mock private lateinit var foldStateProvider: FoldStateProvider
-
+ private val testFoldStateProvider = TestFoldStateProvider()
private val fakeClock = FakeSystemClock()
private lateinit var foldStateLoggingProvider: FoldStateLoggingProvider
@@ -65,12 +57,10 @@ class FoldStateLoggingProviderTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
foldStateLoggingProvider =
- FoldStateLoggingProviderImpl(foldStateProvider, fakeClock).apply {
+ FoldStateLoggingProviderImpl(testFoldStateProvider, fakeClock).apply {
addCallback(foldStateLoggingListener)
init()
}
-
- verify(foldStateProvider).addCallback(foldUpdatesListener.capture())
}
@Test
@@ -183,10 +173,10 @@ class FoldStateLoggingProviderTest : SysuiTestCase() {
fun uninit_removesCallback() {
foldStateLoggingProvider.uninit()
- verify(foldStateProvider).removeCallback(foldUpdatesListener.value)
+ assertThat(testFoldStateProvider.hasListeners).isFalse()
}
private fun sendFoldUpdate(@FoldUpdate foldUpdate: Int) {
- foldUpdatesListener.value.onFoldUpdate(foldUpdate)
+ testFoldStateProvider.sendFoldUpdate(foldUpdate)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
new file mode 100644
index 000000000000..ab450e2506f9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.config
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+/**
+ * A test that checks that we load correct resources in
+ * ResourceUnfoldTransitionConfig as we use strings there instead of R constants.
+ * Internal Android resource constants are not available in public APIs,
+ * so we can't use them there directly.
+ */
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ResourceUnfoldTransitionConfigTest : SysuiTestCase() {
+
+ private val config = ResourceUnfoldTransitionConfig()
+
+ @Test
+ fun testIsEnabled() {
+ assertThat(config.isEnabled).isEqualTo(mContext.resources
+ .getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled))
+ }
+
+ @Test
+ fun testHingeAngleEnabled() {
+ assertThat(config.isHingeAngleEnabled).isEqualTo(mContext.resources
+ .getBoolean(com.android.internal.R.bool.config_unfoldTransitionHingeAngle))
+ }
+
+ @Test
+ fun testHalfFoldedTimeout() {
+ assertThat(config.halfFoldedTimeoutMillis).isEqualTo(mContext.resources
+ .getInteger(com.android.internal.R.integer.config_unfoldTransitionHalfFoldedTimeout))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index 1f1f88b07ea1..87fca1f23f1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -16,82 +16,69 @@
package com.android.systemui.unfold.updates
-import android.app.ActivityManager
-import android.app.ActivityManager.RunningTaskInfo
-import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
-import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
-import android.app.WindowConfiguration.ActivityType
-import android.hardware.devicestate.DeviceStateManager
-import android.hardware.devicestate.DeviceStateManager.FoldStateListener
import android.os.Handler
import android.testing.AndroidTestingRunner
import androidx.core.util.Consumer
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider
+import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
-import com.android.systemui.unfold.util.FoldableDeviceStates
-import com.android.systemui.unfold.util.FoldableTestUtils
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
+import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@SmallTest
class DeviceFoldStateProviderTest : SysuiTestCase() {
- @Mock private lateinit var hingeAngleProvider: HingeAngleProvider
-
- @Mock private lateinit var screenStatusProvider: ScreenStatusProvider
-
- @Mock private lateinit var deviceStateManager: DeviceStateManager
-
- @Mock private lateinit var activityManager: ActivityManager
-
- @Mock private lateinit var handler: Handler
-
- @Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
+ @Mock
+ private lateinit var activityTypeProvider: ActivityManagerActivityTypeProvider
- @Captor private lateinit var screenOnListenerCaptor: ArgumentCaptor<ScreenListener>
+ @Mock
+ private lateinit var handler: Handler
- @Captor private lateinit var hingeAngleCaptor: ArgumentCaptor<Consumer<Float>>
+ private val foldProvider = TestFoldProvider()
+ private val screenOnStatusProvider = TestScreenOnStatusProvider()
+ private val testHingeAngleProvider = TestHingeAngleProvider()
private lateinit var foldStateProvider: DeviceFoldStateProvider
private val foldUpdates: MutableList<Int> = arrayListOf()
private val hingeAngleUpdates: MutableList<Float> = arrayListOf()
- private lateinit var deviceStates: FoldableDeviceStates
-
private var scheduledRunnable: Runnable? = null
private var scheduledRunnableDelay: Long? = null
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- overrideResource(
- com.android.internal.R.integer.config_unfoldTransitionHalfFoldedTimeout,
- HALF_OPENED_TIMEOUT_MILLIS.toInt())
- deviceStates = FoldableTestUtils.findDeviceStates(context)
+
+ val config = object : UnfoldTransitionConfig by ResourceUnfoldTransitionConfig() {
+ override val halfFoldedTimeoutMillis: Int
+ get() = HALF_OPENED_TIMEOUT_MILLIS.toInt()
+ }
foldStateProvider =
DeviceFoldStateProvider(
- context,
- hingeAngleProvider,
- screenStatusProvider,
- deviceStateManager,
- activityManager,
+ config,
+ testHingeAngleProvider,
+ screenOnStatusProvider,
+ foldProvider,
+ activityTypeProvider,
context.mainExecutor,
- handler)
+ handler
+ )
foldStateProvider.addCallback(
object : FoldStateProvider.FoldUpdatesListener {
@@ -105,10 +92,6 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
})
foldStateProvider.start()
- verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
- verify(screenStatusProvider).addCallback(screenOnListenerCaptor.capture())
- verify(hingeAngleProvider).addCallback(hingeAngleCaptor.capture())
-
whenever(handler.postDelayed(any<Runnable>(), any())).then { invocationOnMock ->
scheduledRunnable = invocationOnMock.getArgument<Runnable>(0)
scheduledRunnableDelay = invocationOnMock.getArgument<Long>(1)
@@ -125,7 +108,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
}
// By default, we're on launcher.
- setupForegroundActivityType(ACTIVITY_TYPE_HOME)
+ setupForegroundActivityType(isHomeActivity = true)
}
@Test
@@ -146,14 +129,14 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
fun testOnFolded_stopsHingeAngleProvider() {
setFoldState(folded = true)
- verify(hingeAngleProvider).stop()
+ assertThat(testHingeAngleProvider.isStarted).isFalse()
}
@Test
fun testOnUnfolded_startsHingeAngleProvider() {
setFoldState(folded = false)
- verify(hingeAngleProvider).start()
+ assertThat(testHingeAngleProvider.isStarted).isTrue()
}
@Test
@@ -310,7 +293,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
@Test
fun startClosingEvent_whileNotOnLauncher_doesNotTriggerBeforeThreshold() {
- setupForegroundActivityType(ACTIVITY_TYPE_STANDARD)
+ setupForegroundActivityType(isHomeActivity = false)
sendHingeAngleEvent(180)
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1)
@@ -319,8 +302,28 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
}
@Test
+ fun startClosingEvent_whileActivityTypeNotAvailable_triggerBeforeThreshold() {
+ setupForegroundActivityType(isHomeActivity = null)
+ sendHingeAngleEvent(180)
+
+ sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1)
+
+ assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
+ }
+
+ @Test
+ fun startClosingEvent_whileOnLauncher_doesTriggerBeforeThreshold() {
+ setupForegroundActivityType(isHomeActivity = true)
+ sendHingeAngleEvent(180)
+
+ sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1)
+
+ assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
+ }
+
+ @Test
fun startClosingEvent_whileNotOnLauncher_triggersAfterThreshold() {
- setupForegroundActivityType(ACTIVITY_TYPE_STANDARD)
+ setupForegroundActivityType(isHomeActivity = false)
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES)
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES - 1)
@@ -328,9 +331,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
}
- private fun setupForegroundActivityType(@ActivityType type: Int) {
- val taskInfo = RunningTaskInfo().apply { topActivityType = type }
- whenever(activityManager.getRunningTasks(1)).thenReturn(listOf(taskInfo))
+ private fun setupForegroundActivityType(isHomeActivity: Boolean?) {
+ whenever(activityTypeProvider.isHomeActivity).thenReturn(isHomeActivity)
}
private fun simulateTimeout(waitTime: Long = HALF_OPENED_TIMEOUT_MILLIS) {
@@ -348,16 +350,72 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
}
private fun setFoldState(folded: Boolean) {
- val state = if (folded) deviceStates.folded else deviceStates.unfolded
- foldStateListenerCaptor.value.onStateChanged(state)
+ foldProvider.notifyFolded(folded)
}
private fun fireScreenOnEvent() {
- screenOnListenerCaptor.value.onScreenTurnedOn()
+ screenOnStatusProvider.notifyScreenTurnedOn()
}
private fun sendHingeAngleEvent(angle: Int) {
- hingeAngleCaptor.value.accept(angle.toFloat())
+ testHingeAngleProvider.notifyAngle(angle.toFloat())
+ }
+
+ private class TestFoldProvider : FoldProvider {
+ private val callbacks = arrayListOf<FoldCallback>()
+
+ override fun registerCallback(callback: FoldCallback, executor: Executor) {
+ callbacks += callback
+ }
+
+ override fun unregisterCallback(callback: FoldCallback) {
+ callbacks -= callback
+ }
+
+ fun notifyFolded(isFolded: Boolean) {
+ callbacks.forEach { it.onFoldUpdated(isFolded) }
+ }
+ }
+
+ private class TestScreenOnStatusProvider : ScreenStatusProvider {
+ private val callbacks = arrayListOf<ScreenListener>()
+
+ override fun addCallback(listener: ScreenListener) {
+ callbacks += listener
+ }
+
+ override fun removeCallback(listener: ScreenListener) {
+ callbacks -= listener
+ }
+
+ fun notifyScreenTurnedOn() {
+ callbacks.forEach { it.onScreenTurnedOn() }
+ }
+ }
+
+ private class TestHingeAngleProvider : HingeAngleProvider {
+ private val callbacks = arrayListOf<Consumer<Float>>()
+ var isStarted: Boolean = false
+
+ override fun start() {
+ isStarted = true;
+ }
+
+ override fun stop() {
+ isStarted = false;
+ }
+
+ override fun addCallback(listener: Consumer<Float>) {
+ callbacks += listener
+ }
+
+ override fun removeCallback(listener: Consumer<Float>) {
+ callbacks -= listener
+ }
+
+ fun notifyAngle(angle: Float) {
+ callbacks.forEach { it.accept(angle) }
+ }
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
index a3f17aa5ba55..b2cedbf8d606 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
@@ -20,18 +20,18 @@ import android.view.IRotationWatcher
import android.view.IWindowManager
import android.view.Surface
import androidx.test.filters.SmallTest
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.TestUnfoldTransitionProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.util.mockito.any
-import com.android.systemui.SysuiTestCase
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
-import org.mockito.Mockito.verify
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -41,16 +41,13 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
@Mock
lateinit var windowManager: IWindowManager
- @Mock
- lateinit var sourceProvider: UnfoldTransitionProgressProvider
+ private val sourceProvider = TestUnfoldTransitionProvider()
@Mock
lateinit var transitionListener: TransitionProgressListener
lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
- private val sourceProviderListenerCaptor =
- ArgumentCaptor.forClass(TransitionProgressListener::class.java)
private val rotationWatcherCaptor =
ArgumentCaptor.forClass(IRotationWatcher.Stub::class.java)
@@ -66,7 +63,6 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
progressProvider.init()
- verify(sourceProvider).addCallback(sourceProviderListenerCaptor.capture())
verify(windowManager).watchRotation(rotationWatcherCaptor.capture(), any())
progressProvider.addCallback(transitionListener)
@@ -76,7 +72,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
fun testNaturalRotation0_sendTransitionStartedEvent_eventReceived() {
onRotationChanged(Surface.ROTATION_0)
- source.onTransitionStarted()
+ sourceProvider.onTransitionStarted()
verify(transitionListener).onTransitionStarted()
}
@@ -85,7 +81,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
fun testNaturalRotation0_sendTransitionProgressEvent_eventReceived() {
onRotationChanged(Surface.ROTATION_0)
- source.onTransitionProgress(0.5f)
+ sourceProvider.onTransitionProgress(0.5f)
verify(transitionListener).onTransitionProgress(0.5f)
}
@@ -94,7 +90,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
fun testNotNaturalRotation90_sendTransitionStartedEvent_eventNotReceived() {
onRotationChanged(Surface.ROTATION_90)
- source.onTransitionStarted()
+ sourceProvider.onTransitionStarted()
verify(transitionListener, never()).onTransitionStarted()
}
@@ -103,7 +99,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
fun testNaturalRotation90_sendTransitionProgressEvent_eventNotReceived() {
onRotationChanged(Surface.ROTATION_90)
- source.onTransitionProgress(0.5f)
+ sourceProvider.onTransitionProgress(0.5f)
verify(transitionListener, never()).onTransitionProgress(0.5f)
}
@@ -111,7 +107,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
@Test
fun testRotationBecameUnnaturalDuringTransition_sendsTransitionFinishedEvent() {
onRotationChanged(Surface.ROTATION_0)
- source.onTransitionStarted()
+ sourceProvider.onTransitionStarted()
clearInvocations(transitionListener)
onRotationChanged(Surface.ROTATION_90)
@@ -122,7 +118,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
@Test
fun testRotationBecameNaturalDuringTransition_sendsTransitionStartedEvent() {
onRotationChanged(Surface.ROTATION_90)
- source.onTransitionStarted()
+ sourceProvider.onTransitionStarted()
clearInvocations(transitionListener)
onRotationChanged(Surface.ROTATION_0)
@@ -133,7 +129,4 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
private fun onRotationChanged(rotation: Int) {
rotationWatcherCaptor.value.onRotationChanged(rotation)
}
-
- private val source: TransitionProgressListener
- get() = sourceProviderListenerCaptor.value
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
index db7a85166807..fc2a78a5ee7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
@@ -21,6 +21,7 @@ import android.database.ContentObserver
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.TestUnfoldTransitionProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.util.mockito.any
@@ -41,15 +42,11 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
lateinit var contentResolver: ContentResolver
@Mock
- lateinit var sourceProvider: UnfoldTransitionProgressProvider
-
- @Mock
lateinit var sinkProvider: TransitionProgressListener
- lateinit var progressProvider: ScaleAwareTransitionProgressProvider
+ private val sourceProvider = TestUnfoldTransitionProvider()
- private val sourceProviderListenerCaptor =
- ArgumentCaptor.forClass(TransitionProgressListener::class.java)
+ lateinit var progressProvider: ScaleAwareTransitionProgressProvider
private val animatorDurationScaleListenerCaptor =
ArgumentCaptor.forClass(ContentObserver::class.java)
@@ -63,7 +60,6 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
contentResolver
)
- verify(sourceProvider).addCallback(sourceProviderListenerCaptor.capture())
verify(contentResolver).registerContentObserver(any(), any(),
animatorDurationScaleListenerCaptor.capture())
@@ -74,7 +70,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
fun onTransitionStarted_animationsEnabled_eventReceived() {
setAnimationsEnabled(true)
- source.onTransitionStarted()
+ sourceProvider.onTransitionStarted()
verify(sinkProvider).onTransitionStarted()
}
@@ -83,7 +79,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
fun onTransitionStarted_animationsNotEnabled_eventNotReceived() {
setAnimationsEnabled(false)
- source.onTransitionStarted()
+ sourceProvider.onTransitionStarted()
verifyNoMoreInteractions(sinkProvider)
}
@@ -92,7 +88,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
fun onTransitionEnd_animationsEnabled_eventReceived() {
setAnimationsEnabled(true)
- source.onTransitionFinished()
+ sourceProvider.onTransitionFinished()
verify(sinkProvider).onTransitionFinished()
}
@@ -101,7 +97,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
fun onTransitionEnd_animationsNotEnabled_eventNotReceived() {
setAnimationsEnabled(false)
- source.onTransitionFinished()
+ sourceProvider.onTransitionFinished()
verifyNoMoreInteractions(sinkProvider)
}
@@ -110,7 +106,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
fun onTransitionProgress_animationsEnabled_eventReceived() {
setAnimationsEnabled(true)
- source.onTransitionProgress(42f)
+ sourceProvider.onTransitionProgress(42f)
verify(sinkProvider).onTransitionProgress(42f)
}
@@ -119,7 +115,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
fun onTransitionProgress_animationsNotEnabled_eventNotReceived() {
setAnimationsEnabled(false)
- source.onTransitionProgress(42f)
+ sourceProvider.onTransitionProgress(42f)
verifyNoMoreInteractions(sinkProvider)
}
@@ -133,7 +129,4 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
ValueAnimator.setDurationScale(durationScale)
animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */false)
}
-
- private val source: TransitionProgressListener
- get() = sourceProviderListenerCaptor.value
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt
index 8f851ec60981..a064e8c81076 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt
@@ -24,6 +24,8 @@ import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
class TestFoldStateProvider : FoldStateProvider {
private val listeners: MutableList<FoldUpdatesListener> = arrayListOf()
+ val hasListeners: Boolean
+ get() = listeners.isNotEmpty()
override fun start() {
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
index eaad69c6b9d2..66367ecfc95c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt
@@ -23,6 +23,7 @@ import android.view.LayoutInflater
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.UserSwitcherController
@@ -46,6 +47,8 @@ class UserSwitcherActivityTest : SysuiTestCase() {
@Mock
private lateinit var layoutInflater: LayoutInflater
@Mock
+ private lateinit var falsingCollector: FalsingCollector
+ @Mock
private lateinit var falsingManager: FalsingManager
@Mock
private lateinit var userManager: UserManager
@@ -59,6 +62,7 @@ class UserSwitcherActivityTest : SysuiTestCase() {
userSwitcherController,
broadcastDispatcher,
layoutInflater,
+ falsingCollector,
falsingManager,
userManager,
userTracker
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/NotificationChannelsTest.java
index c7bcdefdc4c3..900d7928fb44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/NotificationChannelsTest.java
@@ -28,7 +28,6 @@ import android.util.ArraySet;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.NotificationChannels;
import org.junit.Before;
import org.junit.Test;
@@ -41,7 +40,7 @@ import java.util.Set;
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ChannelsTest extends SysuiTestCase {
+public class NotificationChannelsTest extends SysuiTestCase {
private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
@Before
@@ -55,9 +54,10 @@ public class ChannelsTest extends SysuiTestCase {
NotificationChannels.ALERTS,
NotificationChannels.SCREENSHOTS_HEADSUP,
NotificationChannels.STORAGE,
- NotificationChannels.GENERAL,
+ NotificationChannels.INSTANT,
NotificationChannels.BATTERY,
- NotificationChannels.HINTS
+ NotificationChannels.HINTS,
+ NotificationChannels.SETUP
));
NotificationChannels.createAll(mContext);
ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
@@ -67,4 +67,11 @@ public class ChannelsTest extends SysuiTestCase {
list.forEach((chan) -> assertTrue(ALL_CHANNELS.contains(chan.getId())));
}
+ @Test
+ public void testChannelCleanup() {
+ new NotificationChannels(mContext).start();
+ ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+ verify(mMockNotificationManager).deleteNotificationChannel(captor.capture());
+ assertEquals(NotificationChannels.GENERAL, captor.getValue());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
index 5118637ea710..125b3627b342 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
@@ -65,7 +65,12 @@ public class ConditionMonitorTest extends SysuiTestCase {
mCondition3 = spy(new FakeCondition());
mConditions = new HashSet<>(Arrays.asList(mCondition1, mCondition2, mCondition3));
- mConditionMonitor = new Monitor(mExecutor, mConditions, null /*callbacks*/);
+ mConditionMonitor = new Monitor(mExecutor);
+ }
+
+ public Monitor.Subscription.Builder getDefaultBuilder(Monitor.Callback callback) {
+ return new Monitor.Subscription.Builder(callback)
+ .addConditions(mConditions);
}
@Test
@@ -74,10 +79,20 @@ public class ConditionMonitorTest extends SysuiTestCase {
final Condition regularCondition = Mockito.mock(Condition.class);
final Monitor.Callback callback = Mockito.mock(Monitor.Callback.class);
- final Monitor monitor = new Monitor(
- mExecutor,
- new HashSet<>(Arrays.asList(overridingCondition, regularCondition)),
- new HashSet<>(Arrays.asList(callback)));
+ final Monitor.Callback referenceCallback = Mockito.mock(Monitor.Callback.class);
+
+ final Monitor monitor = new Monitor(mExecutor);
+
+ monitor.addSubscription(getDefaultBuilder(callback)
+ .addCondition(overridingCondition)
+ .addCondition(regularCondition)
+ .build());
+
+ monitor.addSubscription(getDefaultBuilder(referenceCallback)
+ .addCondition(regularCondition)
+ .build());
+
+ mExecutor.runAllReady();
when(overridingCondition.isOverridingCondition()).thenReturn(true);
when(overridingCondition.isConditionMet()).thenReturn(true);
@@ -92,7 +107,9 @@ public class ConditionMonitorTest extends SysuiTestCase {
mExecutor.runAllReady();
verify(callback).onConditionsChanged(eq(true));
+ verify(referenceCallback).onConditionsChanged(eq(false));
Mockito.clearInvocations(callback);
+ Mockito.clearInvocations(referenceCallback);
when(regularCondition.isConditionMet()).thenReturn(true);
when(overridingCondition.isConditionMet()).thenReturn(false);
@@ -101,12 +118,7 @@ public class ConditionMonitorTest extends SysuiTestCase {
mExecutor.runAllReady();
verify(callback).onConditionsChanged(eq(false));
-
- clearInvocations(callback);
- monitor.removeCondition(overridingCondition);
- mExecutor.runAllReady();
-
- verify(callback).onConditionsChanged(eq(true));
+ verify(referenceCallback, never()).onConditionsChanged(anyBoolean());
}
/**
@@ -120,11 +132,14 @@ public class ConditionMonitorTest extends SysuiTestCase {
final Condition regularCondition = Mockito.mock(Condition.class);
final Monitor.Callback callback = Mockito.mock(Monitor.Callback.class);
- final Monitor monitor = new Monitor(
- mExecutor,
- new HashSet<>(Arrays.asList(overridingCondition, overridingCondition2,
- regularCondition)),
- new HashSet<>(Arrays.asList(callback)));
+ final Monitor monitor = new Monitor(mExecutor);
+
+ monitor.addSubscription(getDefaultBuilder(callback)
+ .addCondition(overridingCondition)
+ .addCondition(overridingCondition2)
+ .build());
+
+ mExecutor.runAllReady();
when(overridingCondition.isOverridingCondition()).thenReturn(true);
when(overridingCondition.isConditionMet()).thenReturn(true);
@@ -144,17 +159,35 @@ public class ConditionMonitorTest extends SysuiTestCase {
Mockito.clearInvocations(callback);
}
+ // Ensure that updating a callback that is removed doesn't result in an exception due to the
+ // absence of the condition.
+ @Test
+ public void testUpdateRemovedCallback() {
+ final Monitor.Callback callback1 =
+ mock(Monitor.Callback.class);
+ final Monitor.Subscription.Token subscription1 =
+ mConditionMonitor.addSubscription(getDefaultBuilder(callback1).build());
+ ArgumentCaptor<Condition.Callback> monitorCallback =
+ ArgumentCaptor.forClass(Condition.Callback.class);
+ mExecutor.runAllReady();
+ verify(mCondition1).addCallback(monitorCallback.capture());
+ // This will execute first before the handler for onConditionChanged.
+ mConditionMonitor.removeSubscription(subscription1);
+ monitorCallback.getValue().onConditionChanged(mCondition1);
+ mExecutor.runAllReady();
+ }
+
@Test
public void addCallback_addFirstCallback_addCallbackToAllConditions() {
final Monitor.Callback callback1 =
mock(Monitor.Callback.class);
- mConditionMonitor.addCallback(callback1);
+ mConditionMonitor.addSubscription(getDefaultBuilder(callback1).build());
mExecutor.runAllReady();
mConditions.forEach(condition -> verify(condition).addCallback(any()));
final Monitor.Callback callback2 =
mock(Monitor.Callback.class);
- mConditionMonitor.addCallback(callback2);
+ mConditionMonitor.addSubscription(getDefaultBuilder(callback2).build());
mExecutor.runAllReady();
mConditions.forEach(condition -> verify(condition, times(1)).addCallback(any()));
}
@@ -163,7 +196,7 @@ public class ConditionMonitorTest extends SysuiTestCase {
public void addCallback_addFirstCallback_reportWithDefaultValue() {
final Monitor.Callback callback =
mock(Monitor.Callback.class);
- mConditionMonitor.addCallback(callback);
+ mConditionMonitor.addSubscription(getDefaultBuilder(callback).build());
mExecutor.runAllReady();
verify(callback).onConditionsChanged(false);
}
@@ -174,67 +207,65 @@ public class ConditionMonitorTest extends SysuiTestCase {
mock(Monitor.Callback.class);
final Condition condition = mock(Condition.class);
when(condition.isConditionMet()).thenReturn(true);
- final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition)),
- new HashSet<>(Arrays.asList(callback1)));
+ final Monitor monitor = new Monitor(mExecutor);
+ monitor.addSubscription(new Monitor.Subscription.Builder(callback1)
+ .addCondition(condition)
+ .build());
final Monitor.Callback callback2 =
mock(Monitor.Callback.class);
- monitor.addCallback(callback2);
+ monitor.addSubscription(new Monitor.Subscription.Builder(callback2)
+ .addCondition(condition)
+ .build());
mExecutor.runAllReady();
verify(callback2).onConditionsChanged(eq(true));
}
@Test
public void addCallback_noConditions_reportAllConditionsMet() {
- final Monitor monitor = new Monitor(mExecutor, new HashSet<>(), null /*callbacks*/);
+ final Monitor monitor = new Monitor(mExecutor);
final Monitor.Callback callback = mock(Monitor.Callback.class);
- monitor.addCallback(callback);
+ monitor.addSubscription(new Monitor.Subscription.Builder(callback).build());
mExecutor.runAllReady();
verify(callback).onConditionsChanged(true);
}
@Test
- public void addCallback_withMultipleInstancesOfTheSameCallback_registerOnlyOne() {
- final Monitor monitor = new Monitor(mExecutor, new HashSet<>(), null /*callbacks*/);
- final Monitor.Callback callback = mock(Monitor.Callback.class);
-
- // Adds the same instance multiple times.
- monitor.addCallback(callback);
- monitor.addCallback(callback);
- monitor.addCallback(callback);
+ public void removeCallback_noFailureOnDoubleRemove() {
+ final Condition condition = mock(Condition.class);
+ final Monitor monitor = new Monitor(mExecutor);
+ final Monitor.Callback callback =
+ mock(Monitor.Callback.class);
+ final Monitor.Subscription.Token token = monitor.addSubscription(
+ new Monitor.Subscription.Builder(callback).addCondition(condition).build()
+ );
+ monitor.removeSubscription(token);
+ mExecutor.runAllReady();
+ // Ensure second removal doesn't cause an exception.
+ monitor.removeSubscription(token);
mExecutor.runAllReady();
-
- // Callback should only be triggered once.
- verify(callback, times(1)).onConditionsChanged(true);
}
@Test
public void removeCallback_shouldNoLongerReceiveUpdate() {
final Condition condition = mock(Condition.class);
- final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition)),
- null);
+ final Monitor monitor = new Monitor(mExecutor);
final Monitor.Callback callback =
mock(Monitor.Callback.class);
- monitor.addCallback(callback);
- monitor.removeCallback(callback);
+ final Monitor.Subscription.Token token = monitor.addSubscription(
+ new Monitor.Subscription.Builder(callback).addCondition(condition).build()
+ );
+ monitor.removeSubscription(token);
mExecutor.runAllReady();
clearInvocations(callback);
final ArgumentCaptor<Condition.Callback> conditionCallbackCaptor =
ArgumentCaptor.forClass(Condition.Callback.class);
verify(condition).addCallback(conditionCallbackCaptor.capture());
- final Condition.Callback conditionCallback = conditionCallbackCaptor.getValue();
- when(condition.isConditionMet()).thenReturn(true);
- conditionCallback.onConditionChanged(condition);
- mExecutor.runAllReady();
- verify(callback, never()).onConditionsChanged(true);
-
- when(condition.isConditionMet()).thenReturn(false);
- conditionCallback.onConditionChanged(condition);
- mExecutor.runAllReady();
- verify(callback, never()).onConditionsChanged(false);
+ final Condition.Callback conditionCallback = conditionCallbackCaptor.getValue();
+ verify(condition).removeCallback(conditionCallback);
}
@Test
@@ -243,14 +274,16 @@ public class ConditionMonitorTest extends SysuiTestCase {
mock(Monitor.Callback.class);
final Monitor.Callback callback2 =
mock(Monitor.Callback.class);
- mConditionMonitor.addCallback(callback1);
- mConditionMonitor.addCallback(callback2);
+ final Monitor.Subscription.Token subscription1 =
+ mConditionMonitor.addSubscription(getDefaultBuilder(callback1).build());
+ final Monitor.Subscription.Token subscription2 =
+ mConditionMonitor.addSubscription(getDefaultBuilder(callback2).build());
- mConditionMonitor.removeCallback(callback1);
+ mConditionMonitor.removeSubscription(subscription1);
mExecutor.runAllReady();
mConditions.forEach(condition -> verify(condition, never()).removeCallback(any()));
- mConditionMonitor.removeCallback(callback2);
+ mConditionMonitor.removeSubscription(subscription2);
mExecutor.runAllReady();
mConditions.forEach(condition -> verify(condition).removeCallback(any()));
}
@@ -259,7 +292,7 @@ public class ConditionMonitorTest extends SysuiTestCase {
public void updateCallbacks_allConditionsMet_reportTrue() {
final Monitor.Callback callback =
mock(Monitor.Callback.class);
- mConditionMonitor.addCallback(callback);
+ mConditionMonitor.addSubscription(getDefaultBuilder(callback).build());
clearInvocations(callback);
mCondition1.fakeUpdateCondition(true);
@@ -274,7 +307,7 @@ public class ConditionMonitorTest extends SysuiTestCase {
public void updateCallbacks_oneConditionStoppedMeeting_reportFalse() {
final Monitor.Callback callback =
mock(Monitor.Callback.class);
- mConditionMonitor.addCallback(callback);
+ mConditionMonitor.addSubscription(getDefaultBuilder(callback).build());
mCondition1.fakeUpdateCondition(true);
mCondition2.fakeUpdateCondition(true);
@@ -290,7 +323,7 @@ public class ConditionMonitorTest extends SysuiTestCase {
public void updateCallbacks_shouldOnlyUpdateWhenValueChanges() {
final Monitor.Callback callback =
mock(Monitor.Callback.class);
- mConditionMonitor.addCallback(callback);
+ mConditionMonitor.addSubscription(getDefaultBuilder(callback).build());
mExecutor.runAllReady();
verify(callback).onConditionsChanged(false);
clearInvocations(callback);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
index 22d7273dcd20..046ad1293521 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
@@ -145,4 +145,28 @@ public class ObservableServiceConnectionTest extends SysuiTestCase {
connection.unbind();
verify(mContext, never()).unbindService(eq(connection));
}
+
+ @Test
+ public void testUnbind() {
+ ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext,
+ mIntent, mExecutor, mTransformer);
+ connection.addCallback(mCallback);
+ connection.onServiceDisconnected(mComponentName);
+
+ // Disconnects before binds should be ignored.
+ verify(mCallback, never()).onDisconnected(eq(connection), anyInt());
+
+ when(mContext.bindService(eq(mIntent), anyInt(), eq(mExecutor), eq(connection)))
+ .thenReturn(true);
+ connection.bind();
+
+ mExecutor.runAllReady();
+
+ connection.unbind();
+
+ mExecutor.runAllReady();
+
+ verify(mCallback).onDisconnected(eq(connection),
+ eq(ObservableServiceConnection.DISCONNECT_REASON_UNBIND));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
index 53d4a96b0640..db0139c9b0d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.util.service;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.testing.AndroidTestingRunner;
@@ -120,6 +121,24 @@ public class PersistentConnectionManagerTest extends SysuiTestCase {
}
/**
+ * Ensures manual unbind does not reconnect.
+ */
+ @Test
+ public void testStopDoesNotReconnect() {
+ mConnectionManager.start();
+ ArgumentCaptor<ObservableServiceConnection.Callback<Proxy>> connectionCallbackCaptor =
+ ArgumentCaptor.forClass(ObservableServiceConnection.Callback.class);
+
+ verify(mConnection).addCallback(connectionCallbackCaptor.capture());
+ verify(mConnection).bind();
+ Mockito.clearInvocations(mConnection);
+ mConnectionManager.stop();
+ mFakeExecutor.advanceClockToNext();
+ mFakeExecutor.runAllReady();
+ verify(mConnection, never()).bind();
+ }
+
+ /**
* Ensures rebind on package change.
*/
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 7d4e27fa2580..9866013093cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,12 +21,9 @@ import static android.app.PendingIntent.FLAG_MUTABLE;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
-import static android.service.notification.NotificationListenerService.REASON_CANCEL;
-import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NOTIF_CANCEL;
import static com.google.common.truth.Truth.assertThat;
@@ -62,7 +59,6 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.hardware.face.FaceManager;
import android.os.Handler;
import android.os.PowerManager;
import android.os.UserHandle;
@@ -75,6 +71,7 @@ import android.testing.TestableLooper;
import android.util.Pair;
import android.util.SparseArray;
import android.view.View;
+import android.view.ViewTreeObserver;
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -88,20 +85,19 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
@@ -110,7 +106,6 @@ import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -118,7 +113,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -139,8 +133,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;
-
-import com.google.common.collect.ImmutableList;
+import com.android.wm.shell.sysui.ShellController;
import org.junit.Before;
import org.junit.Ignore;
@@ -155,22 +148,17 @@ import java.util.HashMap;
import java.util.List;
import java.util.Optional;
-/**
- * Tests the NotificationEntryManager setup with BubbleController.
- * The {@link NotifPipeline} setup with BubbleController is tested in
- * {@link NewNotifPipelineBubblesTest}.
- */
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class BubblesTest extends SysuiTestCase {
@Mock
- private NotificationEntryManager mNotificationEntryManager;
- @Mock
private CommonNotifCollection mCommonNotifCollection;
@Mock
private NotificationGroupManagerLegacy mNotificationGroupManager;
@Mock
+ private BubblesManager.NotifCallback mNotifCallback;
+ @Mock
private WindowManager mWindowManager;
@Mock
private IActivityManager mActivityManager;
@@ -183,8 +171,6 @@ public class BubblesTest extends SysuiTestCase {
@Mock
private ZenModeConfig mZenModeConfig;
@Mock
- private FaceManager mFaceManager;
- @Mock
private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
@@ -196,15 +182,17 @@ public class BubblesTest extends SysuiTestCase {
private FloatingContentCoordinator mFloatingContentCoordinator;
@Mock
private BubbleDataRepository mDataRepository;
+ @Mock
+ private NotificationShadeWindowView mNotificationShadeWindowView;
+ @Mock
+ private AuthController mAuthController;
private SysUiState mSysUiState;
private boolean mSysUiStateBubblesExpanded;
private boolean mSysUiStateBubblesManageMenuExpanded;
@Captor
- private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
- @Captor
- private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor;
+ private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
@Captor
private ArgumentCaptor<List<Bubble>> mBubbleListCaptor;
@Captor
@@ -212,27 +200,23 @@ public class BubblesTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
-
private BubblesManager mBubblesManager;
- // TODO(178618782): Move tests on the controller directly to the shell
private TestableBubbleController mBubbleController;
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
- private NotificationEntryListener mEntryListener;
- private NotificationRemoveInterceptor mRemoveInterceptor;
-
+ private NotifCollectionListener mEntryListener;
private NotificationTestHelper mNotificationTestHelper;
private NotificationEntry mRow;
private NotificationEntry mRow2;
- private NotificationEntry mRow3;
private ExpandableNotificationRow mNonBubbleNotifRow;
private BubbleEntry mBubbleEntry;
private BubbleEntry mBubbleEntry2;
- private BubbleEntry mBubbleEntry3;
private BubbleEntry mBubbleEntryUser11;
private BubbleEntry mBubbleEntry2User11;
@Mock
+ private ShellController mShellController;
+ @Mock
private Bubbles.BubbleExpandListener mBubbleExpandListener;
@Mock
private PendingIntent mDeleteIntent;
@@ -245,12 +229,8 @@ public class BubblesTest extends SysuiTestCase {
@Mock
private NotifPipeline mNotifPipeline;
@Mock
- private NotifPipelineFlags mNotifPipelineFlags;
- @Mock
private DumpManager mDumpManager;
@Mock
- private NotificationShadeWindowView mNotificationShadeWindowView;
- @Mock
private IStatusBarService mStatusBarService;
@Mock
private NotificationVisibilityProvider mVisibilityProvider;
@@ -269,8 +249,6 @@ public class BubblesTest extends SysuiTestCase {
@Mock
private ScreenOffAnimationController mScreenOffAnimationController;
@Mock
- private AuthController mAuthController;
- @Mock
private TaskViewTransitions mTaskViewTransitions;
@Mock
private Optional<OneHandedController> mOneHandedOptional;
@@ -290,8 +268,9 @@ public class BubblesTest extends SysuiTestCase {
// For the purposes of this test, just run everything synchronously
ShellExecutor syncExecutor = new SyncExecutor();
- mContext.addMockSystemService(FaceManager.class, mFaceManager);
when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
+ when(mNotificationShadeWindowView.getViewTreeObserver())
+ .thenReturn(mock(ViewTreeObserver.class));
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
@@ -308,11 +287,9 @@ public class BubblesTest extends SysuiTestCase {
TestableLooper.get(this));
mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
- mRow3 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow);
mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2);
- mBubbleEntry3 = BubblesManager.notifToBubbleEntry(mRow3);
UserHandle handle = mock(UserHandle.class);
when(handle.getIdentifier()).thenReturn(11);
@@ -321,9 +298,6 @@ public class BubblesTest extends SysuiTestCase {
mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry(
mNotificationTestHelper.createBubble(handle));
- // Return non-null notification data from the CommonNotifCollection
- when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
-
mZenModeConfig.suppressedVisualEffects = 0;
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
@@ -336,7 +310,6 @@ public class BubblesTest extends SysuiTestCase {
(sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0;
});
- // TODO: Fix
mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
mPositioner.setMaxBubbles(5);
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
@@ -348,6 +321,7 @@ public class BubblesTest extends SysuiTestCase {
mock(AmbientDisplayConfiguration.class),
mock(NotificationFilter.class),
mock(StatusBarStateController.class),
+ mock(KeyguardStateController.class),
mock(BatteryController.class),
mock(HeadsUpManager.class),
mock(NotificationInterruptLogger.class),
@@ -355,11 +329,10 @@ public class BubblesTest extends SysuiTestCase {
mock(NotifPipelineFlags.class),
mock(KeyguardNotificationVisibilityProvider.class)
);
-
- when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false);
when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor);
mBubbleController = new TestableBubbleController(
mContext,
+ mShellController,
mBubbleData,
mFloatingContentCoordinator,
mDataRepository,
@@ -388,7 +361,6 @@ public class BubblesTest extends SysuiTestCase {
mNotificationShadeWindowController,
mock(KeyguardStateController.class),
mShadeController,
- mConfigurationController,
mStatusBarService,
mock(INotificationManager.class),
mVisibilityProvider,
@@ -396,23 +368,22 @@ public class BubblesTest extends SysuiTestCase {
mZenModeController,
mLockscreenUserManager,
mNotificationGroupManager,
- mNotificationEntryManager,
mCommonNotifCollection,
mNotifPipeline,
mSysUiState,
- mNotifPipelineFlags,
mDumpManager,
syncExecutor);
+ mBubblesManager.addNotifCallback(mNotifCallback);
- // XXX: Does *this* need to be changed?
// Get a reference to the BubbleController's entry listener
- verify(mNotificationEntryManager, atLeastOnce())
- .addNotificationEntryListener(mEntryListenerCaptor.capture());
- mEntryListener = mEntryListenerCaptor.getValue();
- // And the remove interceptor
- verify(mNotificationEntryManager, atLeastOnce())
- .addNotificationRemoveInterceptor(mRemoveInterceptorCaptor.capture());
- mRemoveInterceptor = mRemoveInterceptorCaptor.getValue();
+ verify(mNotifPipeline, atLeastOnce())
+ .addCollectionListener(mNotifListenerCaptor.capture());
+ mEntryListener = mNotifListenerCaptor.getValue();
+ }
+
+ @Test
+ public void instantiateController_registerConfigChangeListener() {
+ verify(mShellController, times(1)).addConfigurationChangeListener(any());
}
@Test
@@ -433,90 +404,75 @@ public class BubblesTest extends SysuiTestCase {
@Test
public void testRemoveBubble() {
mBubbleController.updateBubble(mBubbleEntry);
- assertNotNull(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey()));
+ assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
assertTrue(mBubbleController.hasBubbles());
- verify(mNotificationEntryManager).updateNotifications(any());
+ verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
mBubbleController.removeBubble(
mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
- verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
+ verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
}
@Test
- public void testPromoteBubble_autoExpand() throws Exception {
- mBubbleController.updateBubble(mBubbleEntry2);
+ public void testRemoveBubble_withDismissedNotif_inOverflow() {
+ mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
- when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
- when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
- mBubbleController.removeBubble(
- mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
-
- Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getKey());
- assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b));
- verify(mNotificationEntryManager, never()).performRemoveNotification(
- eq(mRow.getSbn()), any(), anyInt());
- assertThat(mRow.isBubble()).isFalse();
-
- Bubble b2 = mBubbleData.getBubbleInStackWithKey(mRow2.getKey());
- assertThat(mBubbleData.getSelectedBubble()).isEqualTo(b2);
- mBubbleController.promoteBubbleFromOverflow(b);
+ assertTrue(mBubbleController.hasBubbles());
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
- assertThat(b.isBubble()).isTrue();
- assertThat(b.shouldAutoExpand()).isTrue();
- int flags = Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE
- | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
- verify(mStatusBarService, times(1)).onNotificationBubbleChanged(
- eq(b.getKey()), eq(true), eq(flags));
- }
+ // Make it look like dismissed notif
+ mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true);
- @Test
- public void testCancelOverflowBubble() {
- mBubbleController.updateBubble(mBubbleEntry2);
- mBubbleController.updateBubble(mBubbleEntry, /* suppressFlyout */
- false, /* showInShade */ true);
- when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
- when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
+ // Now remove the bubble
mBubbleController.removeBubble(
mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey()));
- mBubbleController.removeBubble(
- mRow.getKey(), DISMISS_NOTIF_CANCEL);
- verify(mNotificationEntryManager, times(1)).performRemoveNotification(
- eq(mRow.getSbn()), any(), anyInt());
- assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
- assertFalse(mRow.isBubble());
+ // We don't remove the notification since the bubble is still in overflow.
+ verify(mNotifCallback, never()).removeNotification(eq(mRow), any(), anyInt());
+ assertFalse(mBubbleController.hasBubbles());
}
@Test
- public void testUserChange_doesNotRemoveNotif() {
+ public void testRemoveBubble_withDismissedNotif_notInOverflow() {
+ mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
+ when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
+
assertTrue(mBubbleController.hasBubbles());
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+ // Make it look like dismissed notif
+ mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true);
+ // Now remove the bubble
mBubbleController.removeBubble(
- mRow.getKey(), Bubbles.DISMISS_USER_CHANGED);
- verify(mNotificationEntryManager, never()).performRemoveNotification(
- eq(mRow.getSbn()), any(), anyInt());
+ mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
+ assertFalse(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey()));
+
+ // Since the notif is dismissed and not in overflow, once the bubble is removed,
+ // removeNotification gets called to really remove the notif
+ verify(mNotifCallback, times(1)).removeNotification(eq(mRow),
+ any(), anyInt());
assertFalse(mBubbleController.hasBubbles());
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
- assertTrue(mRow.isBubble());
}
@Test
public void testDismissStack() {
mBubbleController.updateBubble(mBubbleEntry);
- verify(mNotificationEntryManager, times(1)).updateNotifications(any());
+ verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
mBubbleController.updateBubble(mBubbleEntry2);
- verify(mNotificationEntryManager, times(2)).updateNotifications(any());
+ verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey()));
assertTrue(mBubbleController.hasBubbles());
mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
- verify(mNotificationEntryManager, times(3)).updateNotifications(any());
+ verify(mNotifCallback, times(3)).invalidateNotifications(anyString());
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey()));
@@ -528,7 +484,7 @@ public class BubblesTest extends SysuiTestCase {
assertStackCollapsed();
// Mark it as a bubble and add it explicitly
- mEntryListener.onPendingEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
@@ -536,7 +492,6 @@ public class BubblesTest extends SysuiTestCase {
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Expand the stack
- BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
@@ -556,8 +511,8 @@ public class BubblesTest extends SysuiTestCase {
@Ignore("Currently broken.")
public void testCollapseAfterChangingExpandedBubble() {
// Mark it as a bubble and add it explicitly
- mEntryListener.onPendingEntryAdded(mRow);
- mEntryListener.onPendingEntryAdded(mRow2);
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow2);
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.updateBubble(mBubbleEntry2);
@@ -593,6 +548,7 @@ public class BubblesTest extends SysuiTestCase {
verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
true, mRow.getKey());
+
// Collapse
mBubbleController.collapseStack();
assertStackCollapsed();
@@ -602,7 +558,7 @@ public class BubblesTest extends SysuiTestCase {
@Test
public void testExpansionRemovesShowInShadeAndDot() {
// Mark it as a bubble and add it explicitly
- mEntryListener.onPendingEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
@@ -627,7 +583,7 @@ public class BubblesTest extends SysuiTestCase {
@Test
public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() {
// Mark it as a bubble and add it explicitly
- mEntryListener.onPendingEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
// We should have bubbles & their notifs should not be suppressed
@@ -649,7 +605,7 @@ public class BubblesTest extends SysuiTestCase {
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
// Send update
- mEntryListener.onPreEntryUpdated(mRow);
+ mEntryListener.onEntryUpdated(mRow);
// Nothing should have changed
// Notif is suppressed after expansion
@@ -661,8 +617,8 @@ public class BubblesTest extends SysuiTestCase {
@Test
public void testRemoveLastExpanded_collapses() {
// Mark it as a bubble and add it explicitly
- mEntryListener.onPendingEntryAdded(mRow);
- mEntryListener.onPendingEntryAdded(mRow2);
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow2);
mBubbleController.updateBubble(mBubbleEntry);
mBubbleController.updateBubble(mBubbleEntry2);
@@ -707,7 +663,7 @@ public class BubblesTest extends SysuiTestCase {
@Test
public void testRemoveLastExpandedEmptyOverflow_collapses() {
// Mark it as a bubble and add it explicitly
- mEntryListener.onPendingEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
// Expand
@@ -732,6 +688,7 @@ public class BubblesTest extends SysuiTestCase {
assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
}
+
@Test
public void testAutoExpand_fails_noFlag() {
assertStackCollapsed();
@@ -739,7 +696,7 @@ public class BubblesTest extends SysuiTestCase {
Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */);
// Add the auto expand bubble
- mEntryListener.onPendingEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
// Expansion shouldn't change
@@ -755,7 +712,7 @@ public class BubblesTest extends SysuiTestCase {
Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */);
// Add the auto expand bubble
- mEntryListener.onPendingEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
// Expansion should change
@@ -771,7 +728,7 @@ public class BubblesTest extends SysuiTestCase {
Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
// Add the suppress notif bubble
- mEntryListener.onPendingEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
// Notif should be suppressed because we were foreground
@@ -805,22 +762,8 @@ public class BubblesTest extends SysuiTestCase {
}
@Test
- public void testExpandStackAndSelectBubble_removedFirst() {
- mEntryListener.onPendingEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
-
- // Simulate notification cancellation.
- mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getKey(), mRow, REASON_APP_CANCEL);
-
- mBubbleController.expandStackAndSelectBubble(mBubbleEntry);
-
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
- @Test
public void testMarkNewNotificationAsShowInShade() {
- mEntryListener.onPendingEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow);
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mTestableLooper.processAllMessages();
@@ -829,8 +772,8 @@ public class BubblesTest extends SysuiTestCase {
@Test
public void testAddNotif_notBubble() {
- mEntryListener.onPendingEntryAdded(mNonBubbleNotifRow.getEntry());
- mEntryListener.onPreEntryUpdated(mNonBubbleNotifRow.getEntry());
+ mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry());
+ mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry());
assertThat(mBubbleController.hasBubbles()).isFalse();
}
@@ -868,48 +811,33 @@ public class BubblesTest extends SysuiTestCase {
NotificationListenerService.Ranking ranking = new RankingBuilder(
mRow.getRanking()).setCanBubble(false).build();
mRow.setRanking(ranking);
- mEntryListener.onPreEntryUpdated(mRow);
+ mEntryListener.onEntryUpdated(mRow);
assertFalse(mBubbleController.hasBubbles());
verify(mDeleteIntent, never()).send();
}
@Test
- public void testRemoveBubble_succeeds_appCancel() {
- mEntryListener.onPendingEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
-
- assertTrue(mBubbleController.hasBubbles());
-
- boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getKey(), mRow, REASON_APP_CANCEL);
-
- // Cancels always remove so no need to intercept
- assertFalse(intercepted);
- }
-
- @Test
public void testRemoveBubble_entryListenerRemove() {
- mEntryListener.onPendingEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
// Removes the notification
- mEntryListener.onEntryRemoved(mRow, null, false, REASON_APP_CANCEL);
+ mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL);
assertFalse(mBubbleController.hasBubbles());
}
@Test
- public void removeBubble_clearAllIntercepted() {
- mEntryListener.onPendingEntryAdded(mRow);
+ public void removeBubble_intercepted() {
+ mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
- boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getKey(), mRow, REASON_CANCEL_ALL);
+ boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
// Intercept!
assertTrue(intercepted);
@@ -918,99 +846,51 @@ public class BubblesTest extends SysuiTestCase {
}
@Test
- public void removeBubble_userDismissNotifIntercepted() {
- mEntryListener.onPendingEntryAdded(mRow);
+ public void removeBubble_dismissIntoOverflow_intercepted() {
+ mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
- boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getKey(), mRow, REASON_CANCEL);
-
- // Intercept!
- assertTrue(intercepted);
- // Should update show in shade state
- assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
- }
-
- @Test
- public void removeNotif_inOverflow_intercepted() {
- // Get bubble with notif in shade.
- mEntryListener.onPendingEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
- assertTrue(mBubbleController.hasBubbles());
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
- // Dismiss the bubble into overflow.
- mBubbleController.removeBubble(
- mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ // Dismiss the bubble
+ mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
assertFalse(mBubbleController.hasBubbles());
- boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getKey(), mRow, REASON_CANCEL);
+ // Dismiss the notification
+ boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
- // Notif is no longer a bubble, but still in overflow, so we intercept removal.
+ // Intercept dismissal since bubble is going into overflow
assertTrue(intercepted);
}
@Test
- public void removeNotif_notInOverflow_notIntercepted() {
- // Get bubble with notif in shade.
- mEntryListener.onPendingEntryAdded(mRow);
+ public void removeBubble_notIntercepted() {
+ mEntryListener.onEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
- mBubbleController.removeBubble(
- mRow.getKey(), Bubbles.DISMISS_NO_LONGER_BUBBLE);
+ // Dismiss the bubble
+ mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
assertFalse(mBubbleController.hasBubbles());
- boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getKey(), mRow, REASON_CANCEL);
+ // Dismiss the notification
+ boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
- // Notif is no longer a bubble, so we should not intercept removal.
+ // Not a bubble anymore so we don't intercept dismissal.
assertFalse(intercepted);
}
@Test
- public void testOverflowBubble_maxReached_notInShade_bubbleRemoved() {
- mBubbleController.updateBubble(
- mBubbleEntry, /* suppressFlyout */ false, /* showInShade */ false);
- mBubbleController.updateBubble(
- mBubbleEntry2, /* suppressFlyout */ false, /* showInShade */ false);
- mBubbleController.updateBubble(
- mBubbleEntry3, /* suppressFlyout */ false, /* showInShade */ false);
- when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
- when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
- when(mCommonNotifCollection.getEntry(mRow3.getKey())).thenReturn(mRow3);
- assertEquals(mBubbleData.getBubbles().size(), 3);
-
- mBubbleData.setMaxOverflowBubbles(1);
- mBubbleController.removeBubble(
- mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
- assertEquals(mBubbleData.getBubbles().size(), 2);
- assertEquals(mBubbleData.getOverflowBubbles().size(), 1);
-
- mBubbleController.removeBubble(
- mRow2.getKey(), Bubbles.DISMISS_USER_GESTURE);
- // Overflow max of 1 is reached; mRow is oldest, so it gets removed
- verify(mNotificationEntryManager, times(1)).performRemoveNotification(
- eq(mRow.getSbn()), any(), eq(REASON_CANCEL));
- assertEquals(mBubbleData.getBubbles().size(), 1);
- assertEquals(mBubbleData.getOverflowBubbles().size(), 1);
- }
-
- @Test
public void testNotifyShadeSuppressionChange_notificationDismiss() {
- mEntryListener.onPendingEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow);
assertTrue(mBubbleController.hasBubbles());
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
- mRemoveInterceptor.onNotificationRemoveRequested(
- mRow.getKey(), mRow, REASON_CANCEL);
+ mBubblesManager.handleDismissalInterception(mRow);
// Should update show in shade state
assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
@@ -1022,7 +902,7 @@ public class BubblesTest extends SysuiTestCase {
@Test
public void testNotifyShadeSuppressionChange_bubbleExpanded() {
- mEntryListener.onPendingEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow);
assertTrue(mBubbleController.hasBubbles());
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
@@ -1042,9 +922,9 @@ public class BubblesTest extends SysuiTestCase {
// GIVEN a group summary with a bubble child
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+ mEntryListener.onEntryAdded(groupedBubble.getEntry());
when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
.thenReturn(groupedBubble.getEntry());
- mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble);
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -1054,10 +934,10 @@ public class BubblesTest extends SysuiTestCase {
// THEN the summary and bubbled child are suppressed from the shade
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupedBubble.getEntry().getKey(),
- groupSummary.getEntry().getSbn().getGroupKey()));
+ groupedBubble.getEntry().getSbn().getGroupKey()));
assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
groupedBubble.getEntry().getKey(),
- groupSummary.getEntry().getSbn().getGroupKey()));
+ groupedBubble.getEntry().getSbn().getGroupKey()));
assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
}
@@ -1066,7 +946,7 @@ public class BubblesTest extends SysuiTestCase {
// GIVEN a group summary with a bubble child
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
- mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
+ mEntryListener.onEntryAdded(groupedBubble.getEntry());
when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
.thenReturn(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble);
@@ -1076,7 +956,7 @@ public class BubblesTest extends SysuiTestCase {
mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
// WHEN the summary is cancelled by the app
- mEntryListener.onEntryRemoved(groupSummary.getEntry(), null, false, REASON_APP_CANCEL);
+ mEntryListener.onEntryRemoved(groupSummary.getEntry(), REASON_APP_CANCEL);
// THEN the summary and its children are removed from bubble data
assertFalse(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -1085,14 +965,14 @@ public class BubblesTest extends SysuiTestCase {
}
@Test
- public void testSummaryDismissal_marksBubblesHiddenFromShadeAndDismissesNonBubbledChildren()
+ public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren()
throws Exception {
// GIVEN a group summary with two (non-bubble) children and one bubble child
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+ mEntryListener.onEntryAdded(groupedBubble.getEntry());
when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
.thenReturn(groupedBubble.getEntry());
- mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
groupSummary.addChildNotification(groupedBubble);
// WHEN the summary is dismissed
@@ -1100,16 +980,15 @@ public class BubblesTest extends SysuiTestCase {
// THEN only the NON-bubble children are dismissed
List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren();
- verify(mNotificationEntryManager, times(1)).performRemoveNotification(
- eq(childrenRows.get(0).getEntry().getSbn()), any(),
- eq(REASON_GROUP_SUMMARY_CANCELED));
- verify(mNotificationEntryManager, times(1)).performRemoveNotification(
- eq(childrenRows.get(1).getEntry().getSbn()), any(),
- eq(REASON_GROUP_SUMMARY_CANCELED));
- verify(mNotificationEntryManager, never()).performRemoveNotification(
- eq(groupedBubble.getEntry().getSbn()), any(), anyInt());
-
- // THEN the bubble child is suppressed from the shade
+ verify(mNotifCallback, times(1)).removeNotification(
+ eq(childrenRows.get(0).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
+ verify(mNotifCallback, times(1)).removeNotification(
+ eq(childrenRows.get(1).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
+ verify(mNotifCallback, never()).removeNotification(eq(groupedBubble.getEntry()),
+ any(), anyInt());
+
+ // THEN the bubble child still exists as a bubble and is suppressed from the shade
+ assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupedBubble.getEntry().getKey(),
groupedBubble.getEntry().getSbn().getGroupKey()));
@@ -1117,34 +996,17 @@ public class BubblesTest extends SysuiTestCase {
groupedBubble.getEntry().getKey(),
groupedBubble.getEntry().getSbn().getGroupKey()));
- // THEN the summary is removed from GroupManager
- verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry());
+ // THEN the summary is also suppressed from the shade
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ groupSummary.getEntry().getKey(),
+ groupSummary.getEntry().getSbn().getGroupKey()));
+ assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+ groupSummary.getEntry().getKey(),
+ groupSummary.getEntry().getSbn().getGroupKey()));
}
/**
- * Verifies that when a non visually interruptive update occurs for a bubble in the overflow,
- * the that bubble does not get promoted from the overflow.
- */
- @Test
- public void test_notVisuallyInterruptive_updateOverflowBubble_notAdded() {
- // Setup
- mBubbleController.updateBubble(mBubbleEntry);
- mBubbleController.updateBubble(mBubbleEntry2);
- assertTrue(mBubbleController.hasBubbles());
-
- // Overflow it
- mBubbleData.dismissBubbleWithKey(mRow.getKey(),
- Bubbles.DISMISS_USER_GESTURE);
- assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getKey())).isFalse();
- assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
-
- // Test
- mBubbleController.updateBubble(mBubbleEntry);
- assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getKey())).isFalse();
- }
-
- /**
* Verifies that when the user changes, the bubbles in the overflow list is cleared. Doesn't
* test the loading from the repository which would be a nice thing to add.
*/
@@ -1185,15 +1047,17 @@ public class BubblesTest extends SysuiTestCase {
*/
@Test
public void testOverflowLoadedOnce() {
- mBubbleController.updateBubble(mBubbleEntry);
- mBubbleController.updateBubble(mBubbleEntry2);
+ // XXX
+ when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
+ when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
+
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow2);
mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
- assertThat(mBubbleData.getOverflowBubbles().isEmpty()).isFalse();
+ assertThat(mBubbleData.getOverflowBubbles()).isNotEmpty();
- mBubbleController.updateBubble(mBubbleEntry);
- mBubbleController.updateBubble(mBubbleEntry2);
- mBubbleController.removeBubble(mBubbleEntry.getKey(), DISMISS_NOTIF_CANCEL);
- mBubbleController.removeBubble(mBubbleEntry2.getKey(), DISMISS_NOTIF_CANCEL);
+ mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL);
+ mEntryListener.onEntryRemoved(mRow2, REASON_APP_CANCEL);
assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
verify(mDataRepository, times(1)).loadBubbles(anyInt(), any());
@@ -1376,6 +1240,7 @@ public class BubblesTest extends SysuiTestCase {
assertStackCollapsed();
}
+
@Test
public void testRegisterUnregisterBroadcastListener() {
spyOn(mContext);
@@ -1455,7 +1320,7 @@ public class BubblesTest extends SysuiTestCase {
@Test
public void testSetShouldAutoExpand_notifiesFlagChanged() {
- mEntryListener.onPendingEntryAdded(mRow);
+ mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
Bubble b = mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey());
@@ -1551,7 +1416,7 @@ public class BubblesTest extends SysuiTestCase {
}
/**
- * Sets the bubble metadata flags for this entry. These ]flags are normally set by
+ * Sets the bubble metadata flags for this entry. These flags are normally set by
* NotificationManagerService when the notification is sent, however, these tests do not
* go through that path so we set them explicitly when testing.
*/
@@ -1570,12 +1435,15 @@ public class BubblesTest extends SysuiTestCase {
private Notification.BubbleMetadata getMetadata() {
Intent target = new Intent(mContext, BubblesTestActivity.class);
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, FLAG_MUTABLE);
-
- return new Notification.BubbleMetadata.Builder(bubbleIntent,
- Icon.createWithResource(mContext, R.drawable.bubble_ic_create_bubble))
+ return new Notification.BubbleMetadata.Builder(
+ bubbleIntent,
+ Icon.createWithResource(
+ mContext,
+ com.android.wm.shell.R.drawable.bubble_ic_create_bubble))
.build();
}
+
/**
* Asserts that the bubble stack is expanded and also validates the cached state is updated.
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
deleted file mode 100644
index a6327b9daed5..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ /dev/null
@@ -1,1401 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.wmshell;
-
-import static android.app.Notification.FLAG_BUBBLE;
-import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
-import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
-import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
-import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.IActivityManager;
-import android.app.INotificationManager;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.LauncherApps;
-import android.content.pm.UserInfo;
-import android.hardware.display.AmbientDisplayConfiguration;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.service.dreams.IDreamManager;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.ZenModeConfig;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.util.Pair;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.RankingBuilder;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.TaskViewTransitions;
-import com.android.wm.shell.WindowManagerShellWrapper;
-import com.android.wm.shell.bubbles.Bubble;
-import com.android.wm.shell.bubbles.BubbleData;
-import com.android.wm.shell.bubbles.BubbleDataRepository;
-import com.android.wm.shell.bubbles.BubbleEntry;
-import com.android.wm.shell.bubbles.BubbleLogger;
-import com.android.wm.shell.bubbles.BubbleStackView;
-import com.android.wm.shell.bubbles.Bubbles;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.FloatingContentCoordinator;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.draganddrop.DragAndDropController;
-import com.android.wm.shell.onehanded.OneHandedController;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Tests the NotifPipeline setup with BubbleController.
- * The NotificationEntryManager setup with BubbleController is tested in
- * {@link BubblesTest}.
- */
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class NewNotifPipelineBubblesTest extends SysuiTestCase {
- @Mock
- private NotificationEntryManager mNotificationEntryManager;
- @Mock
- private CommonNotifCollection mCommonNotifCollection;
- @Mock
- private NotificationGroupManagerLegacy mNotificationGroupManager;
- @Mock
- private BubblesManager.NotifCallback mNotifCallback;
- @Mock
- private WindowManager mWindowManager;
- @Mock
- private IActivityManager mActivityManager;
- @Mock
- private DozeParameters mDozeParameters;
- @Mock
- private ConfigurationController mConfigurationController;
- @Mock
- private ZenModeController mZenModeController;
- @Mock
- private ZenModeConfig mZenModeConfig;
- @Mock
- private NotificationLockscreenUserManager mLockscreenUserManager;
- @Mock
- private SysuiStatusBarStateController mStatusBarStateController;
- @Mock
- private KeyguardViewMediator mKeyguardViewMediator;
- @Mock
- private KeyguardBypassController mKeyguardBypassController;
- @Mock
- private FloatingContentCoordinator mFloatingContentCoordinator;
- @Mock
- private BubbleDataRepository mDataRepository;
- @Mock
- private NotificationShadeWindowView mNotificationShadeWindowView;
- @Mock
- private AuthController mAuthController;
-
- private SysUiState mSysUiState;
- private boolean mSysUiStateBubblesExpanded;
- private boolean mSysUiStateBubblesManageMenuExpanded;
-
- @Captor
- private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
- @Captor
- private ArgumentCaptor<List<Bubble>> mBubbleListCaptor;
- @Captor
- private ArgumentCaptor<IntentFilter> mFilterArgumentCaptor;
- @Captor
- private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
-
- private BubblesManager mBubblesManager;
- private TestableBubbleController mBubbleController;
- private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
- private NotifCollectionListener mEntryListener;
- private NotificationTestHelper mNotificationTestHelper;
- private NotificationEntry mRow;
- private NotificationEntry mRow2;
- private ExpandableNotificationRow mNonBubbleNotifRow;
- private BubbleEntry mBubbleEntry;
- private BubbleEntry mBubbleEntry2;
-
- private BubbleEntry mBubbleEntryUser11;
- private BubbleEntry mBubbleEntry2User11;
-
- @Mock
- private Bubbles.BubbleExpandListener mBubbleExpandListener;
- @Mock
- private PendingIntent mDeleteIntent;
- @Mock
- private SysuiColorExtractor mColorExtractor;
- @Mock
- ColorExtractor.GradientColors mGradientColors;
- @Mock
- private ShadeController mShadeController;
- @Mock
- private NotifPipeline mNotifPipeline;
- @Mock
- private NotifPipelineFlags mNotifPipelineFlags;
- @Mock
- private DumpManager mDumpManager;
- @Mock
- private IStatusBarService mStatusBarService;
- @Mock
- private NotificationVisibilityProvider mVisibilityProvider;
- @Mock
- private LauncherApps mLauncherApps;
- @Mock
- private WindowManagerShellWrapper mWindowManagerShellWrapper;
- @Mock
- private BubbleLogger mBubbleLogger;
- @Mock
- private TaskStackListenerImpl mTaskStackListener;
- @Mock
- private ShellTaskOrganizer mShellTaskOrganizer;
- @Mock
- private KeyguardStateController mKeyguardStateController;
- @Mock
- private ScreenOffAnimationController mScreenOffAnimationController;
- @Mock
- private TaskViewTransitions mTaskViewTransitions;
- @Mock
- private Optional<OneHandedController> mOneHandedOptional;
-
- private TestableBubblePositioner mPositioner;
-
- private BubbleData mBubbleData;
-
- private TestableLooper mTestableLooper;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- mTestableLooper = TestableLooper.get(this);
-
- // For the purposes of this test, just run everything synchronously
- ShellExecutor syncExecutor = new SyncExecutor();
-
- when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
-
- mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
- mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
- mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
- mColorExtractor, mDumpManager, mKeyguardStateController,
- mScreenOffAnimationController, mAuthController);
- mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
- mNotificationShadeWindowController.attach();
-
- // Need notifications for bubbles
- mNotificationTestHelper = new NotificationTestHelper(
- mContext,
- mDependency,
- TestableLooper.get(this));
- mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
- mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
- mNonBubbleNotifRow = mNotificationTestHelper.createRow();
- mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow);
- mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2);
-
- UserHandle handle = mock(UserHandle.class);
- when(handle.getIdentifier()).thenReturn(11);
- mBubbleEntryUser11 = BubblesManager.notifToBubbleEntry(
- mNotificationTestHelper.createBubble(handle));
- mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry(
- mNotificationTestHelper.createBubble(handle));
-
- mZenModeConfig.suppressedVisualEffects = 0;
- when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
-
- mSysUiState = new SysUiState();
- mSysUiState.addCallback(sysUiFlags -> {
- mSysUiStateBubblesManageMenuExpanded =
- (sysUiFlags
- & QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0;
- mSysUiStateBubblesExpanded =
- (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0;
- });
-
- mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
- mPositioner.setMaxBubbles(5);
- mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
-
- TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
- new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
- mock(PowerManager.class),
- mock(IDreamManager.class),
- mock(AmbientDisplayConfiguration.class),
- mock(NotificationFilter.class),
- mock(StatusBarStateController.class),
- mock(BatteryController.class),
- mock(HeadsUpManager.class),
- mock(NotificationInterruptLogger.class),
- mock(Handler.class),
- mock(NotifPipelineFlags.class),
- mock(KeyguardNotificationVisibilityProvider.class)
- );
- when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(true);
- when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor);
- mBubbleController = new TestableBubbleController(
- mContext,
- mBubbleData,
- mFloatingContentCoordinator,
- mDataRepository,
- mStatusBarService,
- mWindowManager,
- mWindowManagerShellWrapper,
- mock(UserManager.class),
- mLauncherApps,
- mBubbleLogger,
- mTaskStackListener,
- mShellTaskOrganizer,
- mPositioner,
- mock(DisplayController.class),
- mOneHandedOptional,
- mock(DragAndDropController.class),
- syncExecutor,
- mock(Handler.class),
- mTaskViewTransitions,
- mock(SyncTransactionQueue.class));
- mBubbleController.setExpandListener(mBubbleExpandListener);
- spyOn(mBubbleController);
-
- mBubblesManager = new BubblesManager(
- mContext,
- mBubbleController.asBubbles(),
- mNotificationShadeWindowController,
- mock(KeyguardStateController.class),
- mShadeController,
- mConfigurationController,
- mStatusBarService,
- mock(INotificationManager.class),
- mVisibilityProvider,
- interruptionStateProvider,
- mZenModeController,
- mLockscreenUserManager,
- mNotificationGroupManager,
- mNotificationEntryManager,
- mCommonNotifCollection,
- mNotifPipeline,
- mSysUiState,
- mNotifPipelineFlags,
- mDumpManager,
- syncExecutor);
- mBubblesManager.addNotifCallback(mNotifCallback);
-
- // Get a reference to the BubbleController's entry listener
- verify(mNotifPipeline, atLeastOnce())
- .addCollectionListener(mNotifListenerCaptor.capture());
- mEntryListener = mNotifListenerCaptor.getValue();
- }
-
- @Test
- public void testAddBubble() {
- mBubbleController.updateBubble(mBubbleEntry);
- assertTrue(mBubbleController.hasBubbles());
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
- @Test
- public void testHasBubbles() {
- assertFalse(mBubbleController.hasBubbles());
- mBubbleController.updateBubble(mBubbleEntry);
- assertTrue(mBubbleController.hasBubbles());
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
- @Test
- public void testRemoveBubble() {
- mBubbleController.updateBubble(mBubbleEntry);
- assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
- assertTrue(mBubbleController.hasBubbles());
- verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
-
- mBubbleController.removeBubble(
- mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
- assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
- verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
-
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
- @Test
- public void testRemoveBubble_withDismissedNotif_inOverflow() {
- mEntryListener.onEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
-
- assertTrue(mBubbleController.hasBubbles());
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
- // Make it look like dismissed notif
- mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true);
-
- // Now remove the bubble
- mBubbleController.removeBubble(
- mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
- assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey()));
-
- // We don't remove the notification since the bubble is still in overflow.
- verify(mNotifCallback, never()).removeNotification(eq(mRow), any(), anyInt());
- assertFalse(mBubbleController.hasBubbles());
- }
-
- @Test
- public void testRemoveBubble_withDismissedNotif_notInOverflow() {
- mEntryListener.onEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
- when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
-
- assertTrue(mBubbleController.hasBubbles());
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
- // Make it look like dismissed notif
- mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true);
-
- // Now remove the bubble
- mBubbleController.removeBubble(
- mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
- assertFalse(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey()));
-
- // Since the notif is dismissed and not in overflow, once the bubble is removed,
- // removeNotification gets called to really remove the notif
- verify(mNotifCallback, times(1)).removeNotification(eq(mRow),
- any(), anyInt());
- assertFalse(mBubbleController.hasBubbles());
- }
-
- @Test
- public void testDismissStack() {
- mBubbleController.updateBubble(mBubbleEntry);
- verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
- assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
- mBubbleController.updateBubble(mBubbleEntry2);
- verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
- assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey()));
- assertTrue(mBubbleController.hasBubbles());
-
- mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
- verify(mNotifCallback, times(3)).invalidateNotifications(anyString());
- assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
- assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey()));
-
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
- @Test
- public void testExpandCollapseStack() {
- assertStackCollapsed();
-
- // Mark it as a bubble and add it explicitly
- mEntryListener.onEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
-
- // We should have bubbles & their notifs should not be suppressed
- assertTrue(mBubbleController.hasBubbles());
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
- // Expand the stack
- mBubbleData.setExpanded(true);
- assertStackExpanded();
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
- // Make sure the notif is suppressed
- assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-
- // Collapse
- mBubbleController.collapseStack();
- verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
- assertStackCollapsed();
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
- @Test
- @Ignore("Currently broken.")
- public void testCollapseAfterChangingExpandedBubble() {
- // Mark it as a bubble and add it explicitly
- mEntryListener.onEntryAdded(mRow);
- mEntryListener.onEntryAdded(mRow2);
- mBubbleController.updateBubble(mBubbleEntry);
- mBubbleController.updateBubble(mBubbleEntry2);
-
- // We should have bubbles & their notifs should not be suppressed
- assertTrue(mBubbleController.hasBubbles());
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry2);
-
- // Expand
- BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleData.setExpanded(true);
- assertStackExpanded();
- verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
- true, mRow2.getKey());
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
- // Last added is the one that is expanded
- assertEquals(mRow2.getKey(), mBubbleData.getSelectedBubble().getKey());
- assertBubbleNotificationSuppressedFromShade(mBubbleEntry2);
-
- // Switch which bubble is expanded
- mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(
- mRow.getKey()));
- mBubbleData.setExpanded(true);
- assertEquals(mRow.getKey(), mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey());
- assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-
- // collapse for previous bubble
- verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
- false, mRow2.getKey());
- // expand for selected bubble
- verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
- true, mRow.getKey());
-
-
- // Collapse
- mBubbleController.collapseStack();
- assertStackCollapsed();
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
- @Test
- public void testExpansionRemovesShowInShadeAndDot() {
- // Mark it as a bubble and add it explicitly
- mEntryListener.onEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
-
- // We should have bubbles & their notifs should not be suppressed
- assertTrue(mBubbleController.hasBubbles());
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
- mTestableLooper.processAllMessages();
- assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
-
- // Expand
- mBubbleData.setExpanded(true);
- assertStackExpanded();
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
- // Notif is suppressed after expansion
- assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
- // Notif shouldn't show dot after expansion
- assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
- }
-
- @Test
- public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() {
- // Mark it as a bubble and add it explicitly
- mEntryListener.onEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
-
- // We should have bubbles & their notifs should not be suppressed
- assertTrue(mBubbleController.hasBubbles());
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
- mTestableLooper.processAllMessages();
- assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
-
- // Expand
- mBubbleData.setExpanded(true);
- assertStackExpanded();
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
- // Notif is suppressed after expansion
- assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
- // Notif shouldn't show dot after expansion
- assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
-
- // Send update
- mEntryListener.onEntryUpdated(mRow);
-
- // Nothing should have changed
- // Notif is suppressed after expansion
- assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
- // Notif shouldn't show dot after expansion
- assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
- }
-
- @Test
- public void testRemoveLastExpanded_collapses() {
- // Mark it as a bubble and add it explicitly
- mEntryListener.onEntryAdded(mRow);
- mEntryListener.onEntryAdded(mRow2);
- mBubbleController.updateBubble(mBubbleEntry);
- mBubbleController.updateBubble(mBubbleEntry2);
-
- // Expand
- BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleData.setExpanded(true);
-
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
- assertStackExpanded();
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getKey());
-
- // Last added is the one that is expanded
- assertEquals(mRow2.getKey(), mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey());
- assertBubbleNotificationSuppressedFromShade(mBubbleEntry2);
-
- // Dismiss currently expanded
- mBubbleController.removeBubble(
- mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey(),
- Bubbles.DISMISS_USER_GESTURE);
- verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getKey());
-
- // Make sure first bubble is selected
- assertEquals(mRow.getKey(), mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey());
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
-
- // Dismiss that one
- mBubbleController.removeBubble(
- mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey(),
- Bubbles.DISMISS_USER_GESTURE);
-
- // We should be collapsed
- verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
- assertFalse(mBubbleController.hasBubbles());
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
- @Test
- public void testRemoveLastExpandedEmptyOverflow_collapses() {
- // Mark it as a bubble and add it explicitly
- mEntryListener.onEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
-
- // Expand
- BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleData.setExpanded(true);
-
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
- assertStackExpanded();
- verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
-
- // Block the bubble so it won't be in the overflow
- mBubbleController.removeBubble(
- mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getKey(),
- Bubbles.DISMISS_BLOCKED);
-
- verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
-
- // We should be collapsed
- verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
- assertFalse(mBubbleController.hasBubbles());
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
-
- @Test
- public void testAutoExpand_fails_noFlag() {
- assertStackCollapsed();
- setMetadataFlags(mRow,
- Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */);
-
- // Add the auto expand bubble
- mEntryListener.onEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
-
- // Expansion shouldn't change
- verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */,
- mRow.getKey());
- assertStackCollapsed();
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
- @Test
- public void testAutoExpand_succeeds_withFlag() {
- setMetadataFlags(mRow,
- Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */);
-
- // Add the auto expand bubble
- mEntryListener.onEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
-
- // Expansion should change
- verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */,
- mRow.getKey());
- assertStackExpanded();
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
- @Test
- public void testSuppressNotif_onInitialNotif() {
- setMetadataFlags(mRow,
- Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
-
- // Add the suppress notif bubble
- mEntryListener.onEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
-
- // Notif should be suppressed because we were foreground
- assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
- // Dot + flyout is hidden because notif is suppressed
- assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
- assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout());
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
- @Test
- public void testSuppressNotif_onUpdateNotif() {
- mBubbleController.updateBubble(mBubbleEntry);
-
- // Should not be suppressed
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
- // Should show dot
- assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
-
- // Update to suppress notif
- setMetadataFlags(mRow,
- Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
- mBubbleController.updateBubble(mBubbleEntry);
-
- // Notif should be suppressed
- assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
- // Dot + flyout is hidden because notif is suppressed
- assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
- assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout());
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
- @Test
- public void testMarkNewNotificationAsShowInShade() {
- mEntryListener.onEntryAdded(mRow);
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
- mTestableLooper.processAllMessages();
- assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
- }
-
- @Test
- public void testAddNotif_notBubble() {
- mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry());
- mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry());
-
- assertThat(mBubbleController.hasBubbles()).isFalse();
- }
-
- @Test
- public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mBubbleEntry);
- mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_AGED);
- verify(mDeleteIntent, never()).send();
- }
-
- @Test
- public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mBubbleEntry);
- mBubbleController.removeBubble(
- mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
- verify(mDeleteIntent, times(1)).send();
- }
-
- @Test
- public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mBubbleEntry);
- mBubbleController.updateBubble(mBubbleEntry2);
- mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
- verify(mDeleteIntent, times(2)).send();
- }
-
- @Test
- public void testRemoveBubble_noLongerBubbleAfterUpdate()
- throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mBubbleEntry);
- assertTrue(mBubbleController.hasBubbles());
-
- mRow.getSbn().getNotification().flags &= ~FLAG_BUBBLE;
- NotificationListenerService.Ranking ranking = new RankingBuilder(
- mRow.getRanking()).setCanBubble(false).build();
- mRow.setRanking(ranking);
- mEntryListener.onEntryUpdated(mRow);
-
- assertFalse(mBubbleController.hasBubbles());
- verify(mDeleteIntent, never()).send();
- }
-
- @Test
- public void testRemoveBubble_entryListenerRemove() {
- mEntryListener.onEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
-
- assertTrue(mBubbleController.hasBubbles());
-
- // Removes the notification
- mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL);
- assertFalse(mBubbleController.hasBubbles());
- }
-
- @Test
- public void removeBubble_intercepted() {
- mEntryListener.onEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
-
- assertTrue(mBubbleController.hasBubbles());
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
- boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
-
- // Intercept!
- assertTrue(intercepted);
- // Should update show in shade state
- assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
- }
-
- @Test
- public void removeBubble_dismissIntoOverflow_intercepted() {
- mEntryListener.onEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
-
- assertTrue(mBubbleController.hasBubbles());
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
- // Dismiss the bubble
- mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
- assertFalse(mBubbleController.hasBubbles());
-
- // Dismiss the notification
- boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
-
- // Intercept dismissal since bubble is going into overflow
- assertTrue(intercepted);
- }
-
- @Test
- public void removeBubble_notIntercepted() {
- mEntryListener.onEntryAdded(mRow);
- mBubbleController.updateBubble(mBubbleEntry);
-
- assertTrue(mBubbleController.hasBubbles());
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
- // Dismiss the bubble
- mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
- assertFalse(mBubbleController.hasBubbles());
-
- // Dismiss the notification
- boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
-
- // Not a bubble anymore so we don't intercept dismissal.
- assertFalse(intercepted);
- }
-
- @Test
- public void testNotifyShadeSuppressionChange_notificationDismiss() {
- mEntryListener.onEntryAdded(mRow);
-
- assertTrue(mBubbleController.hasBubbles());
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
- mBubblesManager.handleDismissalInterception(mRow);
-
- // Should update show in shade state
- assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-
- // Should notify delegate that shade state changed
- verify(mBubbleController).onBubbleMetadataFlagChanged(
- mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
- }
-
- @Test
- public void testNotifyShadeSuppressionChange_bubbleExpanded() {
- mEntryListener.onEntryAdded(mRow);
-
- assertTrue(mBubbleController.hasBubbles());
- assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
- mBubbleData.setExpanded(true);
-
- // Once a bubble is expanded the notif is suppressed
- assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-
- // Should notify delegate that shade state changed
- verify(mBubbleController).onBubbleMetadataFlagChanged(
- mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
- }
-
- @Test
- public void testBubbleSummaryDismissal_suppressesSummaryAndBubbleFromShade() throws Exception {
- // GIVEN a group summary with a bubble child
- ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
- ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
- mEntryListener.onEntryAdded(groupedBubble.getEntry());
- when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
- .thenReturn(groupedBubble.getEntry());
- groupSummary.addChildNotification(groupedBubble);
- assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
-
- // WHEN the summary is dismissed
- mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
-
- // THEN the summary and bubbled child are suppressed from the shade
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- groupedBubble.getEntry().getKey(),
- groupedBubble.getEntry().getSbn().getGroupKey()));
- assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
- groupedBubble.getEntry().getKey(),
- groupedBubble.getEntry().getSbn().getGroupKey()));
- assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
- }
-
- @Test
- public void testAppRemovesSummary_removesAllBubbleChildren() throws Exception {
- // GIVEN a group summary with a bubble child
- ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
- ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
- mEntryListener.onEntryAdded(groupedBubble.getEntry());
- when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
- .thenReturn(groupedBubble.getEntry());
- groupSummary.addChildNotification(groupedBubble);
- assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
-
- // GIVEN the summary is dismissed
- mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
-
- // WHEN the summary is cancelled by the app
- mEntryListener.onEntryRemoved(groupSummary.getEntry(), REASON_APP_CANCEL);
-
- // THEN the summary and its children are removed from bubble data
- assertFalse(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
- assertFalse(mBubbleData.isSummarySuppressed(
- groupSummary.getEntry().getSbn().getGroupKey()));
- }
-
- @Test
- public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren()
- throws Exception {
- // GIVEN a group summary with two (non-bubble) children and one bubble child
- ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
- ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
- mEntryListener.onEntryAdded(groupedBubble.getEntry());
- when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
- .thenReturn(groupedBubble.getEntry());
- groupSummary.addChildNotification(groupedBubble);
-
- // WHEN the summary is dismissed
- mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
-
- // THEN only the NON-bubble children are dismissed
- List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren();
- verify(mNotifCallback, times(1)).removeNotification(
- eq(childrenRows.get(0).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
- verify(mNotifCallback, times(1)).removeNotification(
- eq(childrenRows.get(1).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
- verify(mNotifCallback, never()).removeNotification(eq(groupedBubble.getEntry()),
- any(), anyInt());
-
- // THEN the bubble child still exists as a bubble and is suppressed from the shade
- assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- groupedBubble.getEntry().getKey(),
- groupedBubble.getEntry().getSbn().getGroupKey()));
- assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
- groupedBubble.getEntry().getKey(),
- groupedBubble.getEntry().getSbn().getGroupKey()));
-
- // THEN the summary is also suppressed from the shade
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- groupSummary.getEntry().getKey(),
- groupSummary.getEntry().getSbn().getGroupKey()));
- assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
- groupSummary.getEntry().getKey(),
- groupSummary.getEntry().getSbn().getGroupKey()));
- }
-
-
- /**
- * Verifies that when the user changes, the bubbles in the overflow list is cleared. Doesn't
- * test the loading from the repository which would be a nice thing to add.
- */
- @Test
- public void testOnUserChanged_overflowState() {
- int firstUserId = mBubbleEntry.getStatusBarNotification().getUser().getIdentifier();
- int secondUserId = mBubbleEntryUser11.getStatusBarNotification().getUser().getIdentifier();
-
- mBubbleController.updateBubble(mBubbleEntry);
- mBubbleController.updateBubble(mBubbleEntry2);
- assertTrue(mBubbleController.hasBubbles());
- mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
-
- // Verify these are in the overflow
- assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey())).isNotNull();
- assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2.getKey())).isNotNull();
-
- // Switch users
- mBubbleController.onUserChanged(secondUserId);
- assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
-
- // Give this user some bubbles
- mBubbleController.updateBubble(mBubbleEntryUser11);
- mBubbleController.updateBubble(mBubbleEntry2User11);
- assertTrue(mBubbleController.hasBubbles());
- mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
-
- // Verify these are in the overflow
- assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntryUser11.getKey())).isNotNull();
- assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2User11.getKey())).isNotNull();
-
- // Would have loaded bubbles twice because of user switch
- verify(mDataRepository, times(2)).loadBubbles(anyInt(), any());
- }
-
- /**
- * Verifies we only load the overflow data once.
- */
- @Test
- public void testOverflowLoadedOnce() {
- // XXX
- when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
- when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
-
- mEntryListener.onEntryAdded(mRow);
- mEntryListener.onEntryAdded(mRow2);
- mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
- assertThat(mBubbleData.getOverflowBubbles()).isNotEmpty();
-
- mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL);
- mEntryListener.onEntryRemoved(mRow2, REASON_APP_CANCEL);
- assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
-
- verify(mDataRepository, times(1)).loadBubbles(anyInt(), any());
- }
-
- /**
- * Verifies that shortcut deletions triggers that bubble being removed from XML.
- */
- @Test
- public void testDeleteShortcutsDeletesXml() throws Exception {
- ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
- BubbleEntry shortcutBubbleEntry = BubblesManager.notifToBubbleEntry(row.getEntry());
- mBubbleController.updateBubble(shortcutBubbleEntry);
-
- mBubbleData.dismissBubbleWithKey(shortcutBubbleEntry.getKey(),
- Bubbles.DISMISS_SHORTCUT_REMOVED);
-
- verify(mDataRepository, atLeastOnce()).removeBubbles(anyInt(), mBubbleListCaptor.capture());
- assertThat(mBubbleListCaptor.getValue().get(0).getKey()).isEqualTo(
- shortcutBubbleEntry.getKey());
- }
-
- @Test
- public void testShowManageMenuChangesSysuiState() {
- mBubbleController.updateBubble(mBubbleEntry);
- assertTrue(mBubbleController.hasBubbles());
-
- // Expand the stack
- BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleData.setExpanded(true);
- assertStackExpanded();
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
- // Show the menu
- stackView.showManageMenu(true);
- assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */);
- }
-
- @Test
- public void testHideManageMenuChangesSysuiState() {
- mBubbleController.updateBubble(mBubbleEntry);
- assertTrue(mBubbleController.hasBubbles());
-
- // Expand the stack
- BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleData.setExpanded(true);
- assertStackExpanded();
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
- // Show the menu
- stackView.showManageMenu(true);
- assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */);
-
- // Hide the menu
- stackView.showManageMenu(false);
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
- @Test
- public void testCollapseBubbleManageMenuChangesSysuiState() {
- mBubbleController.updateBubble(mBubbleEntry);
- assertTrue(mBubbleController.hasBubbles());
-
- // Expand the stack
- BubbleStackView stackView = mBubbleController.getStackView();
- mBubbleData.setExpanded(true);
- assertStackExpanded();
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
- // Show the menu
- stackView.showManageMenu(true);
- assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */);
-
- // Collapse the stack
- mBubbleData.setExpanded(false);
-
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
- }
-
- @Test
- public void testNotificationChannelModified_channelUpdated_removesOverflowBubble()
- throws Exception {
- // Setup
- ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
- NotificationEntry entry = row.getEntry();
- entry.getChannel().setConversationId(
- row.getEntry().getChannel().getParentChannelId(),
- "shortcutId");
- mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry()));
- assertTrue(mBubbleController.hasBubbles());
-
- // Overflow it
- mBubbleData.dismissBubbleWithKey(entry.getKey(),
- Bubbles.DISMISS_USER_GESTURE);
- assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isTrue();
-
- // Test
- entry.getChannel().setDeleted(true);
- mBubbleController.onNotificationChannelModified(entry.getSbn().getPackageName(),
- entry.getSbn().getUser(),
- entry.getChannel(),
- NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
- assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse();
- }
-
- @Test
- public void testNotificationChannelModified_channelDeleted_removesOverflowBubble()
- throws Exception {
- // Setup
- ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
- NotificationEntry entry = row.getEntry();
- entry.getChannel().setConversationId(
- row.getEntry().getChannel().getParentChannelId(),
- "shortcutId");
- mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry()));
- assertTrue(mBubbleController.hasBubbles());
-
- // Overflow it
- mBubbleData.dismissBubbleWithKey(entry.getKey(),
- Bubbles.DISMISS_USER_GESTURE);
- assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isTrue();
-
- // Test
- entry.getChannel().setDeleted(true);
- mBubbleController.onNotificationChannelModified(entry.getSbn().getPackageName(),
- entry.getSbn().getUser(),
- entry.getChannel(),
- NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
- assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse();
- }
-
- @Test
- public void testStackViewOnBackPressed_updatesBubbleDataExpandState() {
- mBubbleController.updateBubble(mBubbleEntry);
-
- // Expand the stack
- mBubbleData.setExpanded(true);
- assertStackExpanded();
-
- // Hit back
- BubbleStackView stackView = mBubbleController.getStackView();
- stackView.onBackPressed();
-
- // Make sure we're collapsed
- assertStackCollapsed();
- }
-
-
- @Test
- public void testRegisterUnregisterBroadcastListener() {
- spyOn(mContext);
- mBubbleController.updateBubble(mBubbleEntry);
- verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
- mFilterArgumentCaptor.capture());
- assertThat(mFilterArgumentCaptor.getValue().getAction(0)).isEqualTo(
- Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- assertThat(mFilterArgumentCaptor.getValue().getAction(1)).isEqualTo(
- Intent.ACTION_SCREEN_OFF);
-
- mBubbleData.dismissBubbleWithKey(mBubbleEntry.getKey(), REASON_APP_CANCEL);
- // TODO: not certain why this isn't called normally when tests are run, perhaps because
- // it's after an animation in BSV. This calls BubbleController#removeFromWindowManagerMaybe
- mBubbleController.onAllBubblesAnimatedOut();
-
- verify(mContext).unregisterReceiver(eq(mBroadcastReceiverArgumentCaptor.getValue()));
- }
-
- @Test
- public void testBroadcastReceiverCloseDialogs_notGestureNav() {
- spyOn(mContext);
- mBubbleController.updateBubble(mBubbleEntry);
- mBubbleData.setExpanded(true);
- verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
- mFilterArgumentCaptor.capture());
- Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i);
-
- assertStackExpanded();
- }
-
- @Test
- public void testBroadcastReceiverCloseDialogs_reasonGestureNav() {
- spyOn(mContext);
- mBubbleController.updateBubble(mBubbleEntry);
- mBubbleData.setExpanded(true);
-
- verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
- mFilterArgumentCaptor.capture());
- Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- i.putExtra("reason", "gestureNav");
- mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i);
- assertStackCollapsed();
- }
-
- @Test
- public void testBroadcastReceiver_screenOff() {
- spyOn(mContext);
- mBubbleController.updateBubble(mBubbleEntry);
- mBubbleData.setExpanded(true);
-
- verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
- mFilterArgumentCaptor.capture());
-
- Intent i = new Intent(Intent.ACTION_SCREEN_OFF);
- mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i);
- assertStackCollapsed();
- }
-
- @Test
- public void testOnStatusBarStateChanged() {
- mBubbleController.updateBubble(mBubbleEntry);
- mBubbleData.setExpanded(true);
- assertStackExpanded();
- BubbleStackView stackView = mBubbleController.getStackView();
- assertThat(stackView.getVisibility()).isEqualTo(View.VISIBLE);
-
- mBubbleController.onStatusBarStateChanged(false);
-
- assertStackCollapsed();
- assertThat(stackView.getVisibility()).isEqualTo(View.INVISIBLE);
-
- mBubbleController.onStatusBarStateChanged(true);
- assertThat(stackView.getVisibility()).isEqualTo(View.VISIBLE);
- }
-
- @Test
- public void testSetShouldAutoExpand_notifiesFlagChanged() {
- mBubbleController.updateBubble(mBubbleEntry);
-
- assertTrue(mBubbleController.hasBubbles());
- Bubble b = mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey());
- assertThat(b.shouldAutoExpand()).isFalse();
-
- // Set it to the same thing
- b.setShouldAutoExpand(false);
-
- // Verify it doesn't notify
- verify(mBubbleController, never()).onBubbleMetadataFlagChanged(any());
-
- // Set it to something different
- b.setShouldAutoExpand(true);
- verify(mBubbleController).onBubbleMetadataFlagChanged(b);
- }
-
- @Test
- public void testUpdateBubble_skipsDndSuppressListNotifs() {
- mBubbleEntry = new BubbleEntry(mRow.getSbn(), mRow.getRanking(), mRow.isDismissable(),
- mRow.shouldSuppressNotificationDot(), true /* DndSuppressNotifFromList */,
- mRow.shouldSuppressPeek());
- mBubbleEntry.getBubbleMetadata().setFlags(
- Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
-
- mBubbleController.updateBubble(mBubbleEntry);
-
- Bubble b = mBubbleData.getPendingBubbleWithKey(mBubbleEntry.getKey());
- assertThat(b.shouldAutoExpand()).isFalse();
- assertThat(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey())).isNull();
- }
-
- @Test
- public void testOnRankingUpdate_DndSuppressListNotif() {
- // It's in the stack
- mBubbleController.updateBubble(mBubbleEntry);
- assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isTrue();
-
- // Set current user profile
- SparseArray<UserInfo> userInfos = new SparseArray<>();
- userInfos.put(mBubbleEntry.getStatusBarNotification().getUser().getIdentifier(),
- mock(UserInfo.class));
- mBubbleController.onCurrentProfilesChanged(userInfos);
-
- // Send ranking update that the notif is suppressed from the list.
- HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey = new HashMap<>();
- mBubbleEntry = new BubbleEntry(mRow.getSbn(), mRow.getRanking(), mRow.isDismissable(),
- mRow.shouldSuppressNotificationDot(), true /* DndSuppressNotifFromList */,
- mRow.shouldSuppressPeek());
- Pair<BubbleEntry, Boolean> pair = new Pair(mBubbleEntry, true);
- entryDataByKey.put(mBubbleEntry.getKey(), pair);
-
- NotificationListenerService.RankingMap rankingMap =
- mock(NotificationListenerService.RankingMap.class);
- when(rankingMap.getOrderedKeys()).thenReturn(new String[] { mBubbleEntry.getKey() });
- mBubbleController.onRankingUpdated(rankingMap, entryDataByKey);
-
- // Should no longer be in the stack
- assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isFalse();
- }
-
- /**
- * Sets the bubble metadata flags for this entry. These flags are normally set by
- * NotificationManagerService when the notification is sent, however, these tests do not
- * go through that path so we set them explicitly when testing.
- */
- private void setMetadataFlags(NotificationEntry entry, int flag, boolean enableFlag) {
- Notification.BubbleMetadata bubbleMetadata =
- entry.getSbn().getNotification().getBubbleMetadata();
- int flags = bubbleMetadata.getFlags();
- if (enableFlag) {
- flags |= flag;
- } else {
- flags &= ~flag;
- }
- bubbleMetadata.setFlags(flags);
- }
-
- /**
- * Asserts that the bubble stack is expanded and also validates the cached state is updated.
- */
- private void assertStackExpanded() {
- assertTrue(mBubbleController.isStackExpanded());
- assertTrue(mBubbleController.getImplCachedState().isStackExpanded());
- }
-
- /**
- * Asserts that the bubble stack is collapsed and also validates the cached state is updated.
- */
- private void assertStackCollapsed() {
- assertFalse(mBubbleController.isStackExpanded());
- assertFalse(mBubbleController.getImplCachedState().isStackExpanded());
- }
-
- /**
- * Asserts that a bubble notification is suppressed from the shade and also validates the cached
- * state is updated.
- */
- private void assertBubbleNotificationSuppressedFromShade(BubbleEntry entry) {
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- entry.getKey(), entry.getGroupKey()));
- assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
- entry.getKey(), entry.getGroupKey()));
- }
-
- /**
- * Asserts that a bubble notification is not suppressed from the shade and also validates the
- * cached state is updated.
- */
- private void assertBubbleNotificationNotSuppressedFromShade(BubbleEntry entry) {
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- entry.getKey(), entry.getGroupKey()));
- assertFalse(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
- entry.getKey(), entry.getGroupKey()));
- }
-
- /**
- * Asserts that the system ui states associated to bubbles are in the correct state.
- */
- private void assertSysuiStates(boolean stackExpanded, boolean manageMenuExpanded) {
- assertThat(mSysUiStateBubblesExpanded).isEqualTo(stackExpanded);
- assertThat(mSysUiStateBubblesManageMenuExpanded).isEqualTo(manageMenuExpanded);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index 17e5778f7aab..f901c327b76e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -38,6 +38,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.sysui.ShellController;
import java.util.Optional;
@@ -48,6 +49,7 @@ public class TestableBubbleController extends BubbleController {
// Let's assume surfaces can be synchronized immediately.
TestableBubbleController(Context context,
+ ShellController shellController,
BubbleData data,
FloatingContentCoordinator floatingContentCoordinator,
BubbleDataRepository dataRepository,
@@ -67,11 +69,12 @@ public class TestableBubbleController extends BubbleController {
Handler shellMainHandler,
TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue) {
- super(context, data, Runnable::run, floatingContentCoordinator, dataRepository,
- statusBarService, windowManager, windowManagerShellWrapper, userManager,
- launcherApps, bubbleLogger, taskStackListener, shellTaskOrganizer, positioner,
- displayController, oneHandedOptional, dragAndDropController, shellMainExecutor,
- shellMainHandler, new SyncExecutor(), taskViewTransitions, syncQueue);
+ super(context, shellController, data, Runnable::run, floatingContentCoordinator,
+ dataRepository, statusBarService, windowManager, windowManagerShellWrapper,
+ userManager, launcherApps, bubbleLogger, taskStackListener, shellTaskOrganizer,
+ positioner, displayController, oneHandedOptional, dragAndDropController,
+ shellMainExecutor, shellMainHandler, new SyncExecutor(), taskViewTransitions,
+ syncQueue);
setInflateSynchronously(true);
initialize();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
index a7f0dc22e849..d80ea154e77a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
public class TestableNotificationInterruptStateProviderImpl
extends NotificationInterruptStateProviderImpl {
@@ -41,6 +42,7 @@ public class TestableNotificationInterruptStateProviderImpl
AmbientDisplayConfiguration ambientDisplayConfiguration,
NotificationFilter filter,
StatusBarStateController statusBarStateController,
+ KeyguardStateController keyguardStateController,
BatteryController batteryController,
HeadsUpManager headsUpManager,
NotificationInterruptLogger logger,
@@ -54,6 +56,7 @@ public class TestableNotificationInterruptStateProviderImpl
filter,
batteryController,
statusBarStateController,
+ keyguardStateController,
headsUpManager,
logger,
mainHandler,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 185942e6fbc8..9c2136675dfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -24,27 +24,22 @@ import android.test.suitebuilder.annotation.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
-import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.tracing.ProtoTracer;
-import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.compatui.CompatUI;
-import com.android.wm.shell.draganddrop.DragAndDrop;
-import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.sysui.ShellInterface;
import org.junit.Before;
import org.junit.Test;
@@ -65,36 +60,30 @@ import java.util.Optional;
public class WMShellTest extends SysuiTestCase {
WMShell mWMShell;
+ @Mock ShellInterface mShellInterface;
@Mock CommandQueue mCommandQueue;
@Mock ConfigurationController mConfigurationController;
@Mock KeyguardStateController mKeyguardStateController;
@Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock NavigationModeController mNavigationModeController;
@Mock ScreenLifecycle mScreenLifecycle;
@Mock SysUiState mSysUiState;
@Mock Pip mPip;
@Mock SplitScreen mSplitScreen;
@Mock OneHanded mOneHanded;
- @Mock HideDisplayCutout mHideDisplayCutout;
@Mock WakefulnessLifecycle mWakefulnessLifecycle;
@Mock ProtoTracer mProtoTracer;
- @Mock ShellCommandHandler mShellCommandHandler;
- @Mock CompatUI mCompatUI;
@Mock UserInfoController mUserInfoController;
@Mock ShellExecutor mSysUiMainExecutor;
- @Mock DragAndDrop mDragAndDrop;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mWMShell = new WMShell(mContext, Optional.of(mPip),
- Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
- Optional.of(mShellCommandHandler), Optional.of(mCompatUI),
- Optional.of(mDragAndDrop),
- mCommandQueue, mConfigurationController, mKeyguardStateController,
- mKeyguardUpdateMonitor, mNavigationModeController, mScreenLifecycle, mSysUiState,
- mProtoTracer, mWakefulnessLifecycle, mUserInfoController, mSysUiMainExecutor);
+ mWMShell = new WMShell(mContext, mShellInterface, Optional.of(mPip),
+ Optional.of(mSplitScreen), Optional.of(mOneHanded), mCommandQueue,
+ mConfigurationController, mKeyguardStateController, mKeyguardUpdateMonitor,
+ mScreenLifecycle, mSysUiState, mProtoTracer, mWakefulnessLifecycle,
+ mUserInfoController, mSysUiMainExecutor);
}
@Test
@@ -105,35 +94,12 @@ public class WMShellTest extends SysuiTestCase {
}
@Test
- public void initSplitScreen_registersCallbacks() {
- mWMShell.initSplitScreen(mSplitScreen);
-
- verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
- }
-
- @Test
public void initOneHanded_registersCallbacks() {
mWMShell.initOneHanded(mOneHanded);
- verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class));
verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback.class));
verify(mOneHanded).registerEventCallback(any(OneHandedEventCallback.class));
}
-
- @Test
- public void initHideDisplayCutout_registersCallbacks() {
- mWMShell.initHideDisplayCutout(mHideDisplayCutout);
-
- verify(mConfigurationController).addCallback(
- any(ConfigurationController.ConfigurationListener.class));
- }
-
- @Test
- public void initCompatUI_registersCallbacks() {
- mWMShell.initCompatUi(mCompatUI);
-
- verify(mKeyguardStateController).addCallback(any(KeyguardStateController.Callback.class));
- }
}
diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp
new file mode 100644
index 000000000000..108295b90e58
--- /dev/null
+++ b/packages/SystemUI/unfold/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+android_library {
+ name: "SystemUIUnfoldLib",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ "src/**/*.aidl",
+ ],
+ static_libs: [
+ "androidx.dynamicanimation_dynamicanimation",
+ "dagger2",
+ "jsr330",
+ ],
+ java_version: "1.8",
+ min_sdk_version: "current",
+ plugins: ["dagger2-compiler"],
+}
diff --git a/libs/WindowManager/Shell/res/color/unfold_transition_background.xml b/packages/SystemUI/unfold/AndroidManifest.xml
index 63289a3f75d9..ee8afe1aff5b 100644
--- a/libs/WindowManager/Shell/res/color/unfold_transition_background.xml
+++ b/packages/SystemUI/unfold/AndroidManifest.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
+<!--
+ Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,7 +14,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- Matches taskbar color -->
- <item android:color="@android:color/system_neutral2_500" android:lStar="35" />
-</selector>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.systemui.unfold">
+
+
+</manifest>
diff --git a/packages/SystemUI/unfold/lint-baseline.xml b/packages/SystemUI/unfold/lint-baseline.xml
new file mode 100644
index 000000000000..449ed2e60853
--- /dev/null
+++ b/packages/SystemUI/unfold/lint-baseline.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" name="" variant="all" version="7.1.0-dev">
+</issues>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
index 9e5aeb84b624..a5ec0a454412 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -16,16 +16,16 @@
package com.android.systemui.unfold
-import android.app.ActivityManager
import android.content.ContentResolver
import android.content.Context
import android.hardware.SensorManager
-import android.hardware.devicestate.DeviceStateManager
import android.os.Handler
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.dagger.UnfoldBackground
+import com.android.systemui.unfold.dagger.UnfoldMain
+import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
+import com.android.systemui.unfold.util.CurrentActivityTypeProvider
import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
import dagger.BindsInstance
import dagger.Component
@@ -51,12 +51,12 @@ internal interface UnfoldSharedComponent {
@BindsInstance context: Context,
@BindsInstance config: UnfoldTransitionConfig,
@BindsInstance screenStatusProvider: ScreenStatusProvider,
- @BindsInstance deviceStateManager: DeviceStateManager,
- @BindsInstance activityManager: ActivityManager,
+ @BindsInstance foldProvider: FoldProvider,
+ @BindsInstance activityTypeProvider: CurrentActivityTypeProvider,
@BindsInstance sensorManager: SensorManager,
- @BindsInstance @Main handler: Handler,
- @BindsInstance @Main executor: Executor,
- @BindsInstance @UiBackground backgroundExecutor: Executor,
+ @BindsInstance @UnfoldMain handler: Handler,
+ @BindsInstance @UnfoldMain executor: Executor,
+ @BindsInstance @UnfoldBackground backgroundExecutor: Executor,
@BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
@BindsInstance contentResolver: ContentResolver = context.contentResolver
): UnfoldSharedComponent
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
index c612995241ef..8f4ee4dc9838 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -17,8 +17,8 @@
package com.android.systemui.unfold
import android.hardware.SensorManager
-import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.dagger.UnfoldBackground
import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
@@ -70,7 +70,7 @@ class UnfoldSharedModule {
fun hingeAngleProvider(
config: UnfoldTransitionConfig,
sensorManager: SensorManager,
- @UiBackground executor: Executor
+ @UnfoldBackground executor: Executor
): HingeAngleProvider =
if (config.isHingeAngleEnabled) {
HingeSensorAngleProvider(sensorManager, executor)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index cc56007c431a..402dd8474bc4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -17,14 +17,13 @@
package com.android.systemui.unfold
-import android.app.ActivityManager
import android.content.Context
import android.hardware.SensorManager
-import android.hardware.devicestate.DeviceStateManager
import android.os.Handler
-import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
+import com.android.systemui.unfold.util.CurrentActivityTypeProvider
import java.util.concurrent.Executor
/**
@@ -39,8 +38,8 @@ fun createUnfoldTransitionProgressProvider(
context: Context,
config: UnfoldTransitionConfig,
screenStatusProvider: ScreenStatusProvider,
- deviceStateManager: DeviceStateManager,
- activityManager: ActivityManager,
+ foldProvider: FoldProvider,
+ activityTypeProvider: CurrentActivityTypeProvider,
sensorManager: SensorManager,
mainHandler: Handler,
mainExecutor: Executor,
@@ -52,8 +51,8 @@ fun createUnfoldTransitionProgressProvider(
context,
config,
screenStatusProvider,
- deviceStateManager,
- activityManager,
+ foldProvider,
+ activityTypeProvider,
sensorManager,
mainHandler,
mainExecutor,
@@ -64,5 +63,3 @@ fun createUnfoldTransitionProgressProvider(
?: throw IllegalStateException(
"Trying to create " +
"UnfoldTransitionProgressProvider when the transition is disabled")
-
-fun createConfig(context: Context): UnfoldTransitionConfig = ResourceUnfoldTransitionConfig(context)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
index 409dc95ab131..d54481c72bfd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
@@ -15,9 +15,9 @@
*/
package com.android.systemui.unfold
-import android.annotation.FloatRange
-import com.android.systemui.statusbar.policy.CallbackController
+import androidx.annotation.FloatRange
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.util.CallbackController
/**
* Interface that allows to receive unfold transition progress updates.
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt
new file mode 100644
index 000000000000..2044f05664d0
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.compat
+
+import android.content.Context
+import android.content.res.Configuration
+import com.android.systemui.unfold.updates.FoldProvider
+import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
+import java.util.concurrent.Executor
+
+/**
+ * Fold provider that notifies about fold state based on the screen size
+ * It could be used when no activity context is available
+ * TODO(b/232369816): use Jetpack WM library when non-activity contexts supported b/169740873
+ */
+class ScreenSizeFoldProvider(private val context: Context) : FoldProvider {
+
+ private var callbacks: MutableList<FoldCallback> = arrayListOf()
+ private var lastWidth: Int = 0
+
+ override fun registerCallback(callback: FoldCallback, executor: Executor) {
+ callbacks += callback
+ onConfigurationChange(context.resources.configuration)
+ }
+
+ override fun unregisterCallback(callback: FoldCallback) {
+ callbacks -= callback
+ }
+
+ fun onConfigurationChange(newConfig: Configuration) {
+ if (lastWidth == newConfig.smallestScreenWidthDp) {
+ return
+ }
+
+ if (newConfig.smallestScreenWidthDp > INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP) {
+ callbacks.forEach { it.onFoldUpdated(false) }
+ } else {
+ callbacks.forEach { it.onFoldUpdated(true) }
+ }
+ lastWidth = newConfig.smallestScreenWidthDp
+ }
+}
+
+private const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt
new file mode 100644
index 000000000000..c405f3110297
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.compat
+
+import com.android.systemui.unfold.updates.FoldProvider
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
+import java.util.concurrent.Executor
+
+class SizeScreenStatusProvider(
+ private val foldProvider: FoldProvider,
+ private val executor: Executor
+) : ScreenStatusProvider {
+
+ private val listeners: MutableList<ScreenListener> = arrayListOf()
+ private val callback = object : FoldProvider.FoldCallback {
+ override fun onFoldUpdated(isFolded: Boolean) {
+ if (!isFolded) {
+ listeners.forEach { it.onScreenTurnedOn() }
+ }
+ }
+ }
+
+ fun start() {
+ foldProvider.registerCallback(
+ callback,
+ executor
+ )
+ }
+
+ fun stop() {
+ foldProvider.unregisterCallback(callback)
+ }
+
+ override fun addCallback(listener: ScreenListener) {
+ listeners.add(listener)
+ }
+
+ override fun removeCallback(listener: ScreenListener) {
+ listeners.remove(listener)
+ }
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
new file mode 100644
index 000000000000..c51372975a67
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.config
+
+import android.content.res.Resources
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class ResourceUnfoldTransitionConfig @Inject constructor() : UnfoldTransitionConfig {
+
+ override val isEnabled: Boolean by lazy {
+ val id = Resources.getSystem()
+ .getIdentifier("config_unfoldTransitionEnabled", "bool", "android")
+ Resources.getSystem().getBoolean(id)
+ }
+
+ override val isHingeAngleEnabled: Boolean by lazy {
+ val id = Resources.getSystem()
+ .getIdentifier("config_unfoldTransitionHingeAngle", "bool", "android")
+ Resources.getSystem().getBoolean(id)
+ }
+
+ override val halfFoldedTimeoutMillis: Int by lazy {
+ val id = Resources.getSystem()
+ .getIdentifier("config_unfoldTransitionHalfFoldedTimeout", "integer", "android")
+ Resources.getSystem().getInteger(id)
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
index 5b187b3486c6..765e862aa00d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
@@ -18,4 +18,5 @@ package com.android.systemui.unfold.config
interface UnfoldTransitionConfig {
val isEnabled: Boolean
val isHingeAngleEnabled: Boolean
+ val halfFoldedTimeoutMillis: Int
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt
new file mode 100644
index 000000000000..60747954dac3
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.dagger
+
+import javax.inject.Qualifier
+
+/**
+ * Alternative to [UiBackground] qualifier annotation in unfold module.
+ * It is needed as we can't depend on SystemUI code in this module.
+ */
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+annotation class UnfoldBackground
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt
new file mode 100644
index 000000000000..5553690fb562
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.dagger
+
+import javax.inject.Qualifier
+
+/**
+ * Alternative to [Main] qualifier annotation in unfold module.
+ * It is needed as we can't depend on SystemUI code in this module.
+ */
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+annotation class UnfoldMain
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
index 4c85b055aeae..4c85b055aeae 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index 04d920cb15d5..2ab28c65f32f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -16,7 +16,6 @@
package com.android.systemui.unfold.progress
import android.util.Log
-import android.util.MathUtils.saturate
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringAnimation
@@ -70,6 +69,9 @@ class PhysicsBasedUnfoldTransitionProgressProvider(
springAnimation.animateToFinalPosition(progress)
}
+ private fun saturate(amount: Float, low: Float = 0f, high: Float = 1f): Float =
+ if (amount < low) low else if (amount > high) high else amount
+
override fun onFoldUpdate(@FoldUpdate update: Int) {
when (update) {
FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 14581ccd5c0a..e8038fd7dfa6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -15,46 +15,46 @@
*/
package com.android.systemui.unfold.updates
-import android.annotation.FloatRange
-import android.app.ActivityManager
-import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
-import android.content.Context
-import android.hardware.devicestate.DeviceStateManager
import android.os.Handler
import android.util.Log
+import androidx.annotation.FloatRange
import androidx.annotation.VisibleForTesting
import androidx.core.util.Consumer
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.dagger.UnfoldMain
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
import com.android.systemui.unfold.updates.hinge.FULLY_CLOSED_DEGREES
import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
+import com.android.systemui.unfold.util.CurrentActivityTypeProvider
import java.util.concurrent.Executor
import javax.inject.Inject
class DeviceFoldStateProvider
@Inject
constructor(
- context: Context,
+ config: UnfoldTransitionConfig,
private val hingeAngleProvider: HingeAngleProvider,
private val screenStatusProvider: ScreenStatusProvider,
- private val deviceStateManager: DeviceStateManager,
- private val activityManager: ActivityManager,
- @Main private val mainExecutor: Executor,
- @Main private val handler: Handler
+ private val foldProvider: FoldProvider,
+ private val activityTypeProvider: CurrentActivityTypeProvider,
+ @UnfoldMain private val mainExecutor: Executor,
+ @UnfoldMain private val handler: Handler
) : FoldStateProvider {
private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
- @FoldUpdate private var lastFoldUpdate: Int? = null
+ @FoldUpdate
+ private var lastFoldUpdate: Int? = null
- @FloatRange(from = 0.0, to = 180.0) private var lastHingeAngle: Float = 0f
+ @FloatRange(from = 0.0, to = 180.0)
+ private var lastHingeAngle: Float = 0f
private val hingeAngleListener = HingeAngleListener()
private val screenListener = ScreenStatusListener()
- private val foldStateListener = FoldStateListener(context)
+ private val foldStateListener = FoldStateListener()
private val timeoutRunnable = TimeoutRunnable()
/**
@@ -62,22 +62,20 @@ constructor(
* [FOLD_UPDATE_START_CLOSING] or [FOLD_UPDATE_START_OPENING] event, if an end state is not
* reached.
*/
- private val halfOpenedTimeoutMillis: Int =
- context.resources.getInteger(
- com.android.internal.R.integer.config_unfoldTransitionHalfFoldedTimeout)
+ private val halfOpenedTimeoutMillis: Int = config.halfFoldedTimeoutMillis
private var isFolded = false
private var isUnfoldHandled = true
override fun start() {
- deviceStateManager.registerCallback(mainExecutor, foldStateListener)
+ foldProvider.registerCallback(foldStateListener, mainExecutor)
screenStatusProvider.addCallback(screenListener)
hingeAngleProvider.addCallback(hingeAngleListener)
}
override fun stop() {
screenStatusProvider.removeCallback(screenListener)
- deviceStateManager.unregisterCallback(foldStateListener)
+ foldProvider.unregisterCallback(foldStateListener)
hingeAngleProvider.removeCallback(hingeAngleListener)
hingeAngleProvider.stop()
}
@@ -92,13 +90,13 @@ constructor(
override val isFinishedOpening: Boolean
get() = !isFolded &&
- (lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN ||
- lastFoldUpdate == FOLD_UPDATE_FINISH_HALF_OPEN)
+ (lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN ||
+ lastFoldUpdate == FOLD_UPDATE_FINISH_HALF_OPEN)
private val isTransitionInProgress: Boolean
get() =
lastFoldUpdate == FOLD_UPDATE_START_OPENING ||
- lastFoldUpdate == FOLD_UPDATE_START_CLOSING
+ lastFoldUpdate == FOLD_UPDATE_START_CLOSING
private fun onHingeAngle(angle: Float) {
if (DEBUG) {
@@ -136,39 +134,36 @@ constructor(
* apps that support table-top/HALF_FOLDED mode. Only for launcher, there is no threshold.
*/
private fun getClosingThreshold(): Int? {
- val activityType =
- activityManager.getRunningTasks(/* maxNum= */ 1)?.getOrNull(0)?.topActivityType
- ?: return null
+ val isHomeActivity = activityTypeProvider.isHomeActivity ?: return null
if (DEBUG) {
- Log.d(TAG, "activityType=" + activityType)
+ Log.d(TAG, "isHomeActivity=$isHomeActivity")
}
- return if (activityType == ACTIVITY_TYPE_HOME) {
+ return if (isHomeActivity) {
null
} else {
START_CLOSING_ON_APPS_THRESHOLD_DEGREES
}
}
- private inner class FoldStateListener(context: Context) :
- DeviceStateManager.FoldStateListener(
- context,
- { folded: Boolean ->
- isFolded = folded
- lastHingeAngle = FULLY_CLOSED_DEGREES
-
- if (folded) {
- hingeAngleProvider.stop()
- notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
- cancelTimeout()
- isUnfoldHandled = false
- } else {
- notifyFoldUpdate(FOLD_UPDATE_START_OPENING)
- rescheduleAbortAnimationTimeout()
- hingeAngleProvider.start()
- }
- })
+ private inner class FoldStateListener : FoldProvider.FoldCallback {
+ override fun onFoldUpdated(isFolded: Boolean) {
+ this@DeviceFoldStateProvider.isFolded = isFolded
+ lastHingeAngle = FULLY_CLOSED_DEGREES
+
+ if (isFolded) {
+ hingeAngleProvider.stop()
+ notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
+ cancelTimeout()
+ isUnfoldHandled = false
+ } else {
+ notifyFoldUpdate(FOLD_UPDATE_START_OPENING)
+ rescheduleAbortAnimationTimeout()
+ hingeAngleProvider.start()
+ }
+ }
+ }
private fun notifyFoldUpdate(@FoldUpdate update: Int) {
if (DEBUG) {
@@ -234,7 +229,9 @@ private const val TAG = "DeviceFoldProvider"
private const val DEBUG = false
/** Threshold after which we consider the device fully unfolded. */
-@VisibleForTesting const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
+@VisibleForTesting
+const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
/** Fold animation on top of apps only when the angle exceeds this threshold. */
-@VisibleForTesting const val START_CLOSING_ON_APPS_THRESHOLD_DEGREES = 60
+@VisibleForTesting
+const val START_CLOSING_ON_APPS_THRESHOLD_DEGREES = 60
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldProvider.kt
new file mode 100644
index 000000000000..6e87beeb295f
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldProvider.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.updates
+
+import java.util.concurrent.Executor
+
+interface FoldProvider {
+ fun registerCallback(callback: FoldCallback, executor: Executor)
+ fun unregisterCallback(callback: FoldCallback)
+
+ interface FoldCallback {
+ fun onFoldUpdated(isFolded: Boolean)
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index 14a3a70fc6b0..c7a8bf336777 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -15,10 +15,10 @@
*/
package com.android.systemui.unfold.updates
-import android.annotation.FloatRange
-import android.annotation.IntDef
-import com.android.systemui.statusbar.policy.CallbackController
+import androidx.annotation.FloatRange
+import androidx.annotation.IntDef
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
+import com.android.systemui.unfold.util.CallbackController
/**
* Allows to subscribe to main events related to fold/unfold process such as hinge angle update,
@@ -36,7 +36,6 @@ interface FoldStateProvider : CallbackController<FoldUpdatesListener> {
}
@IntDef(
- prefix = ["FOLD_UPDATE_"],
value =
[
FOLD_UPDATE_START_OPENING,
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
new file mode 100644
index 000000000000..e985506bd989
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.updates.hinge
+
+import androidx.core.util.Consumer
+
+internal object EmptyHingeAngleProvider : HingeAngleProvider {
+ override fun start() {}
+
+ override fun stop() {}
+
+ override fun removeCallback(listener: Consumer<Float>) {}
+
+ override fun addCallback(listener: Consumer<Float>) {}
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
new file mode 100644
index 000000000000..e464c3f81546
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.updates.hinge
+
+import androidx.core.util.Consumer
+import com.android.systemui.unfold.util.CallbackController
+
+/**
+ * Emits device hinge angle values (angle between two integral parts of the device).
+ *
+ * The hinge angle could be from 0 to 360 degrees inclusive. For foldable devices usually 0
+ * corresponds to fully closed (folded) state and 180 degrees corresponds to fully open (flat)
+ * state.
+ */
+interface HingeAngleProvider : CallbackController<Consumer<Float>> {
+ fun start()
+ fun stop()
+}
+
+const val FULLY_OPEN_DEGREES = 180f
+const val FULLY_CLOSED_DEGREES = 0f
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
index c93412b53817..3fc5d610dc2d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
@@ -1,3 +1,17 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.updates.hinge
import android.hardware.Sensor
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
index 668c69442cac..d95e050474de 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
@@ -15,8 +15,8 @@
*/
package com.android.systemui.unfold.updates.screen
-import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
+import com.android.systemui.unfold.util.CallbackController
interface ScreenStatusProvider : CallbackController<ScreenListener> {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
index 1574c8d37ab1..d8bc01804f14 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt
@@ -1,3 +1,17 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.util
import android.os.Trace
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CallbackController.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CallbackController.kt
new file mode 100644
index 000000000000..46ad534722cd
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CallbackController.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.util
+
+interface CallbackController<T> {
+ fun addCallback(listener: T)
+ fun removeCallback(listener: T)
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt
new file mode 100644
index 000000000000..d0e6cdc9a3c6
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.util
+
+interface CurrentActivityTypeProvider {
+ val isHomeActivity: Boolean?
+}
+
+class EmptyCurrentActivityTypeProvider(override val isHomeActivity: Boolean? = null) :
+ CurrentActivityTypeProvider
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
index dfe87921dd42..5c92b3499835 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
@@ -1,3 +1,17 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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.unfold.util
import android.animation.ValueAnimator
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
index 8491f832b740..8491f832b740 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index dfa34bb50805..0a4ecb227548 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -286,10 +286,18 @@ message SystemMessage {
// Package: android
NOTE_MTE_OVERRIDE_ENABLED = 69;
+ // Notify the user that this is a guest session with information
+ // about first login and ephemeral state
+ // Package: android
+ NOTE_GUEST_SESSION = 70;
+
// Inform the user of notification permissions changes.
// Package: android
NOTE_REVIEW_NOTIFICATION_PERMISSIONS = 71;
+ // Notify the user to setup their dock
+ NOTE_SETUP_DOCK = 72;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 966d887d11f7..dc39b01cf6b6 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -184,7 +184,12 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
mPanningScalingState.mScrollGestureDetector.onTouchEvent(event);
mPanningScalingState.mScaleGestureDetector.onTouchEvent(event);
- stateHandler.onMotionEvent(event, rawEvent, policyFlags);
+ try {
+ stateHandler.onMotionEvent(event, rawEvent, policyFlags);
+ } catch (GestureException e) {
+ Slog.e(mLogTag, "Error processing motion event", e);
+ clearAndTransitionToStateDetecting();
+ }
}
@Override
@@ -281,7 +286,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
interface State {
- void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags);
+ void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)
+ throws GestureException;
default void clear() {}
@@ -439,7 +445,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
private boolean mLastMoveOutsideMagnifiedRegion;
@Override
- public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)
+ throws GestureException {
final int action = event.getActionMasked();
switch (action) {
case ACTION_POINTER_DOWN: {
@@ -449,7 +456,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
break;
case ACTION_MOVE: {
if (event.getPointerCount() != 1) {
- throw new IllegalStateException("Should have one pointer down.");
+ throw new GestureException("Should have one pointer down.");
}
final float eventX = event.getX();
final float eventY = event.getY();
@@ -475,7 +482,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
case ACTION_DOWN:
case ACTION_POINTER_UP: {
- throw new IllegalArgumentException(
+ throw new GestureException(
"Unexpected event type: " + MotionEvent.actionToString(action));
}
}
@@ -1087,4 +1094,13 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
mGestureHandler.mDetectingState.setShortcutTriggered(false);
}
}
+
+ /**
+ * Indicates an error with a gesture handler or state.
+ */
+ private static class GestureException extends Exception {
+ GestureException(String message) {
+ super(message);
+ }
+ }
}
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 1af8ad344190..84707a8d9c00 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -398,18 +398,7 @@ public class AppPredictionPerUserService extends
final IBinder.DeathRecipient mDeathRecipient;
private final RemoteCallbackList<IPredictionCallback> mCallbacks =
- new RemoteCallbackList<IPredictionCallback>() {
- @Override
- public void onCallbackDied(IPredictionCallback callback) {
- if (DEBUG) {
- Slog.d(TAG, "Binder died for session Id=" + mSessionId
- + " and callback=" + callback.asBinder());
- }
- if (mCallbacks.getRegisteredCallbackCount() == 0) {
- destroy();
- }
- }
- };
+ new RemoteCallbackList<>();
AppPredictionSessionInfo(
@NonNull final AppPredictionSessionId id,
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 8fe57e18ea37..4892718d6203 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -123,6 +123,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.IRemoteViewsFactory;
import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
import com.android.server.WidgetBackupProvider;
import org.xmlpull.v1.XmlPullParser;
@@ -266,7 +267,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
mDevicePolicyManagerInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mSaveStateHandler = BackgroundThread.getHandler();
- mCallbackHandler = new CallbackHandler(mContext.getMainLooper());
+ final ServiceThread serviceThread = new ServiceThread(TAG,
+ android.os.Process.THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
+ serviceThread.start();
+ mCallbackHandler = new CallbackHandler(serviceThread.getLooper());
mBackupRestoreController = new BackupRestoreController();
mSecurityPolicy = new SecurityPolicy();
mIsProviderInfoPersisted = !ActivityManager.isLowRamDeviceStatic()
@@ -307,26 +311,26 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
packageFilter.addDataScheme("package");
mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
- packageFilter, null, null);
+ packageFilter, null, mCallbackHandler);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
- sdFilter, null, null);
+ sdFilter, null, mCallbackHandler);
IntentFilter offModeFilter = new IntentFilter();
offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
- offModeFilter, null, null);
+ offModeFilter, null, mCallbackHandler);
IntentFilter suspendPackageFilter = new IntentFilter();
suspendPackageFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
suspendPackageFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
- suspendPackageFilter, null, null);
+ suspendPackageFilter, null, mCallbackHandler);
}
private void registerOnCrossProfileProvidersChangedListener() {
@@ -1218,11 +1222,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
try {
// Ask ActivityManager to bind it. Notice that we are binding the service with the
// caller app instead of DevicePolicyManagerService.
- if(ActivityManager.getService().bindService(
+ if (ActivityManager.getService().bindService(
caller, activtiyToken, intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- connection, flags, mContext.getOpPackageName(),
- widget.provider.getUserId()) != 0) {
+ connection, flags & (Context.BIND_AUTO_CREATE
+ | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE),
+ mContext.getOpPackageName(), widget.provider.getUserId()) != 0) {
// Add it to the mapping of RemoteViewsService to appWidgetIds so that we
// can determine when we can call back to the RemoteViewsService later to
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
index 229799a8457d..d5991d3930a8 100644
--- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
+++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
@@ -73,6 +73,9 @@ class AssociationStoreImpl implements AssociationStore {
private final Set<OnChangeListener> mListeners = new LinkedHashSet<>();
void addAssociation(@NonNull AssociationInfo association) {
+ // Validity check first.
+ checkNotRevoked(association);
+
final int id = association.getId();
if (DEBUG) {
@@ -99,6 +102,9 @@ class AssociationStoreImpl implements AssociationStore {
}
void updateAssociation(@NonNull AssociationInfo updated) {
+ // Validity check first.
+ checkNotRevoked(updated);
+
final int id = updated.getId();
if (DEBUG) {
@@ -292,6 +298,9 @@ class AssociationStoreImpl implements AssociationStore {
}
void setAssociations(Collection<AssociationInfo> allAssociations) {
+ // Validity check first.
+ allAssociations.forEach(AssociationStoreImpl::checkNotRevoked);
+
if (DEBUG) {
Log.i(TAG, "setAssociations() n=" + allAssociations.size());
final StringJoiner stringJoiner = new StringJoiner(", ");
@@ -324,4 +333,11 @@ class AssociationStoreImpl implements AssociationStore {
mAddressMap.clear();
mCachedPerUser.clear();
}
+
+ private static void checkNotRevoked(@NonNull AssociationInfo association) {
+ if (association.isRevoked()) {
+ throw new IllegalArgumentException(
+ "Revoked (removed) associations MUST NOT appear in the AssociationStore");
+ }
+ }
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 2714addaec9e..abc49372053e 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -18,6 +18,7 @@
package com.android.server.companion;
import static android.Manifest.permission.MANAGE_COMPANION_DEVICES;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.SYSTEM_UID;
@@ -48,6 +49,8 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.NotificationManager;
@@ -91,6 +94,7 @@ import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.infra.PerUser;
import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
@@ -105,6 +109,7 @@ import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -128,6 +133,9 @@ public class CompanionDeviceManagerService extends SystemService {
private static final long ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT = DAYS.toMillis(90);
+ private final ActivityManager mActivityManager;
+ private final OnPackageVisibilityChangeListener mOnPackageVisibilityChangeListener;
+
private PersistentDataStore mPersistentStore;
private final PersistUserStateHandler mUserPersistenceHandler;
@@ -152,12 +160,40 @@ public class CompanionDeviceManagerService extends SystemService {
@GuardedBy("mPreviouslyUsedIds")
private final SparseArray<Map<String, Set<Integer>>> mPreviouslyUsedIds = new SparseArray<>();
+ /**
+ * A structure that consists of a set of revoked associations that pending for role holder
+ * removal per each user.
+ *
+ * @see #maybeRemoveRoleHolderForAssociation(AssociationInfo)
+ * @see #addToPendingRoleHolderRemoval(AssociationInfo)
+ * @see #removeFromPendingRoleHolderRemoval(AssociationInfo)
+ * @see #getPendingRoleHolderRemovalAssociationsForUser(int)
+ */
+ @GuardedBy("mRevokedAssociationsPendingRoleHolderRemoval")
+ private final PerUserAssociationSet mRevokedAssociationsPendingRoleHolderRemoval =
+ new PerUserAssociationSet();
+ /**
+ * Contains uid-s of packages pending to be removed from the role holder list (after
+ * revocation of an association), which will happen one the package is no longer visible to the
+ * user.
+ * For quicker uid -> (userId, packageName) look-up this is not a {@code Set<Integer>} but
+ * a {@code Map<Integer, String>} which maps uid-s to packageName-s (userId-s can be derived
+ * from uid-s using {@link UserHandle#getUserId(int)}).
+ *
+ * @see #maybeRemoveRoleHolderForAssociation(AssociationInfo)
+ * @see #addToPendingRoleHolderRemoval(AssociationInfo)
+ * @see #removeFromPendingRoleHolderRemoval(AssociationInfo)
+ */
+ @GuardedBy("mRevokedAssociationsPendingRoleHolderRemoval")
+ private final Map<Integer, String> mUidsPendingRoleHolderRemoval = new HashMap<>();
+
private final RemoteCallbackList<IOnAssociationsChangedListener> mListeners =
new RemoteCallbackList<>();
public CompanionDeviceManagerService(Context context) {
super(context);
+ mActivityManager = context.getSystemService(ActivityManager.class);
mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class);
mAppOpsManager = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
@@ -168,6 +204,9 @@ public class CompanionDeviceManagerService extends SystemService {
mUserPersistenceHandler = new PersistUserStateHandler();
mAssociationStore = new AssociationStoreImpl();
+
+ mOnPackageVisibilityChangeListener =
+ new OnPackageVisibilityChangeListener(mActivityManager);
}
@Override
@@ -204,7 +243,33 @@ public class CompanionDeviceManagerService extends SystemService {
mUserManager.getAliveUsers(), allAssociations, mPreviouslyUsedIds);
}
- mAssociationStore.setAssociations(allAssociations);
+ final Set<AssociationInfo> activeAssociations =
+ new ArraySet<>(/* capacity */ allAssociations.size());
+ // A set contains the userIds that need to persist state after remove the app
+ // from the list of role holders.
+ final Set<Integer> usersToPersistStateFor = new ArraySet<>();
+
+ for (AssociationInfo association : allAssociations) {
+ if (!association.isRevoked()) {
+ activeAssociations.add(association);
+ } else if (maybeRemoveRoleHolderForAssociation(association)) {
+ // Nothing more to do here, but we'll need to persist all the associations to the
+ // disk afterwards.
+ usersToPersistStateFor.add(association.getUserId());
+ } else {
+ addToPendingRoleHolderRemoval(association);
+ }
+ }
+
+ mAssociationStore.setAssociations(activeAssociations);
+
+ // IMPORTANT: only do this AFTER mAssociationStore.setAssociations(), because
+ // persistStateForUser() queries AssociationStore.
+ // (If persistStateForUser() is invoked before mAssociationStore.setAssociations() it
+ // would effectively just clear-out all the persisted associations).
+ for (int userId : usersToPersistStateFor) {
+ persistStateForUser(userId);
+ }
}
@Override
@@ -354,10 +419,18 @@ public class CompanionDeviceManagerService extends SystemService {
}
private void persistStateForUser(@UserIdInt int userId) {
- final List<AssociationInfo> updatedAssociations =
- mAssociationStore.getAssociationsForUser(userId);
+ // We want to store both active associations and the revoked (removed) association that we
+ // are keeping around for the final clean-up (delayed role holder removal).
+ final List<AssociationInfo> allAssociations;
+ // Start with the active associations - these we can get from the AssociationStore.
+ allAssociations = new ArrayList<>(
+ mAssociationStore.getAssociationsForUser(userId));
+ // ... and add the revoked (removed) association, that are yet to be permanently removed.
+ allAssociations.addAll(getPendingRoleHolderRemovalAssociationsForUser(userId));
+
final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUser(userId);
- mPersistentStore.persistStateForUser(userId, updatedAssociations, usedIdsForUser);
+
+ mPersistentStore.persistStateForUser(userId, allAssociations, usedIdsForUser);
}
private void notifyListeners(
@@ -425,13 +498,17 @@ public class CompanionDeviceManagerService extends SystemService {
removalWindow = ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT;
}
- for (AssociationInfo ai : mAssociationStore.getAssociations()) {
- if (!ai.isSelfManaged()) continue;
- final boolean isInactive = currentTime - ai.getLastTimeConnectedMs() >= removalWindow;
- if (isInactive) {
- Slog.i(TAG, "Removing inactive self-managed association: " + ai.getId());
- disassociateInternal(ai.getId());
- }
+ for (AssociationInfo association : mAssociationStore.getAssociations()) {
+ if (!association.isSelfManaged()) continue;
+
+ final boolean isInactive =
+ currentTime - association.getLastTimeConnectedMs() >= removalWindow;
+ if (!isInactive) continue;
+
+ final int id = association.getId();
+
+ Slog.i(TAG, "Removing inactive self-managed association id=" + id);
+ disassociateInternal(id);
}
}
@@ -671,7 +748,7 @@ public class CompanionDeviceManagerService extends SystemService {
enforceCallerIsSystemOr(userId, packageName);
AssociationInfo association = mAssociationStore.getAssociationsForPackageWithAddress(
- userId, packageName, deviceAddress);
+ userId, packageName, deviceAddress);
if (association == null) {
throw new RemoteException(new DeviceNotAssociatedException("App " + packageName
@@ -731,7 +808,7 @@ public class CompanionDeviceManagerService extends SystemService {
enforceUsesCompanionDeviceFeature(getContext(), userId, callingPackage);
checkState(!ArrayUtils.isEmpty(
- mAssociationStore.getAssociationsForPackage(userId, callingPackage)),
+ mAssociationStore.getAssociationsForPackage(userId, callingPackage)),
"App must have an association before calling this API");
}
@@ -791,8 +868,8 @@ public class CompanionDeviceManagerService extends SystemService {
final long timestamp = System.currentTimeMillis();
final AssociationInfo association = new AssociationInfo(id, userId, packageName,
- macAddress, displayName, deviceProfile, selfManaged, false, timestamp,
- Long.MAX_VALUE);
+ macAddress, displayName, deviceProfile, selfManaged,
+ /* notifyOnDeviceNearby */ false, /* revoked */ false, timestamp, Long.MAX_VALUE);
Slog.i(TAG, "New CDM association created=" + association);
mAssociationStore.addAssociation(association);
@@ -804,6 +881,11 @@ public class CompanionDeviceManagerService extends SystemService {
updateSpecialAccessPermissionForAssociatedPackage(association);
logCreateAssociation(deviceProfile);
+
+ // Don't need to update the mRevokedAssociationsPendingRoleHolderRemoval since
+ // maybeRemoveRoleHolderForAssociation in PackageInactivityListener will handle the case
+ // that there are other devices with the same profile, so the role holder won't be removed.
+
return association;
}
@@ -884,36 +966,184 @@ public class CompanionDeviceManagerService extends SystemService {
final String packageName = association.getPackageName();
final String deviceProfile = association.getDeviceProfile();
+ if (!maybeRemoveRoleHolderForAssociation(association)) {
+ // Need to remove the app from list of the role holders, but will have to do it later
+ // (the app is in foreground at the moment).
+ addToPendingRoleHolderRemoval(association);
+ }
+
+ // Need to check if device still present now because CompanionDevicePresenceMonitor will
+ // remove current connected device after mAssociationStore.removeAssociation
final boolean wasPresent = mDevicePresenceMonitor.isDevicePresent(associationId);
// Removing the association.
mAssociationStore.removeAssociation(associationId);
+ // Do not need to persistUserState since CompanionDeviceManagerService will get callback
+ // from #onAssociationChanged, and it will handle the persistUserState which including
+ // active and revoked association.
logRemoveAssociation(deviceProfile);
- final List<AssociationInfo> otherAssociations =
- mAssociationStore.getAssociationsForPackage(userId, packageName);
-
- // Check if the package is associated with other devices with the same profile.
- // If not: take away the role.
- if (deviceProfile != null) {
- final boolean shouldKeepTheRole = any(otherAssociations,
- it -> deviceProfile.equals(it.getDeviceProfile()));
- if (!shouldKeepTheRole) {
- Binder.withCleanCallingIdentity(() ->
- removeRoleHolderForAssociation(getContext(), association));
- }
- }
-
if (!wasPresent || !association.isNotifyOnDeviceNearby()) return;
// The device was connected and the app was notified: check if we need to unbind the app
// now.
- final boolean shouldStayBound = any(otherAssociations,
+ final boolean shouldStayBound = any(
+ mAssociationStore.getAssociationsForPackage(userId, packageName),
it -> it.isNotifyOnDeviceNearby()
&& mDevicePresenceMonitor.isDevicePresent(it.getId()));
if (shouldStayBound) return;
mCompanionAppController.unbindCompanionApplication(userId, packageName);
}
+ /**
+ * First, checks if the companion application should be removed from the list role holders when
+ * upon association's removal, i.e.: association's profile (matches the role) is not null,
+ * the application does not have other associations with the same profile, etc.
+ *
+ * <p>
+ * Then, if establishes that the application indeed has to be removed from the list of the role
+ * holders, checks if it could be done right now -
+ * {@link android.app.role.RoleManager#removeRoleHolderAsUser(String, String, int, UserHandle, java.util.concurrent.Executor, java.util.function.Consumer) RoleManager#removeRoleHolderAsUser()}
+ * will kill the application's process, which leads poor user experience if the application was
+ * in foreground when this happened, to avoid this CDMS delays invoking
+ * {@code RoleManager.removeRoleHolderAsUser()} until the app is no longer in foreground.
+ *
+ * @return {@code true} if the application does NOT need be removed from the list of the role
+ * holders OR if the application was successfully removed from the list of role holders.
+ * I.e.: from the role-management perspective the association is done with.
+ * {@code false} if the application needs to be removed from the list of role the role
+ * holders, BUT it CDMS would prefer to do it later.
+ * I.e.: application is in the foreground at the moment, but invoking
+ * {@code RoleManager.removeRoleHolderAsUser()} will kill the application's process,
+ * which would lead to the poor UX, hence need to try later.
+ */
+
+ private boolean maybeRemoveRoleHolderForAssociation(@NonNull AssociationInfo association) {
+ if (DEBUG) Log.d(TAG, "maybeRemoveRoleHolderForAssociation() association=" + association);
+
+ final String deviceProfile = association.getDeviceProfile();
+ if (deviceProfile == null) {
+ // No role was granted to for this association, there is nothing else we need to here.
+ return true;
+ }
+
+ // Check if the applications is associated with another devices with the profile. If so,
+ // it should remain the role holder.
+ final int id = association.getId();
+ final int userId = association.getUserId();
+ final String packageName = association.getPackageName();
+ final boolean roleStillInUse = any(
+ mAssociationStore.getAssociationsForPackage(userId, packageName),
+ it -> deviceProfile.equals(it.getDeviceProfile()) && id != it.getId());
+ if (roleStillInUse) {
+ // Application should remain a role holder, there is nothing else we need to here.
+ return true;
+ }
+
+ final int packageProcessImportance = getPackageProcessImportance(userId, packageName);
+ if (packageProcessImportance <= IMPORTANCE_VISIBLE) {
+ // Need to remove the app from the list of role holders, but the process is visible to
+ // the user at the moment, so we'll need to it later: log and return false.
+ Slog.i(TAG, "Cannot remove role holder for the removed association id=" + id
+ + " now - process is visible.");
+ return false;
+ }
+
+ removeRoleHolderForAssociation(getContext(), association);
+ return true;
+ }
+
+ private int getPackageProcessImportance(@UserIdInt int userId, @NonNull String packageName) {
+ return Binder.withCleanCallingIdentity(() -> {
+ final int uid =
+ mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId);
+ return mActivityManager.getUidImportance(uid);
+ });
+ }
+
+ /**
+ * Set revoked flag for active association and add the revoked association and the uid into
+ * the caches.
+ *
+ * @see #mRevokedAssociationsPendingRoleHolderRemoval
+ * @see #mUidsPendingRoleHolderRemoval
+ * @see OnPackageVisibilityChangeListener
+ */
+ private void addToPendingRoleHolderRemoval(@NonNull AssociationInfo association) {
+ // First: set revoked flag.
+ association = AssociationInfo.builder(association)
+ .setRevoked(true)
+ .build();
+
+ final String packageName = association.getPackageName();
+ final int userId = association.getUserId();
+ final int uid = mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId);
+
+ // Second: add to the set.
+ synchronized (mRevokedAssociationsPendingRoleHolderRemoval) {
+ mRevokedAssociationsPendingRoleHolderRemoval.forUser(association.getUserId())
+ .add(association);
+ if (!mUidsPendingRoleHolderRemoval.containsKey(uid)) {
+ mUidsPendingRoleHolderRemoval.put(uid, packageName);
+
+ if (mUidsPendingRoleHolderRemoval.size() == 1) {
+ // Just added first uid: start the listener
+ mOnPackageVisibilityChangeListener.startListening();
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove the revoked association form the cache and also remove the uid form the map if
+ * there are other associations with the same package still pending for role holder removal.
+ *
+ * @see #mRevokedAssociationsPendingRoleHolderRemoval
+ * @see #mUidsPendingRoleHolderRemoval
+ * @see OnPackageVisibilityChangeListener
+ */
+ private void removeFromPendingRoleHolderRemoval(@NonNull AssociationInfo association) {
+ final String packageName = association.getPackageName();
+ final int userId = association.getUserId();
+ final int uid = mPackageManagerInternal.getPackageUid(packageName, /* flags */0, userId);
+
+ synchronized (mRevokedAssociationsPendingRoleHolderRemoval) {
+ mRevokedAssociationsPendingRoleHolderRemoval.forUser(userId)
+ .remove(association);
+
+ final boolean shouldKeepUidForRemoval = any(
+ getPendingRoleHolderRemovalAssociationsForUser(userId),
+ ai -> packageName.equals(ai.getPackageName()));
+ // Do not remove the uid form the map since other associations with
+ // the same packageName still pending for role holder removal.
+ if (!shouldKeepUidForRemoval) {
+ mUidsPendingRoleHolderRemoval.remove(uid);
+ }
+
+ if (mUidsPendingRoleHolderRemoval.isEmpty()) {
+ // The set is empty now - can "turn off" the listener.
+ mOnPackageVisibilityChangeListener.stopListening();
+ }
+ }
+ }
+
+ /**
+ * @return a copy of the revoked associations set (safeguarding against
+ * {@code ConcurrentModificationException}-s).
+ */
+ private @NonNull Set<AssociationInfo> getPendingRoleHolderRemovalAssociationsForUser(
+ @UserIdInt int userId) {
+ synchronized (mRevokedAssociationsPendingRoleHolderRemoval) {
+ // Return a copy.
+ return new ArraySet<>(mRevokedAssociationsPendingRoleHolderRemoval.forUser(userId));
+ }
+ }
+
+ private String getPackageNameByUid(int uid) {
+ synchronized (mRevokedAssociationsPendingRoleHolderRemoval) {
+ return mUidsPendingRoleHolderRemoval.get(uid);
+ }
+ }
+
private void updateSpecialAccessPermissionForAssociatedPackage(AssociationInfo association) {
final PackageInfo packageInfo =
getPackageInfo(getContext(), association.getUserId(), association.getPackageName());
@@ -1134,4 +1364,80 @@ public class CompanionDeviceManagerService extends SystemService {
persistStateForUser(userId);
}
}
+
+ /**
+ * An OnUidImportanceListener class which watches the importance of the packages.
+ * In this class, we ONLY interested in the importance of the running process is greater than
+ * {@link RunningAppProcessInfo.IMPORTANCE_VISIBLE} for the uids have been added into the
+ * {@link mUidsPendingRoleHolderRemoval}. Lastly remove the role holder for the revoked
+ * associations for the same packages.
+ *
+ * @see #maybeRemoveRoleHolderForAssociation(AssociationInfo)
+ * @see #removeFromPendingRoleHolderRemoval(AssociationInfo)
+ * @see #getPendingRoleHolderRemovalAssociationsForUser(int)
+ */
+ private class OnPackageVisibilityChangeListener implements
+ ActivityManager.OnUidImportanceListener {
+ final @NonNull ActivityManager mAm;
+
+ OnPackageVisibilityChangeListener(@NonNull ActivityManager am) {
+ this.mAm = am;
+ }
+
+ void startListening() {
+ Binder.withCleanCallingIdentity(
+ () -> mAm.addOnUidImportanceListener(
+ /* listener */ OnPackageVisibilityChangeListener.this,
+ RunningAppProcessInfo.IMPORTANCE_VISIBLE));
+ }
+
+ void stopListening() {
+ Binder.withCleanCallingIdentity(
+ () -> mAm.removeOnUidImportanceListener(
+ /* listener */ OnPackageVisibilityChangeListener.this));
+ }
+
+ @Override
+ public void onUidImportance(int uid, int importance) {
+ if (importance <= RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
+ // The lower the importance value the more "important" the process is.
+ // We are only interested when the process ceases to be visible.
+ return;
+ }
+
+ final String packageName = getPackageNameByUid(uid);
+ if (packageName == null) {
+ // Not interested in this uid.
+ return;
+ }
+
+ final int userId = UserHandle.getUserId(uid);
+
+ boolean needToPersistStateForUser = false;
+
+ for (AssociationInfo association :
+ getPendingRoleHolderRemovalAssociationsForUser(userId)) {
+ if (!packageName.equals(association.getPackageName())) continue;
+
+ if (!maybeRemoveRoleHolderForAssociation(association)) {
+ // Did not remove the role holder, will have to try again later.
+ continue;
+ }
+
+ removeFromPendingRoleHolderRemoval(association);
+ needToPersistStateForUser = true;
+ }
+
+ if (needToPersistStateForUser) {
+ mUserPersistenceHandler.postPersistUserState(userId);
+ }
+ }
+ }
+
+ private static class PerUserAssociationSet extends PerUser<Set<AssociationInfo>> {
+ @Override
+ protected @NonNull Set<AssociationInfo> create(int userId) {
+ return new ArraySet<>();
+ }
+ }
}
diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/PackageUtils.java
index a2b20593a9cb..f523773033d1 100644
--- a/services/companion/java/com/android/server/companion/PackageUtils.java
+++ b/services/companion/java/com/android/server/companion/PackageUtils.java
@@ -30,6 +30,7 @@ import android.companion.CompanionDeviceService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.PackageInfoFlags;
@@ -39,8 +40,6 @@ import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.util.Slog;
-import com.android.internal.util.ArrayUtils;
-
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -65,15 +64,20 @@ final class PackageUtils {
static void enforceUsesCompanionDeviceFeature(@NonNull Context context,
@UserIdInt int userId, @NonNull String packageName) {
- final boolean requested = ArrayUtils.contains(
- getPackageInfo(context, userId, packageName).reqFeatures,
- FEATURE_COMPANION_DEVICE_SETUP);
-
- if (requested) {
- throw new IllegalStateException("Must declare uses-feature "
- + FEATURE_COMPANION_DEVICE_SETUP
- + " in manifest to use this API");
+ String requiredFeature = FEATURE_COMPANION_DEVICE_SETUP;
+
+ FeatureInfo[] requestedFeatures = getPackageInfo(context, userId, packageName).reqFeatures;
+ if (requestedFeatures != null) {
+ for (int i = 0; i < requestedFeatures.length; i++) {
+ if (requiredFeature.equals(requestedFeatures[i].name)) {
+ return;
+ }
+ }
}
+
+ throw new IllegalStateException("Must declare uses-feature "
+ + requiredFeature
+ + " in manifest to use this API");
}
/**
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index 36393894f727..4b56c1b28036 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -103,7 +103,7 @@ import java.util.concurrent.ConcurrentMap;
* Since Android T the data is stored to "companion_device_manager.xml" file in
* {@link Environment#getDataSystemDeDirectory(int) /data/system_de/}.
*
- * See {@link #getBaseStorageFileForUser(int) getBaseStorageFileForUser()}
+ * See {@link DataStoreUtils#getBaseStorageFileForUser(int, String)}
*
* <p>
* Since Android T the data is stored using the v1 schema.
@@ -120,7 +120,7 @@ import java.util.concurrent.ConcurrentMap;
* <li> {@link #readPreviouslyUsedIdsV1(TypedXmlPullParser, Map) readPreviouslyUsedIdsV1()}
* </ul>
*
- * The following snippet is a sample of a file that is using v0 schema.
+ * The following snippet is a sample of a file that is using v1 schema.
* <pre>{@code
* <state persistence-version="1">
* <associations>
@@ -130,6 +130,8 @@ import java.util.concurrent.ConcurrentMap;
* mac_address="AA:BB:CC:DD:EE:00"
* self_managed="false"
* notify_device_nearby="false"
+ * revoked="false"
+ * last_time_connected="1634641160229"
* time_approved="1634389553216"/>
*
* <association
@@ -139,6 +141,8 @@ import java.util.concurrent.ConcurrentMap;
* display_name="Jhon's Chromebook"
* self_managed="true"
* notify_device_nearby="false"
+ * revoked="false"
+ * last_time_connected="1634641160229"
* time_approved="1634641160229"/>
* </associations>
*
@@ -178,6 +182,7 @@ final class PersistentDataStore {
private static final String XML_ATTR_PROFILE = "profile";
private static final String XML_ATTR_SELF_MANAGED = "self_managed";
private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby";
+ private static final String XML_ATTR_REVOKED = "revoked";
private static final String XML_ATTR_TIME_APPROVED = "time_approved";
private static final String XML_ATTR_LAST_TIME_CONNECTED = "last_time_connected";
@@ -415,7 +420,8 @@ final class PersistentDataStore {
out.add(new AssociationInfo(associationId, userId, appPackage,
MacAddress.fromString(deviceAddress), null, profile,
- /* managedByCompanionApp */false, notify, timeApproved, Long.MAX_VALUE));
+ /* managedByCompanionApp */ false, notify, /* revoked */ false, timeApproved,
+ Long.MAX_VALUE));
}
private static void readAssociationsV1(@NonNull TypedXmlPullParser parser,
@@ -444,13 +450,14 @@ final class PersistentDataStore {
final String displayName = readStringAttribute(parser, XML_ATTR_DISPLAY_NAME);
final boolean selfManaged = readBooleanAttribute(parser, XML_ATTR_SELF_MANAGED);
final boolean notify = readBooleanAttribute(parser, XML_ATTR_NOTIFY_DEVICE_NEARBY);
+ final boolean revoked = readBooleanAttribute(parser, XML_ATTR_REVOKED, false);
final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L);
final long lastTimeConnected = readLongAttribute(
parser, XML_ATTR_LAST_TIME_CONNECTED, Long.MAX_VALUE);
final AssociationInfo associationInfo = createAssociationInfoNoThrow(associationId, userId,
- appPackage, macAddress, displayName, profile, selfManaged, notify, timeApproved,
- lastTimeConnected);
+ appPackage, macAddress, displayName, profile, selfManaged, notify, revoked,
+ timeApproved, lastTimeConnected);
if (associationInfo != null) {
out.add(associationInfo);
}
@@ -503,6 +510,8 @@ final class PersistentDataStore {
writeBooleanAttribute(serializer, XML_ATTR_SELF_MANAGED, a.isSelfManaged());
writeBooleanAttribute(
serializer, XML_ATTR_NOTIFY_DEVICE_NEARBY, a.isNotifyOnDeviceNearby());
+ writeBooleanAttribute(
+ serializer, XML_ATTR_REVOKED, a.isRevoked());
writeLongAttribute(serializer, XML_ATTR_TIME_APPROVED, a.getTimeApprovedMs());
writeLongAttribute(
serializer, XML_ATTR_LAST_TIME_CONNECTED, a.getLastTimeConnectedMs());
@@ -544,11 +553,12 @@ final class PersistentDataStore {
private static AssociationInfo createAssociationInfoNoThrow(int associationId,
@UserIdInt int userId, @NonNull String appPackage, @Nullable MacAddress macAddress,
@Nullable CharSequence displayName, @Nullable String profile, boolean selfManaged,
- boolean notify, long timeApproved, long lastTimeConnected) {
+ boolean notify, boolean revoked, long timeApproved, long lastTimeConnected) {
AssociationInfo associationInfo = null;
try {
associationInfo = new AssociationInfo(associationId, userId, appPackage, macAddress,
- displayName, profile, selfManaged, notify, timeApproved, lastTimeConnected);
+ displayName, profile, selfManaged, notify, revoked, timeApproved,
+ lastTimeConnected);
} catch (Exception e) {
if (DEBUG) Log.w(TAG, "Could not create AssociationInfo", e);
}
diff --git a/services/companion/java/com/android/server/companion/RolesUtils.java b/services/companion/java/com/android/server/companion/RolesUtils.java
index 35488a80b78b..0fff3f488562 100644
--- a/services/companion/java/com/android/server/companion/RolesUtils.java
+++ b/services/companion/java/com/android/server/companion/RolesUtils.java
@@ -85,6 +85,8 @@ final class RolesUtils {
final int userId = associationInfo.getUserId();
final UserHandle userHandle = UserHandle.of(userId);
+ Slog.i(TAG, "Removing CDM role holder, role=" + deviceProfile
+ + ", package=u" + userId + "\\" + packageName);
roleManager.removeRoleHolderAsUser(deviceProfile, packageName,
MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
success -> {
diff --git a/services/contentcapture/Android.bp b/services/contentcapture/Android.bp
index 434f239dbddc..5392c2cde3b8 100644
--- a/services/contentcapture/Android.bp
+++ b/services/contentcapture/Android.bp
@@ -17,6 +17,9 @@ filegroup {
java_library_static {
name: "services.contentcapture",
defaults: ["platform_service_defaults"],
- srcs: [":services.contentcapture-sources"],
+ srcs: [
+ ":services.contentcapture-sources",
+ "java/**/*.logtags",
+ ],
libs: ["services.core"],
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 41a759254909..0428b2322f27 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -60,6 +60,7 @@ import android.service.contentcapture.SnapshotData;
import android.service.voice.VoiceInteractionManagerInternal;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -88,6 +89,11 @@ final class ContentCapturePerUserService
private static final String TAG = ContentCapturePerUserService.class.getSimpleName();
+ private static final int EVENT_LOG_CONNECT_STATE_DIED = 0;
+ static final int EVENT_LOG_CONNECT_STATE_CONNECTED = 1;
+ static final int EVENT_LOG_CONNECT_STATE_DISCONNECTED = 2;
+
+
@GuardedBy("mLock")
private final SparseArray<ContentCaptureServerSession> mSessions = new SparseArray<>();
@@ -190,9 +196,13 @@ final class ContentCapturePerUserService
Slog.w(TAG, "remote service died: " + service);
synchronized (mLock) {
mZombie = true;
+ ComponentName serviceComponent = getServiceComponentName();
writeServiceEvent(
FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_REMOTE_SERVICE_DIED,
- getServiceComponentName());
+ serviceComponent);
+ EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED, mUserId,
+ serviceComponent != null ? serviceComponent.flattenToShortString() : "",
+ EVENT_LOG_CONNECT_STATE_DIED);
}
}
@@ -614,11 +624,16 @@ final class ContentCapturePerUserService
? "null_activities" : activities.size() + " activities") + ")"
+ " for user " + mUserId);
}
+ int packageCount = packages != null ? packages.size() : 0;
+ int activityCount = activities != null ? activities.size() : 0;
ArraySet<String> oldList =
mMaster.mGlobalContentCaptureOptions.getWhitelistedPackages(mUserId);
+ EventLog.writeEvent(EventLogTags.CC_CURRENT_ALLOWLIST, mUserId, oldList.size());
mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities);
+ EventLog.writeEvent(EventLogTags.CC_SET_ALLOWLIST, mUserId,
+ packageCount, activityCount);
writeSetWhitelistEvent(getServiceComponentName(), packages, activities);
updateContentCaptureOptions(oldList);
@@ -699,12 +714,14 @@ final class ContentCapturePerUserService
private void updateContentCaptureOptions(@Nullable ArraySet<String> oldList) {
ArraySet<String> adding = mMaster.mGlobalContentCaptureOptions
.getWhitelistedPackages(mUserId);
+ EventLog.writeEvent(EventLogTags.CC_CURRENT_ALLOWLIST, mUserId, adding.size());
if (oldList != null && adding != null) {
adding.removeAll(oldList);
}
int N = adding != null ? adding.size() : 0;
+ EventLog.writeEvent(EventLogTags.CC_UPDATE_OPTIONS, mUserId, N);
for (int i = 0; i < N; i++) {
String packageName = adding.valueAt(i);
ContentCaptureOptions options = mMaster.mGlobalContentCaptureOptions
diff --git a/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags b/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags
new file mode 100644
index 000000000000..6722b9ed3c5f
--- /dev/null
+++ b/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags
@@ -0,0 +1,8 @@
+# See system/logging/logcat/event.logtags for a description of the format of this file.
+
+option java_package com.android.server.contentcapture
+
+53200 cc_connect_state_changed (User|1|5),(component|3),(type|1)
+53201 cc_set_allowlist (User|1|5),(package_count|1),(activity_count|1)
+53202 cc_current_allowlist (User|1|5),(count|1)
+53203 cc_update_options (User|1|5),(count|1)
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index 1efe55aa0767..e22a9d07b4ae 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -31,6 +31,7 @@ import android.service.contentcapture.IContentCaptureService;
import android.service.contentcapture.IContentCaptureServiceCallback;
import android.service.contentcapture.IDataShareCallback;
import android.service.contentcapture.SnapshotData;
+import android.util.EventLog;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.DataRemovalRequest;
@@ -47,6 +48,7 @@ final class RemoteContentCaptureService
private final IBinder mServerCallback;
private final int mIdleUnbindTimeoutMs;
private final ContentCapturePerUserService mPerUserService;
+ private final int mUserId;
RemoteContentCaptureService(Context context, String serviceInterface,
ComponentName serviceComponentName, IContentCaptureServiceCallback callback, int userId,
@@ -61,6 +63,7 @@ final class RemoteContentCaptureService
mPerUserService = perUserService;
mServerCallback = callback.asBinder();
mIdleUnbindTimeoutMs = idleUnbindTimeoutMs;
+ mUserId = userId;
// Bind right away, which will trigger a onConnected() on service's
ensureBoundLocked();
@@ -88,6 +91,9 @@ final class RemoteContentCaptureService
writeServiceEvent(
FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_CONNECTED,
mComponentName);
+ EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED, mUserId,
+ mComponentName != null ? mComponentName.flattenToShortString() : "",
+ ContentCapturePerUserService.EVENT_LOG_CONNECT_STATE_CONNECTED);
} finally {
// Update the system-service state, in case the service reconnected after
// dying
@@ -98,6 +104,9 @@ final class RemoteContentCaptureService
writeServiceEvent(
FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_DISCONNECTED,
mComponentName);
+ EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED, mUserId,
+ mComponentName != null ? mComponentName.flattenToShortString() : "",
+ ContentCapturePerUserService.EVENT_LOG_CONNECT_STATE_DISCONNECTED);
}
} catch (Exception e) {
Slog.w(mTag, "Exception calling onConnectedStateChanged(" + connected + "): " + e);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index b059cc7e2aa2..7d8c19f41ae4 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -168,8 +168,8 @@ public class AccountManagerService
}
@Override
- public void onUserStopping(@NonNull TargetUser user) {
- Slog.i(TAG, "onStopUser " + user);
+ public void onUserStopped(@NonNull TargetUser user) {
+ Slog.i(TAG, "onUserStopped " + user);
mService.purgeUserData(user.getUserIdentifier());
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a78c64b6538d..07a5fb5d0f0f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2297,13 +2297,9 @@ public class ActivityManagerService extends IActivityManager.Stub
return mAppOpsManager;
}
- /**
- * Provides the basic functionality for activity task related tests when a handler thread is
- * given to initialize the dependency members.
- */
+ /** Provides the basic functionality for unit tests. */
@VisibleForTesting
- public ActivityManagerService(Injector injector, ServiceThread handlerThread) {
- final boolean hasHandlerThread = handlerThread != null;
+ ActivityManagerService(Injector injector, @NonNull ServiceThread handlerThread) {
mInjector = injector;
mContext = mInjector.getContext();
mUiContext = null;
@@ -2311,33 +2307,27 @@ public class ActivityManagerService extends IActivityManager.Stub
mPackageWatchdog = null;
mAppOpsService = mInjector.getAppOpsService(null /* file */, null /* handler */);
mBatteryStatsService = null;
- mHandler = hasHandlerThread ? new MainHandler(handlerThread.getLooper()) : null;
+ mHandler = new MainHandler(handlerThread.getLooper());
mHandlerThread = handlerThread;
- mConstants = hasHandlerThread
- ? new ActivityManagerConstants(mContext, this, mHandler) : null;
+ mConstants = new ActivityManagerConstants(mContext, this, mHandler);
final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */);
mPlatformCompat = null;
mProcessList = injector.getProcessList(this);
mProcessList.init(this, activeUids, mPlatformCompat);
mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(), null);
mPhantomProcessList = new PhantomProcessList(this);
- mOomAdjuster = hasHandlerThread
- ? new OomAdjuster(this, mProcessList, activeUids, handlerThread) : null;
+ mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids, handlerThread);
- mIntentFirewall = hasHandlerThread
- ? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null;
+ mIntentFirewall = null;
mProcessStats = null;
mCpHelper = new ContentProviderHelper(this, false);
- // For the usage of {@link ActiveServices#cleanUpServices} that may be invoked from
- // {@link ActivityTaskSupervisor#cleanUpRemovedTaskLocked}.
- mServices = hasHandlerThread ? new ActiveServices(this) : null;
+ mServices = null;
mSystemThread = null;
mUiHandler = injector.getUiHandler(null /* service */);
mUidObserverController = new UidObserverController(mUiHandler);
- mUserController = hasHandlerThread ? new UserController(this) : null;
- mPendingIntentController = hasHandlerThread
- ? new PendingIntentController(handlerThread.getLooper(), mUserController,
- mConstants) : null;
+ mUserController = new UserController(this);
+ mPendingIntentController =
+ new PendingIntentController(handlerThread.getLooper(), mUserController, mConstants);
mAppRestrictionController = new AppRestrictionController(mContext, this);
mProcStartHandlerThread = null;
mProcStartHandler = null;
@@ -2346,7 +2336,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mFactoryTest = FACTORY_TEST_OFF;
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mInternal = new LocalService();
- mPendingStartActivityUids = new PendingStartActivityUids(mContext);
+ mPendingStartActivityUids = new PendingStartActivityUids();
mUseFifoUiScheduling = false;
mEnableOffloadQueue = false;
mFgBroadcastQueue = mBgBroadcastQueue = mBgOffloadBroadcastQueue =
@@ -2482,7 +2472,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
mInternal = new LocalService();
- mPendingStartActivityUids = new PendingStartActivityUids(mContext);
+ mPendingStartActivityUids = new PendingStartActivityUids();
mTraceErrorLogger = new TraceErrorLogger();
mComponentAliasResolver = new ComponentAliasResolver(this);
}
@@ -14399,6 +14389,19 @@ public class ActivityManagerService extends IActivityManager.Stub
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
+ // Non-system callers can't declare that a broadcast is alarm-related.
+ // The PendingIntent invocation case is handled in PendingIntentRecord.
+ if (bOptions != null && callingUid != SYSTEM_UID) {
+ if (bOptions.containsKey(BroadcastOptions.KEY_ALARM_BROADCAST)) {
+ if (DEBUG_BROADCAST) {
+ Slog.w(TAG, "Non-system caller " + callingUid
+ + " may not flag broadcast as alarm-related");
+ }
+ throw new SecurityException(
+ "Non-system callers may not flag broadcasts as alarm-related");
+ }
+ }
+
final long origId = Binder.clearCallingIdentity();
try {
return broadcastIntentLocked(callerApp,
@@ -14412,6 +14415,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ // Not the binder call surface
int broadcastIntentInPackage(String packageName, @Nullable String featureId, int uid,
int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
@@ -17380,7 +17384,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// next top activity on time. This race will fail the following binder transactions WM
// sends to the activity. After this race issue between WM/ATMS and AMS is solved, this
// workaround can be removed. (b/213288355)
- if (isNewPending && mOomAdjuster != null) { // It can be null in unit test.
+ if (isNewPending) {
mOomAdjuster.mCachedAppOptimizer.unfreezeProcess(pid);
}
// We need to update the network rules for the app coming to the top state so that
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 19ffc1733f3d..ae91d75ef0ce 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -70,6 +70,7 @@ final class BroadcastRecord extends Binder {
final boolean callerInstantApp; // caller is an Instant App?
final boolean ordered; // serialize the send to receivers?
final boolean sticky; // originated from existing sticky data?
+ final boolean alarm; // originated from an alarm triggering?
final boolean initialSticky; // initial broadcast from register to sticky?
final int userId; // user id this broadcast was for
final String resolvedType; // the resolved data type
@@ -305,6 +306,7 @@ final class BroadcastRecord extends Binder {
this.allowBackgroundActivityStarts = allowBackgroundActivityStarts;
mBackgroundActivityStartsToken = backgroundActivityStartsToken;
this.timeoutExempt = timeoutExempt;
+ alarm = options != null && options.isAlarmBroadcast();
}
/**
@@ -357,6 +359,7 @@ final class BroadcastRecord extends Binder {
allowBackgroundActivityStarts = from.allowBackgroundActivityStarts;
mBackgroundActivityStartsToken = from.mBackgroundActivityStartsToken;
timeoutExempt = from.timeoutExempt;
+ alarm = from.alarm;
}
/**
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 728792f608ef..453385938aca 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -22,6 +22,9 @@ import static android.os.Process.PROC_PARENS;
import static android.os.Process.PROC_SPACE_TERM;
import static android.os.Process.SYSTEM_UID;
+import static com.android.internal.util.FrameworkStatsLog.PROVIDER_ACQUISITION_EVENT_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
+import static com.android.internal.util.FrameworkStatsLog.PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
import static com.android.server.am.ActivityManagerService.TAG_MU;
import static com.android.server.am.ProcessProfileRecord.HOSTING_COMPONENT_TYPE_PROVIDER;
@@ -73,6 +76,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.RescueParty;
import com.android.server.pm.UserManagerInternal;
@@ -241,6 +245,10 @@ public class ContentProviderHelper {
ContentProviderHolder holder = cpr.newHolder(null, true);
// don't give caller the provider object, it needs to make its own.
holder.provider = null;
+ FrameworkStatsLog.write(
+ PROVIDER_ACQUISITION_EVENT_REPORTED,
+ r.uid, callingUid,
+ PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM);
return holder;
}
@@ -307,6 +315,10 @@ public class ContentProviderHelper {
dyingProc = cpr.proc;
} else {
cpr.proc.mState.setVerifiedAdj(cpr.proc.mState.getSetAdj());
+ FrameworkStatsLog.write(
+ PROVIDER_ACQUISITION_EVENT_REPORTED,
+ cpr.proc.uid, callingUid,
+ PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -479,6 +491,10 @@ public class ContentProviderHelper {
} catch (RemoteException e) {
}
}
+ FrameworkStatsLog.write(
+ PROVIDER_ACQUISITION_EVENT_REPORTED,
+ proc.uid, callingUid,
+ PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM);
} else {
checkTime(startTime, "getContentProviderImpl: before start process");
proc = mService.startProcessLocked(
@@ -495,6 +511,10 @@ public class ContentProviderHelper {
+ ": process is bad");
return null;
}
+ FrameworkStatsLog.write(
+ PROVIDER_ACQUISITION_EVENT_REPORTED,
+ proc.uid, callingUid,
+ PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD);
}
cpr.launchingApp = proc;
mLaunchingProviders.add(cpr);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index f7fbbe4ebead..e9658dbc7b1a 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1556,14 +1556,22 @@ public class OomAdjuster {
boolean foregroundActivities = false;
boolean hasVisibleActivities = false;
- if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) {
+ if (app == topApp && (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP
+ || PROCESS_STATE_CUR_TOP == PROCESS_STATE_IMPORTANT_FOREGROUND)) {
// The last app on the list is the foreground app.
adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
- state.setAdjType("top-activity");
+ if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP) {
+ schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+ state.setAdjType("top-activity");
+ } else {
+ // Demote the scheduling group to avoid CPU contention if there is another more
+ // important process which also uses top-app, such as if SystemUI is animating.
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ state.setAdjType("intermediate-top-activity");
+ }
foregroundActivities = true;
hasVisibleActivities = true;
- procState = PROCESS_STATE_CUR_TOP;
+ procState = PROCESS_STATE_TOP;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app);
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 4044cceb606b..bda60ff2172b 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -18,6 +18,7 @@ package com.android.server.am;
import static android.app.ActivityManager.START_SUCCESS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -34,6 +35,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerWhitelistManager;
import android.os.PowerWhitelistManager.ReasonCode;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.TransactionTooLargeException;
@@ -416,6 +418,22 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
+
+ // Only system senders can declare a broadcast to be alarm-originated. We check
+ // this here rather than in the general case handling below to fail before the other
+ // invocation side effects such as allowlisting.
+ if (options != null && callingUid != Process.SYSTEM_UID
+ && key.type == ActivityManager.INTENT_SENDER_BROADCAST) {
+ if (options.containsKey(BroadcastOptions.KEY_ALARM_BROADCAST)) {
+ if (DEBUG_BROADCAST_LIGHT) {
+ Slog.w(TAG, "Non-system caller " + callingUid
+ + " may not flag broadcast as alarm-related");
+ }
+ throw new SecurityException(
+ "Non-system callers may not flag broadcasts as alarm-related");
+ }
+ }
+
final long origId = Binder.clearCallingIdentity();
int res = START_SUCCESS;
diff --git a/services/core/java/com/android/server/am/PendingStartActivityUids.java b/services/core/java/com/android/server/am/PendingStartActivityUids.java
index bd600571f0a3..da09317add90 100644
--- a/services/core/java/com/android/server/am/PendingStartActivityUids.java
+++ b/services/core/java/com/android/server/am/PendingStartActivityUids.java
@@ -16,7 +16,6 @@
package com.android.server.am;
-import android.content.Context;
import android.os.SystemClock;
import android.util.Pair;
import android.util.Slog;
@@ -40,11 +39,6 @@ final class PendingStartActivityUids {
// Key is uid, value is Pair of pid and SystemClock.elapsedRealtime() when the
// uid is added.
private final SparseArray<Pair<Integer, Long>> mPendingUids = new SparseArray();
- private Context mContext;
-
- PendingStartActivityUids(Context context) {
- mContext = context;
- }
/** Returns {@code true} if the uid is put to the pending array. Otherwise it existed. */
synchronized boolean add(int uid, int pid) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 92a8dcd2ba8f..98e3a214435a 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2269,7 +2269,9 @@ public final class ProcessList {
final boolean inBgRestricted = ast.isAppBackgroundRestricted(
app.info.uid, app.info.packageName);
if (inBgRestricted) {
- mAppsInBackgroundRestricted.add(app);
+ synchronized (mService) {
+ mAppsInBackgroundRestricted.add(app);
+ }
}
app.mState.setBackgroundRestricted(inBgRestricted);
}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 3a789741842f..dd73cbed06d9 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -60,6 +60,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -68,6 +69,7 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -77,9 +79,12 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
+import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
+import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.AtomicFile;
import android.util.AttributeSet;
import android.util.KeyValueListParser;
import android.util.Slog;
@@ -90,6 +95,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.CompatibilityOverrideConfig;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -99,9 +105,17 @@ import com.android.server.SystemService.TargetUser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedWriter;
+import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStreamWriter;
import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
/**
@@ -122,6 +136,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
static final int POPULATE_GAME_MODE_SETTINGS = 3;
static final int SET_GAME_STATE = 4;
static final int CANCEL_GAME_LOADING_MODE = 5;
+ static final int WRITE_GAME_MODE_INTERVENTION_LIST_FILE = 6;
static final int WRITE_SETTINGS_DELAY = 10 * 1000; // 10 seconds
static final int LOADING_BOOST_MAX_DURATION = 5 * 1000; // 5 seconds
@@ -131,6 +146,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
.build();
private static final String PACKAGE_NAME_MSG_KEY = "packageName";
private static final String USER_ID_MSG_KEY = "userId";
+ private static final String GAME_MODE_INTERVENTION_LIST_FILE_NAME =
+ "game_mode_intervention.list";
private final Context mContext;
private final Object mLock = new Object();
@@ -138,8 +155,12 @@ public final class GameManagerService extends IGameManagerService.Stub {
private final Object mOverrideConfigLock = new Object();
private final Handler mHandler;
private final PackageManager mPackageManager;
+ private final UserManager mUserManager;
private final IPlatformCompat mPlatformCompat;
private final PowerManagerInternal mPowerManagerInternal;
+ private final File mSystemDir;
+ @VisibleForTesting
+ final AtomicFile mGameModeInterventionListFile;
private DeviceConfigListener mDeviceConfigListener;
@GuardedBy("mLock")
private final ArrayMap<Integer, GameManagerSettings> mSettings = new ArrayMap<>();
@@ -158,9 +179,53 @@ public final class GameManagerService extends IGameManagerService.Stub {
mContext = context;
mHandler = new SettingsHandler(looper);
mPackageManager = mContext.getPackageManager();
+ mUserManager = mContext.getSystemService(UserManager.class);
mPlatformCompat = IPlatformCompat.Stub.asInterface(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ mSystemDir = new File(Environment.getDataDirectory(), "system");
+ mSystemDir.mkdirs();
+ FileUtils.setPermissions(mSystemDir.toString(),
+ FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IROTH | FileUtils.S_IXOTH,
+ -1, -1);
+ mGameModeInterventionListFile = new AtomicFile(new File(mSystemDir,
+ GAME_MODE_INTERVENTION_LIST_FILE_NAME));
+ FileUtils.setPermissions(mGameModeInterventionListFile.getBaseFile().getAbsolutePath(),
+ FileUtils.S_IRUSR | FileUtils.S_IWUSR
+ | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+ -1, -1);
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_GAME_SERVICE)) {
+ mGameServiceController = new GameServiceController(
+ context, BackgroundThread.getExecutor(),
+ new GameServiceProviderSelectorImpl(
+ context.getResources(),
+ context.getPackageManager()),
+ new GameServiceProviderInstanceFactoryImpl(context));
+ } else {
+ mGameServiceController = null;
+ }
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ GameManagerService(Context context, Looper looper, File dataDir) {
+ mContext = context;
+ mHandler = new SettingsHandler(looper);
+ mPackageManager = mContext.getPackageManager();
+ mUserManager = mContext.getSystemService(UserManager.class);
+ mPlatformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ mSystemDir = new File(dataDir, "system");
+ mSystemDir.mkdirs();
+ FileUtils.setPermissions(mSystemDir.toString(),
+ FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IROTH | FileUtils.S_IXOTH,
+ -1, -1);
+ mGameModeInterventionListFile = new AtomicFile(new File(mSystemDir,
+ GAME_MODE_INTERVENTION_LIST_FILE_NAME));
+ FileUtils.setPermissions(mGameModeInterventionListFile.getBaseFile().getAbsolutePath(),
+ FileUtils.S_IRUSR | FileUtils.S_IWUSR
+ | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+ -1, -1);
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_GAME_SERVICE)) {
mGameServiceController = new GameServiceController(
context, BackgroundThread.getExecutor(),
@@ -306,6 +371,22 @@ public final class GameManagerService extends IGameManagerService.Stub {
mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, false);
break;
}
+ case WRITE_GAME_MODE_INTERVENTION_LIST_FILE: {
+ final int userId = (int) msg.obj;
+ if (userId < 0) {
+ Slog.wtf(TAG, "Attempt to write setting for invalid user: " + userId);
+ synchronized (mLock) {
+ removeMessages(WRITE_GAME_MODE_INTERVENTION_LIST_FILE, null);
+ }
+ break;
+ }
+
+ Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+ removeMessages(WRITE_GAME_MODE_INTERVENTION_LIST_FILE, null);
+ writeGameModeInterventionsToFile(userId);
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ break;
+ }
}
}
}
@@ -955,6 +1036,11 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
updateInterventions(packageName, gameMode, userId);
+ final Message msg = mHandler.obtainMessage(WRITE_GAME_MODE_INTERVENTION_LIST_FILE);
+ msg.obj = userId;
+ if (!mHandler.hasEqualMessages(WRITE_GAME_MODE_INTERVENTION_LIST_FILE, userId)) {
+ mHandler.sendMessage(msg);
+ }
}
/**
@@ -1520,15 +1606,98 @@ public final class GameManagerService extends IGameManagerService.Stub {
final int newGameMode = getNewGameMode(gameMode, config);
if (newGameMode != gameMode) {
setGameMode(packageName, newGameMode, userId);
+ } else {
+ // Make sure we handle the case when the interventions are changed while
+ // the game mode remains the same. We call only updateInterventions() here.
+ updateInterventions(packageName, gameMode, userId);
}
- updateInterventions(packageName, gameMode, userId);
}
} catch (Exception e) {
Slog.e(TAG, "Failed to update compat modes for user " + userId + ": " + e);
}
+
+ final Message msg = mHandler.obtainMessage(WRITE_GAME_MODE_INTERVENTION_LIST_FILE);
+ msg.obj = userId;
+ if (!mHandler.hasEqualMessages(WRITE_GAME_MODE_INTERVENTION_LIST_FILE, userId)) {
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ /*
+ Write the interventions and mode of each game to file /system/data/game_mode_intervention.list
+ Each line will contain the information of each game, separated by tab.
+ The format of the output is:
+ <package name> <UID> <current mode> <game mode 1> <interventions> <game mode 2> <interventions>
+ For example:
+ com.android.app1 1425 1 2 angle=0,scaling=1.0,fps=60 3 angle=1,scaling=0.5,fps=30
+ */
+ private void writeGameModeInterventionsToFile(@UserIdInt int userId) {
+ FileOutputStream fileOutputStream = null;
+ BufferedWriter bufferedWriter;
+ try {
+ fileOutputStream = mGameModeInterventionListFile.startWrite();
+ bufferedWriter = new BufferedWriter(new OutputStreamWriter(fileOutputStream,
+ Charset.defaultCharset()));
+
+ final StringBuilder sb = new StringBuilder();
+ final List<String> installedGamesList = getInstalledGamePackageNamesByAllUsers(userId);
+ for (final String packageName : installedGamesList) {
+ GamePackageConfiguration packageConfig = getConfig(packageName);
+ if (packageConfig == null) {
+ continue;
+ }
+ sb.append(packageName);
+ sb.append("\t");
+ sb.append(mPackageManager.getPackageUidAsUser(packageName, userId));
+ sb.append("\t");
+ sb.append(getGameMode(packageName, userId));
+ sb.append("\t");
+ final int[] modes = packageConfig.getAvailableGameModes();
+ for (int mode : modes) {
+ final GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
+ packageConfig.getGameModeConfiguration(mode);
+ if (gameModeConfiguration == null) {
+ continue;
+ }
+ sb.append(mode);
+ sb.append("\t");
+ final int useAngle = gameModeConfiguration.getUseAngle() ? 1 : 0;
+ sb.append(TextUtils.formatSimple("angle=%d", useAngle));
+ sb.append(",");
+ final String scaling = gameModeConfiguration.getScaling();
+ sb.append("scaling=");
+ sb.append(scaling);
+ sb.append(",");
+ final int fps = gameModeConfiguration.getFps();
+ sb.append(TextUtils.formatSimple("fps=%d", fps));
+ sb.append("\t");
+ }
+ sb.append("\n");
+ }
+ bufferedWriter.append(sb);
+ bufferedWriter.flush();
+ FileUtils.sync(fileOutputStream);
+ mGameModeInterventionListFile.finishWrite(fileOutputStream);
+ } catch (Exception e) {
+ mGameModeInterventionListFile.failWrite(fileOutputStream);
+ Slog.wtf(TAG, "Failed to write game_mode_intervention.list, exception " + e);
+ }
+ return;
+ }
+
+ private int[] getAllUserIds(@UserIdInt int currentUserId) {
+ final List<UserInfo> users = mUserManager.getUsers();
+ int[] userIds = new int[users.size()];
+ for (int i = 0; i < userIds.length; ++i) {
+ userIds[i] = users.get(i).id;
+ }
+ if (currentUserId != -1) {
+ userIds = ArrayUtils.appendInt(userIds, currentUserId);
+ }
+ return userIds;
}
- private String[] getInstalledGamePackageNames(int userId) {
+ private String[] getInstalledGamePackageNames(@UserIdInt int userId) {
final List<PackageInfo> packages =
mPackageManager.getInstalledPackagesAsUser(0, userId);
return packages.stream().filter(e -> e.applicationInfo != null && e.applicationInfo.category
@@ -1537,6 +1706,17 @@ public final class GameManagerService extends IGameManagerService.Stub {
.toArray(String[]::new);
}
+ private List<String> getInstalledGamePackageNamesByAllUsers(@UserIdInt int currentUserId) {
+ HashSet<String> packageSet = new HashSet<>();
+
+ final int[] userIds = getAllUserIds(currentUserId);
+ for (int userId : userIds) {
+ packageSet.addAll(Arrays.asList(getInstalledGamePackageNames(userId)));
+ }
+
+ return new ArrayList<>(packageSet);
+ }
+
/**
* @hide
*/
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 2c05dd2a2f65..3a869f859e52 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -275,6 +275,25 @@ public class AudioService extends IAudioService.Stub
// indicates whether the system maps all streams to a single stream.
private final boolean mIsSingleVolume;
+ /**
+ * indicates whether STREAM_NOTIFICATION is aliased to STREAM_RING
+ * not final due to test method, see {@link #setNotifAliasRingForTest(boolean)}.
+ */
+ private boolean mNotifAliasRing;
+
+ /**
+ * Test method to temporarily override whether STREAM_NOTIFICATION is aliased to STREAM_RING,
+ * volumes will be updated in case of a change.
+ * @param alias if true, STREAM_NOTIFICATION is aliased to STREAM_RING
+ */
+ /*package*/ void setNotifAliasRingForTest(boolean alias) {
+ boolean update = (mNotifAliasRing != alias);
+ mNotifAliasRing = alias;
+ if (update) {
+ updateStreamVolumeAlias(true, "AudioServiceTest");
+ }
+ }
+
/*package*/ boolean isPlatformVoice() {
return mPlatformType == AudioSystem.PLATFORM_VOICE;
}
@@ -944,10 +963,25 @@ public class AudioService extends IAudioService.Stub
*/
public AudioService(Context context, AudioSystemAdapter audioSystem,
SystemServerAdapter systemServer, SettingsAdapter settings, @Nullable Looper looper) {
+ this (context, audioSystem, systemServer, settings, looper,
+ context.getSystemService(AppOpsManager.class));
+ }
+
+ /**
+ * @param context
+ * @param audioSystem Adapter for {@link AudioSystem}
+ * @param systemServer Adapter for privilieged functionality for system server components
+ * @param settings Adapter for {@link Settings}
+ * @param looper Looper to use for the service's message handler. If this is null, an
+ * {@link AudioSystemThread} is created as the messaging thread instead.
+ */
+ public AudioService(Context context, AudioSystemAdapter audioSystem,
+ SystemServerAdapter systemServer, SettingsAdapter settings, @Nullable Looper looper,
+ AppOpsManager appOps) {
sLifecycleLogger.log(new AudioEventLogger.StringEvent("AudioService()"));
mContext = context;
mContentResolver = context.getContentResolver();
- mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
+ mAppOps = appOps;
mAudioSystem = audioSystem;
mSystemServer = systemServer;
@@ -978,6 +1012,9 @@ public class AudioService extends IAudioService.Stub
mUseVolumeGroupAliases = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_handleVolumeAliasesUsingVolumeGroups);
+ mNotifAliasRing = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_alias_ring_notif_stream_types);
+
// Initialize volume
// Priority 1 - Android Property
// Priority 2 - Audio Policy Service
@@ -2026,7 +2063,13 @@ public class AudioService extends IAudioService.Stub
pw.println("\nStream volumes (device: index)");
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int i = 0; i < numStreamTypes; i++) {
- pw.println("- " + AudioSystem.STREAM_NAMES[i] + ":");
+ StringBuilder alias = new StringBuilder();
+ if (mStreamVolumeAlias[i] != i) {
+ alias.append(" (aliased to: ")
+ .append(AudioSystem.STREAM_NAMES[mStreamVolumeAlias[i]])
+ .append(")");
+ }
+ pw.println("- " + AudioSystem.STREAM_NAMES[i] + alias + ":");
mStreamStates[i].dump(pw);
pw.println("");
}
@@ -2058,6 +2101,10 @@ public class AudioService extends IAudioService.Stub
mStreamVolumeAlias = STREAM_VOLUME_ALIAS_DEFAULT;
dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
}
+ if (!mNotifAliasRing) {
+ mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] =
+ AudioSystem.STREAM_NOTIFICATION;
+ }
}
if (mIsSingleVolume) {
@@ -9134,11 +9181,8 @@ public class AudioService extends IAudioService.Stub
Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT);
if (settings == null) {
Log.e(TAG, "error reading spatial audio device settings");
- } else {
- Log.v(TAG, "restoring spatial audio device settings: " + settings);
- mSpatializerHelper.setSADeviceSettings(settings);
}
- mSpatializerHelper.init(/*effectExpected*/ mHasSpatializerEffect);
+ mSpatializerHelper.init(/*effectExpected*/ mHasSpatializerEffect, settings);
mSpatializerHelper.setFeatureEnabled(mHasSpatializerEffect);
}
@@ -9916,6 +9960,7 @@ public class AudioService extends IAudioService.Stub
pw.print(" mBtScoOnByApp="); pw.println(mBtScoOnByApp);
pw.print(" mIsSingleVolume="); pw.println(mIsSingleVolume);
pw.print(" mUseFixedVolume="); pw.println(mUseFixedVolume);
+ pw.print(" mNotifAliasRing="); pw.println(mNotifAliasRing);
pw.print(" mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
pw.print(" mFullVolumeDevices="); pw.println(dumpDeviceTypes(mFullVolumeDevices));
pw.print(" mAbsoluteVolumeDevices.keySet()="); pw.println(dumpDeviceTypes(
@@ -10325,6 +10370,11 @@ public class AudioService extends IAudioService.Stub
@Override
public void setAccessibilityServiceUids(IntArray uids) {
+ // TODO(b/233287010): Fix voice interaction and a11y concurrency in audio policy service
+ if (isPlatformAutomotive()) {
+ return;
+ }
+
synchronized (mAccessibilityServiceUidsLock) {
if (uids.size() == 0) {
mAccessibilityServiceUids = null;
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 5b26672c7de2..cd5960ffbf32 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -175,7 +175,7 @@ public class SpatializerHelper {
mASA = asa;
}
- synchronized void init(boolean effectExpected) {
+ synchronized void init(boolean effectExpected, @Nullable String settings) {
loglogi("init effectExpected=" + effectExpected);
if (!effectExpected) {
loglogi("init(): setting state to STATE_NOT_SUPPORTED due to effect not expected");
@@ -278,20 +278,22 @@ public class SpatializerHelper {
mSACapableDeviceTypes.add(SPAT_MODE_FOR_DEVICE_TYPE.keyAt(i));
}
}
+
+ // When initialized from AudioService, the settings string will be non-null.
+ // Saved settings need to be applied after spatialization support is initialized above.
+ if (settings != null) {
+ setSADeviceSettings(settings);
+ }
+
// for both transaural / binaural, we are not forcing enablement as the init() method
// could have been called another time after boot in case of audioserver restart
- if (mTransauralSupported) {
- // not force-enabling as this device might already be in the device list
- addCompatibleAudioDevice(
- new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
- false /*forceEnable*/);
- }
- if (mBinauralSupported) {
- // not force-enabling as this device might already be in the device list
- addCompatibleAudioDevice(
- new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, ""),
- false /*forceEnable*/);
- }
+ addCompatibleAudioDevice(
+ new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
+ false /*forceEnable*/);
+ // not force-enabling as this device might already be in the device list
+ addCompatibleAudioDevice(
+ new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, ""),
+ false /*forceEnable*/);
} catch (RemoteException e) {
resetCapabilities();
} finally {
@@ -321,7 +323,7 @@ public class SpatializerHelper {
mState = STATE_UNINITIALIZED;
mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
- init(true);
+ init(true, null /* settings */);
setSpatializerEnabledInt(featureEnabled);
}
@@ -497,10 +499,9 @@ public class SpatializerHelper {
synchronized @NonNull List<AudioDeviceAttributes> getCompatibleAudioDevices() {
// build unionOf(mCompatibleAudioDevices, mEnabledDevice) - mDisabledAudioDevices
ArrayList<AudioDeviceAttributes> compatList = new ArrayList<>();
- for (SADeviceState dev : mSADevices) {
- if (dev.mEnabled) {
- compatList.add(new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
- dev.mDeviceType, dev.mDeviceAddress == null ? "" : dev.mDeviceAddress));
+ for (SADeviceState deviceState : mSADevices) {
+ if (deviceState.mEnabled) {
+ compatList.add(deviceState.getAudioDeviceAttributes());
}
}
return compatList;
@@ -521,15 +522,15 @@ public class SpatializerHelper {
*/
private void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada,
boolean forceEnable) {
+ if (!isDeviceCompatibleWithSpatializationModes(ada)) {
+ return;
+ }
loglogi("addCompatibleAudioDevice: dev=" + ada);
- final int deviceType = ada.getType();
- final boolean wireless = isWireless(deviceType);
boolean isInList = false;
SADeviceState deviceUpdated = null; // non-null on update.
for (SADeviceState deviceState : mSADevices) {
- if (deviceType == deviceState.mDeviceType
- && (!wireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
+ if (deviceState.matchesAudioDeviceAttributes(ada)) {
isInList = true;
if (forceEnable) {
deviceState.mEnabled = true;
@@ -539,11 +540,10 @@ public class SpatializerHelper {
}
}
if (!isInList) {
- final SADeviceState dev = new SADeviceState(deviceType,
- wireless ? ada.getAddress() : "");
- dev.mEnabled = true;
- mSADevices.add(dev);
- deviceUpdated = dev;
+ final SADeviceState deviceState = new SADeviceState(ada.getType(), ada.getAddress());
+ deviceState.mEnabled = true;
+ mSADevices.add(deviceState);
+ deviceUpdated = deviceState;
}
if (deviceUpdated != null) {
onRoutingUpdated();
@@ -574,13 +574,10 @@ public class SpatializerHelper {
synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
loglogi("removeCompatibleAudioDevice: dev=" + ada);
- final int deviceType = ada.getType();
- final boolean wireless = isWireless(deviceType);
SADeviceState deviceUpdated = null; // non-null on update.
for (SADeviceState deviceState : mSADevices) {
- if (deviceType == deviceState.mDeviceType
- && (!wireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
+ if (deviceState.matchesAudioDeviceAttributes(ada)) {
deviceState.mEnabled = false;
deviceUpdated = deviceState;
break;
@@ -602,10 +599,9 @@ public class SpatializerHelper {
// if not a wireless device, this value will be overwritten to map the type
// to TYPE_BUILTIN_SPEAKER or TYPE_WIRED_HEADPHONES
@AudioDeviceInfo.AudioDeviceType int deviceType = ada.getType();
- final boolean wireless = isWireless(deviceType);
// if not a wireless device: find if media device is in the speaker, wired headphones
- if (!wireless) {
+ if (!isWireless(deviceType)) {
// is the device type capable of doing SA?
if (!mSACapableDeviceTypes.contains(deviceType)) {
Log.i(TAG, "Device incompatible with Spatial Audio dev:" + ada);
@@ -640,9 +636,7 @@ public class SpatializerHelper {
boolean enabled = false;
boolean available = false;
for (SADeviceState deviceState : mSADevices) {
- if (deviceType == deviceState.mDeviceType
- && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
- || !wireless) {
+ if (deviceState.matchesAudioDeviceAttributes(ada)) {
available = true;
enabled = deviceState.mEnabled;
break;
@@ -652,11 +646,12 @@ public class SpatializerHelper {
}
private synchronized void addWirelessDeviceIfNew(@NonNull AudioDeviceAttributes ada) {
+ if (!isDeviceCompatibleWithSpatializationModes(ada)) {
+ return;
+ }
boolean knownDevice = false;
for (SADeviceState deviceState : mSADevices) {
- // wireless device so always check address
- if (ada.getType() == deviceState.mDeviceType
- && ada.getAddress().equals(deviceState.mDeviceAddress)) {
+ if (deviceState.matchesAudioDeviceAttributes(ada)) {
knownDevice = true;
break;
}
@@ -704,13 +699,8 @@ public class SpatializerHelper {
if (ada.getRole() != AudioDeviceAttributes.ROLE_OUTPUT) {
return false;
}
-
- final int deviceType = ada.getType();
- final boolean wireless = isWireless(deviceType);
for (SADeviceState deviceState : mSADevices) {
- if (deviceType == deviceState.mDeviceType
- && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
- || !wireless) {
+ if (deviceState.matchesAudioDeviceAttributes(ada)) {
return true;
}
}
@@ -719,12 +709,19 @@ public class SpatializerHelper {
private synchronized boolean canBeSpatializedOnDevice(@NonNull AudioAttributes attributes,
@NonNull AudioFormat format, @NonNull AudioDeviceAttributes[] devices) {
- final byte modeForDevice = (byte) SPAT_MODE_FOR_DEVICE_TYPE.get(devices[0].getType(),
+ if (isDeviceCompatibleWithSpatializationModes(devices[0])) {
+ return AudioSystem.canBeSpatialized(attributes, format, devices);
+ }
+ return false;
+ }
+
+ private boolean isDeviceCompatibleWithSpatializationModes(@NonNull AudioDeviceAttributes ada) {
+ final byte modeForDevice = (byte) SPAT_MODE_FOR_DEVICE_TYPE.get(ada.getType(),
/*default when type not found*/ SpatializationMode.SPATIALIZER_BINAURAL);
if ((modeForDevice == SpatializationMode.SPATIALIZER_BINAURAL && mBinauralSupported)
|| (modeForDevice == SpatializationMode.SPATIALIZER_TRANSAURAL
&& mTransauralSupported)) {
- return AudioSystem.canBeSpatialized(attributes, format, devices);
+ return true;
}
return false;
}
@@ -741,7 +738,7 @@ public class SpatializerHelper {
return;
}
if (mState == STATE_UNINITIALIZED) {
- init(true);
+ init(true, null /* settings */);
}
setSpatializerEnabledInt(true);
} else {
@@ -1089,13 +1086,8 @@ public class SpatializerHelper {
Log.v(TAG, "no headtracking support, ignoring setHeadTrackerEnabled to " + enabled
+ " for " + ada);
}
- final int deviceType = ada.getType();
- final boolean wireless = isWireless(deviceType);
-
for (SADeviceState deviceState : mSADevices) {
- if (deviceType == deviceState.mDeviceType
- && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
- || !wireless) {
+ if (deviceState.matchesAudioDeviceAttributes(ada)) {
if (!deviceState.mHasHeadTracker) {
Log.e(TAG, "Called setHeadTrackerEnabled enabled:" + enabled
+ " device:" + ada + " on a device without headtracker");
@@ -1109,7 +1101,7 @@ public class SpatializerHelper {
}
}
// check current routing to see if it affects the headtracking mode
- if (ROUTING_DEVICES[0].getType() == deviceType
+ if (ROUTING_DEVICES[0].getType() == ada.getType()
&& ROUTING_DEVICES[0].getAddress().equals(ada.getAddress())) {
setDesiredHeadTrackingMode(enabled ? mDesiredHeadTrackingModeWhenEnabled
: Spatializer.HEAD_TRACKING_MODE_DISABLED);
@@ -1121,13 +1113,8 @@ public class SpatializerHelper {
Log.v(TAG, "no headtracking support, hasHeadTracker always false for " + ada);
return false;
}
- final int deviceType = ada.getType();
- final boolean wireless = isWireless(deviceType);
-
for (SADeviceState deviceState : mSADevices) {
- if (deviceType == deviceState.mDeviceType
- && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
- || !wireless) {
+ if (deviceState.matchesAudioDeviceAttributes(ada)) {
return deviceState.mHasHeadTracker;
}
}
@@ -1144,13 +1131,8 @@ public class SpatializerHelper {
Log.v(TAG, "no headtracking support, setHasHeadTracker always false for " + ada);
return false;
}
- final int deviceType = ada.getType();
- final boolean wireless = isWireless(deviceType);
-
for (SADeviceState deviceState : mSADevices) {
- if (deviceType == deviceState.mDeviceType
- && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
- || !wireless) {
+ if (deviceState.matchesAudioDeviceAttributes(ada)) {
if (!deviceState.mHasHeadTracker) {
deviceState.mHasHeadTracker = true;
mAudioService.persistSpatialAudioDeviceSettings();
@@ -1168,13 +1150,8 @@ public class SpatializerHelper {
Log.v(TAG, "no headtracking support, isHeadTrackerEnabled always false for " + ada);
return false;
}
- final int deviceType = ada.getType();
- final boolean wireless = isWireless(deviceType);
-
for (SADeviceState deviceState : mSADevices) {
- if (deviceType == deviceState.mDeviceType
- && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
- || !wireless) {
+ if (deviceState.matchesAudioDeviceAttributes(ada)) {
if (!deviceState.mHasHeadTracker) {
return false;
}
@@ -1531,7 +1508,7 @@ public class SpatializerHelper {
SADeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType, @NonNull String address) {
mDeviceType = deviceType;
- mDeviceAddress = Objects.requireNonNull(address);
+ mDeviceAddress = isWireless(deviceType) ? Objects.requireNonNull(address) : "";
}
@Override
@@ -1599,6 +1576,18 @@ public class SpatializerHelper {
return null;
}
}
+
+ public AudioDeviceAttributes getAudioDeviceAttributes() {
+ return new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
+ mDeviceType, mDeviceAddress == null ? "" : mDeviceAddress);
+ }
+
+ public boolean matchesAudioDeviceAttributes(AudioDeviceAttributes ada) {
+ final int deviceType = ada.getType();
+ final boolean wireless = isWireless(deviceType);
+ return (deviceType == mDeviceType)
+ && (!wireless || ada.getAddress().equals(mDeviceAddress));
+ }
}
/*package*/ synchronized String getSADeviceSettings() {
@@ -1619,7 +1608,9 @@ public class SpatializerHelper {
// small list, not worth overhead of Arrays.stream(devSettings)
for (String setting : devSettings) {
SADeviceState devState = SADeviceState.fromPersistedString(setting);
- if (devState != null) {
+ if (devState != null
+ && isDeviceCompatibleWithSpatializationModes(
+ devState.getAudioDeviceAttributes())) {
mSADevices.add(devState);
logDeviceState(devState, "setSADeviceSettings");
}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
index ad24cf0591ca..262be08b60e2 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
@@ -28,8 +28,10 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.Utils;
@@ -41,6 +43,10 @@ public class BiometricLogger {
public static final String TAG = "BiometricLogger";
public static final boolean DEBUG = false;
+ private static final Object sLock = new Object();
+
+ @GuardedBy("sLock")
+ private static int sAlsCounter;
private final int mStatsModality;
private final int mStatsAction;
@@ -345,13 +351,33 @@ public class BiometricLogger {
if (!mLightSensorEnabled) {
mLightSensorEnabled = true;
mLastAmbientLux = 0;
- mSensorManager.registerListener(mLightSensorListener, lightSensor,
- SensorManager.SENSOR_DELAY_NORMAL);
+ int localAlsCounter;
+ synchronized (sLock) {
+ localAlsCounter = sAlsCounter++;
+ }
+
+ if (localAlsCounter == 0) {
+ mSensorManager.registerListener(mLightSensorListener, lightSensor,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ } else {
+ Slog.e(TAG, "Ignoring request to subscribe to ALSProbe due to non-zero ALS"
+ + " counter: " + localAlsCounter);
+ Slog.e(TAG, Log.getStackTraceString(new Throwable()));
+ }
}
} else {
mLightSensorEnabled = false;
mLastAmbientLux = 0;
mSensorManager.unregisterListener(mLightSensorListener);
+ int localAlsCounter;
+ synchronized (sLock) {
+ localAlsCounter = --sAlsCounter;
+ }
+ if (localAlsCounter != 0) {
+ Slog.e(TAG, "Non-zero ALS counter after unsubscribing from ALSProbe: "
+ + localAlsCounter);
+ Slog.e(TAG, Log.getStackTraceString(new Throwable()));
+ }
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/UsageStats.java b/services/core/java/com/android/server/biometrics/sensors/face/UsageStats.java
index d99abcd4b3d2..9494547c7aaf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/UsageStats.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/UsageStats.java
@@ -70,20 +70,24 @@ public class UsageStats {
private int mAcceptCount;
private int mRejectCount;
- private SparseIntArray mErrorCount;
+ private int mErrorCount;
+ private int mAuthAttemptCount;
+ private SparseIntArray mErrorFrequencyMap;
private long mAcceptLatency;
private long mRejectLatency;
- private SparseLongArray mErrorLatency;
+ private long mErrorLatency;
+ private SparseLongArray mErrorLatencyMap;
public UsageStats(Context context) {
mAuthenticationEvents = new ArrayDeque<>();
- mErrorCount = new SparseIntArray();
- mErrorLatency = new SparseLongArray();
+ mErrorFrequencyMap = new SparseIntArray();
+ mErrorLatencyMap = new SparseLongArray();
mContext = context;
}
public void addEvent(AuthenticationEvent event) {
+ mAuthAttemptCount++;
if (mAuthenticationEvents.size() >= EVENT_LOG_SIZE) {
mAuthenticationEvents.removeFirst();
}
@@ -96,29 +100,38 @@ public class UsageStats {
mRejectCount++;
mRejectLatency += event.mLatency;
} else {
- mErrorCount.put(event.mError, mErrorCount.get(event.mError, 0) + 1);
- mErrorLatency.put(event.mError, mErrorLatency.get(event.mError, 0L) + event.mLatency);
+ mErrorCount++;
+ mErrorLatency += event.mLatency;
+ mErrorFrequencyMap.put(event.mError, mErrorFrequencyMap.get(event.mError, 0) + 1);
+ mErrorLatencyMap.put(event.mError,
+ mErrorLatencyMap.get(event.mError, 0L) + event.mLatency);
}
}
public void print(PrintWriter pw) {
- pw.println("Events since last reboot: " + mAuthenticationEvents.size());
+ pw.println("Printing most recent events since last reboot("
+ + mAuthenticationEvents.size() + " events)");
for (AuthenticationEvent event : mAuthenticationEvents) {
pw.println(event.toString(mContext));
}
// Dump aggregated usage stats
- pw.println("Accept\tCount: " + mAcceptCount + "\tLatency: " + mAcceptLatency
+ pw.println("");
+ pw.println("Accept Count: " + mAcceptCount + "\tLatency: " + mAcceptLatency
+ "\tAverage: " + (mAcceptCount > 0 ? mAcceptLatency / mAcceptCount : 0));
- pw.println("Reject\tCount: " + mRejectCount + "\tLatency: " + mRejectLatency
+ pw.println("Reject Count: " + mRejectCount + "\tLatency: " + mRejectLatency
+ "\tAverage: " + (mRejectCount > 0 ? mRejectLatency / mRejectCount : 0));
-
- for (int i = 0; i < mErrorCount.size(); i++) {
- final int key = mErrorCount.keyAt(i);
- final int count = mErrorCount.get(i);
+ pw.println("Total Error Count: " + mErrorCount + "\tLatency: " + mErrorLatency
+ + "\tAverage: " + (mErrorCount > 0 ? mErrorLatency / mErrorCount : 0));
+ pw.println("Total Attempts: " + mAuthAttemptCount);
+ pw.println("");
+
+ for (int i = 0; i < mErrorFrequencyMap.size(); i++) {
+ final int key = mErrorFrequencyMap.keyAt(i);
+ final int count = mErrorFrequencyMap.get(key);
pw.println("Error" + key + "\tCount: " + count
- + "\tLatency: " + mErrorLatency.get(key, 0L)
- + "\tAverage: " + (count > 0 ? mErrorLatency.get(key, 0L) / count : 0)
+ + "\tLatency: " + mErrorLatencyMap.get(key, 0L)
+ + "\tAverage: " + (count > 0 ? mErrorLatencyMap.get(key, 0L) / count : 0)
+ "\t" + FaceManager.getErrorString(mContext, key, 0 /* vendorCode */));
}
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 61350bb6095e..076ac2b4bfe7 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -215,9 +215,12 @@ public class ClipboardService extends SystemService {
private class ListenerInfo {
final int mUid;
final String mPackageName;
- ListenerInfo(int uid, String packageName) {
+ final String mAttributionTag;
+
+ ListenerInfo(int uid, String packageName, String attributionTag) {
mUid = uid;
mPackageName = packageName;
+ mAttributionTag = attributionTag;
}
}
@@ -355,27 +358,43 @@ public class ClipboardService extends SystemService {
}
@Override
- public void setPrimaryClip(ClipData clip, String callingPackage, @UserIdInt int userId) {
- checkAndSetPrimaryClip(clip, callingPackage, userId, callingPackage);
+ public void setPrimaryClip(
+ ClipData clip,
+ String callingPackage,
+ String attributionTag,
+ @UserIdInt int userId) {
+ checkAndSetPrimaryClip(clip, callingPackage, attributionTag, userId, callingPackage);
}
@Override
public void setPrimaryClipAsPackage(
- ClipData clip, String callingPackage, @UserIdInt int userId, String sourcePackage) {
+ ClipData clip,
+ String callingPackage,
+ String attributionTag,
+ @UserIdInt int userId,
+ String sourcePackage) {
getContext().enforceCallingOrSelfPermission(Manifest.permission.SET_CLIP_SOURCE,
"Requires SET_CLIP_SOURCE permission");
- checkAndSetPrimaryClip(clip, callingPackage, userId, sourcePackage);
+ checkAndSetPrimaryClip(clip, callingPackage, attributionTag, userId, sourcePackage);
}
private void checkAndSetPrimaryClip(
- ClipData clip, String callingPackage, @UserIdInt int userId, String sourcePackage) {
+ ClipData clip,
+ String callingPackage,
+ String attributionTag,
+ @UserIdInt int userId,
+ String sourcePackage) {
if (clip == null || clip.getItemCount() <= 0) {
throw new IllegalArgumentException("No items");
}
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
- if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,
- intendingUid, intendingUserId)) {
+ if (!clipboardAccessAllowed(
+ AppOpsManager.OP_WRITE_CLIPBOARD,
+ callingPackage,
+ attributionTag,
+ intendingUid,
+ intendingUserId)) {
return;
}
checkDataOwner(clip, intendingUid);
@@ -392,8 +411,13 @@ public class ClipboardService extends SystemService {
PROPERTY_AUTO_CLEAR_ENABLED, true)) {
mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
userId);
- Message clearMessage = Message.obtain(mClipboardClearHandler,
- ClipboardClearHandler.MSG_CLEAR, userId, intendingUid, userId);
+ Message clearMessage =
+ Message.obtain(
+ mClipboardClearHandler,
+ ClipboardClearHandler.MSG_CLEAR,
+ userId,
+ intendingUid,
+ userId);
mClipboardClearHandler.sendMessageDelayed(clearMessage,
getTimeoutForAutoClear());
}
@@ -409,11 +433,16 @@ public class ClipboardService extends SystemService {
}
@Override
- public void clearPrimaryClip(String callingPackage, @UserIdInt int userId) {
+ public void clearPrimaryClip(
+ String callingPackage, String attributionTag, @UserIdInt int userId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
- if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage,
- intendingUid, intendingUserId)) {
+ if (!clipboardAccessAllowed(
+ AppOpsManager.OP_WRITE_CLIPBOARD,
+ callingPackage,
+ attributionTag,
+ intendingUid,
+ intendingUserId)) {
return;
}
synchronized (mLock) {
@@ -424,11 +453,15 @@ public class ClipboardService extends SystemService {
}
@Override
- public ClipData getPrimaryClip(String pkg, @UserIdInt int userId) {
+ public ClipData getPrimaryClip(String pkg, String attributionTag, @UserIdInt int userId) {
final int intendingUid = getIntendingUid(pkg, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
- if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, pkg,
- intendingUid, intendingUserId)
+ if (!clipboardAccessAllowed(
+ AppOpsManager.OP_READ_CLIPBOARD,
+ pkg,
+ attributionTag,
+ intendingUid,
+ intendingUserId)
|| isDeviceLocked(intendingUserId)) {
return null;
}
@@ -453,12 +486,17 @@ public class ClipboardService extends SystemService {
}
@Override
- public ClipDescription getPrimaryClipDescription(String callingPackage,
- @UserIdInt int userId) {
+ public ClipDescription getPrimaryClipDescription(
+ String callingPackage, String attributionTag, @UserIdInt int userId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
- if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
- intendingUid, intendingUserId, false)
+ if (!clipboardAccessAllowed(
+ AppOpsManager.OP_READ_CLIPBOARD,
+ callingPackage,
+ attributionTag,
+ intendingUid,
+ intendingUserId,
+ false)
|| isDeviceLocked(intendingUserId)) {
return null;
}
@@ -470,11 +508,17 @@ public class ClipboardService extends SystemService {
}
@Override
- public boolean hasPrimaryClip(String callingPackage, @UserIdInt int userId) {
+ public boolean hasPrimaryClip(
+ String callingPackage, String attributionTag, @UserIdInt int userId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
- if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
- intendingUid, intendingUserId, false)
+ if (!clipboardAccessAllowed(
+ AppOpsManager.OP_READ_CLIPBOARD,
+ callingPackage,
+ attributionTag,
+ intendingUid,
+ intendingUserId,
+ false)
|| isDeviceLocked(intendingUserId)) {
return false;
}
@@ -484,19 +528,28 @@ public class ClipboardService extends SystemService {
}
@Override
- public void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,
- String callingPackage, @UserIdInt int userId) {
+ public void addPrimaryClipChangedListener(
+ IOnPrimaryClipChangedListener listener,
+ String callingPackage,
+ String attributionTag,
+ @UserIdInt int userId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
synchronized (mLock) {
- getClipboardLocked(intendingUserId).primaryClipListeners.register(listener,
- new ListenerInfo(intendingUid, callingPackage));
+ getClipboardLocked(intendingUserId)
+ .primaryClipListeners
+ .register(
+ listener,
+ new ListenerInfo(intendingUid, callingPackage, attributionTag));
}
}
@Override
- public void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,
- String callingPackage, @UserIdInt int userId) {
+ public void removePrimaryClipChangedListener(
+ IOnPrimaryClipChangedListener listener,
+ String callingPackage,
+ String attributionTag,
+ @UserIdInt int userId) {
final int intendingUserId = getIntendingUserId(callingPackage, userId);
synchronized (mLock) {
getClipboardLocked(intendingUserId).primaryClipListeners.unregister(listener);
@@ -504,11 +557,16 @@ public class ClipboardService extends SystemService {
}
@Override
- public boolean hasClipboardText(String callingPackage, int userId) {
+ public boolean hasClipboardText(String callingPackage, String attributionTag, int userId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
- if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
- intendingUid, intendingUserId, false)
+ if (!clipboardAccessAllowed(
+ AppOpsManager.OP_READ_CLIPBOARD,
+ callingPackage,
+ attributionTag,
+ intendingUid,
+ intendingUserId,
+ false)
|| isDeviceLocked(intendingUserId)) {
return false;
}
@@ -523,13 +581,19 @@ public class ClipboardService extends SystemService {
}
@Override
- public String getPrimaryClipSource(String callingPackage, int userId) {
+ public String getPrimaryClipSource(
+ String callingPackage, String attributionTag, int userId) {
getContext().enforceCallingOrSelfPermission(Manifest.permission.SET_CLIP_SOURCE,
"Requires SET_CLIP_SOURCE permission");
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
- if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
- intendingUid, intendingUserId, false)
+ if (!clipboardAccessAllowed(
+ AppOpsManager.OP_READ_CLIPBOARD,
+ callingPackage,
+ attributionTag,
+ intendingUid,
+ intendingUserId,
+ false)
|| isDeviceLocked(intendingUserId)) {
return null;
}
@@ -718,8 +782,12 @@ public class ClipboardService extends SystemService {
ListenerInfo li = (ListenerInfo)
clipboard.primaryClipListeners.getBroadcastCookie(i);
- if (clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, li.mPackageName,
- li.mUid, UserHandle.getUserId(li.mUid))) {
+ if (clipboardAccessAllowed(
+ AppOpsManager.OP_READ_CLIPBOARD,
+ li.mPackageName,
+ li.mAttributionTag,
+ li.mUid,
+ UserHandle.getUserId(li.mUid))) {
clipboard.primaryClipListeners.getBroadcastItem(i)
.dispatchPrimaryClipChanged();
}
@@ -965,13 +1033,18 @@ public class ClipboardService extends SystemService {
}
}
- private boolean clipboardAccessAllowed(int op, String callingPackage, int uid,
- @UserIdInt int userId) {
- return clipboardAccessAllowed(op, callingPackage, uid, userId, true);
+ private boolean clipboardAccessAllowed(
+ int op, String callingPackage, String attributionTag, int uid, @UserIdInt int userId) {
+ return clipboardAccessAllowed(op, callingPackage, attributionTag, uid, userId, true);
}
- private boolean clipboardAccessAllowed(int op, String callingPackage, int uid,
- @UserIdInt int userId, boolean shouldNoteOp) {
+ private boolean clipboardAccessAllowed(
+ int op,
+ String callingPackage,
+ String attributionTag,
+ int uid,
+ @UserIdInt int userId,
+ boolean shouldNoteOp) {
boolean allowed;
@@ -1040,7 +1113,7 @@ public class ClipboardService extends SystemService {
// Finally, check the app op.
int appOpsResult;
if (shouldNoteOp) {
- appOpsResult = mAppOps.noteOp(op, uid, callingPackage);
+ appOpsResult = mAppOps.noteOp(op, uid, callingPackage, attributionTag, null);
} else {
appOpsResult = mAppOps.checkOp(op, uid, callingPackage);
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 77d3392da993..f526960d0ef3 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -86,6 +86,8 @@ import android.net.ipsec.ike.ChildSessionConfiguration;
import android.net.ipsec.ike.ChildSessionParams;
import android.net.ipsec.ike.IkeSession;
import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.net.ipsec.ike.exceptions.IkeNetworkLostException;
@@ -168,9 +170,10 @@ import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -190,11 +193,19 @@ public class Vpn {
private static final long VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS = 60 * 1000;
// Length of time (in milliseconds) that an app registered for VpnManager events is placed on
- // the device idle allowlist each time the a VpnManager event is fired.
+ // the device idle allowlist each time the VpnManager event is fired.
private static final long VPN_MANAGER_EVENT_ALLOWLIST_DURATION_MS = 30 * 1000;
private static final String LOCKDOWN_ALLOWLIST_SETTING_NAME =
Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST;
+
+ /**
+ * The retries for consecutive failures.
+ *
+ * <p>If retries have exceeded the length of this array, the last entry in the array will be
+ * used as a repeating interval.
+ */
+ private static final long[] IKEV2_VPN_RETRY_DELAYS_SEC = {1L, 2L, 5L, 30L, 60L, 300L, 900L};
/**
* Largest profile size allowable for Platform VPNs.
*
@@ -473,6 +484,20 @@ public class Vpn {
"Cannot set tunnel's fd as blocking=" + blocking, e);
}
}
+
+ /**
+ * Retrieves the next retry delay
+ *
+ * <p>If retries have exceeded the IKEV2_VPN_RETRY_DELAYS_SEC, the last entry in
+ * the array will be used as a repeating interval.
+ */
+ public long getNextRetryDelaySeconds(int retryCount) {
+ if (retryCount >= IKEV2_VPN_RETRY_DELAYS_SEC.length) {
+ return IKEV2_VPN_RETRY_DELAYS_SEC[IKEV2_VPN_RETRY_DELAYS_SEC.length - 1];
+ } else {
+ return IKEV2_VPN_RETRY_DELAYS_SEC[retryCount];
+ }
+ }
}
public Vpn(Looper looper, Context context, INetworkManagementService netService, INetd netd,
@@ -2605,13 +2630,23 @@ public class Vpn {
void onDefaultNetworkLinkPropertiesChanged(@NonNull LinkProperties lp);
- void onChildOpened(
- @NonNull Network network, @NonNull ChildSessionConfiguration childConfig);
+ void onDefaultNetworkLost(@NonNull Network network);
+
+ void onIkeOpened(int token, @NonNull IkeSessionConfiguration ikeConfiguration);
+
+ void onIkeConnectionInfoChanged(
+ int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo);
- void onChildTransformCreated(
- @NonNull Network network, @NonNull IpSecTransform transform, int direction);
+ void onChildOpened(int token, @NonNull ChildSessionConfiguration childConfig);
- void onSessionLost(@NonNull Network network, @Nullable Exception exception);
+ void onChildTransformCreated(int token, @NonNull IpSecTransform transform, int direction);
+
+ void onChildMigrated(
+ int token,
+ @NonNull IpSecTransform inTransform,
+ @NonNull IpSecTransform outTransform);
+
+ void onSessionLost(int token, @Nullable Exception exception);
}
/**
@@ -2642,6 +2677,10 @@ public class Vpn {
class IkeV2VpnRunner extends VpnRunner implements IkeV2VpnRunnerCallback {
@NonNull private static final String TAG = "IkeV2VpnRunner";
+ // 5 seconds grace period before tearing down the IKE Session in case new default network
+ // will come up
+ private static final long NETWORK_LOST_TIMEOUT_MS = 5000L;
+
@NonNull private final IpSecManager mIpSecManager;
@NonNull private final Ikev2VpnProfile mProfile;
@NonNull private final ConnectivityManager.NetworkCallback mNetworkCallback;
@@ -2653,24 +2692,60 @@ public class Vpn {
* of the mutable Ikev2VpnRunner fields. The Ikev2VpnRunner is built mostly lock-free by
* virtue of everything being serialized on this executor.
*/
- @NonNull private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+ @NonNull
+ private final ScheduledThreadPoolExecutor mExecutor = new ScheduledThreadPoolExecutor(1);
+
+ @Nullable private ScheduledFuture<?> mScheduledHandleNetworkLostTimeout;
+ @Nullable private ScheduledFuture<?> mScheduledHandleRetryIkeSessionTimeout;
/** Signal to ensure shutdown is honored even if a new Network is connected. */
private boolean mIsRunning = true;
+ /**
+ * The token used by the primary/current/active IKE session.
+ *
+ * <p>This token MUST be updated when the VPN switches to use a new IKE session.
+ */
+ private int mCurrentToken = -1;
+
@Nullable private IpSecTunnelInterface mTunnelIface;
- @Nullable private IkeSession mSession;
@Nullable private Network mActiveNetwork;
@Nullable private NetworkCapabilities mUnderlyingNetworkCapabilities;
@Nullable private LinkProperties mUnderlyingLinkProperties;
private final String mSessionKey;
+ @Nullable private IkeSession mSession;
+ @Nullable private IkeSessionConnectionInfo mIkeConnectionInfo;
+
+ // mMobikeEnabled can only be updated after IKE AUTH is finished.
+ private boolean mMobikeEnabled = false;
+
+ /**
+ * The number of attempts since the last successful connection.
+ *
+ * <p>This variable controls the retry delay, and is reset when a new IKE session is
+ * opened or when there is a new default network.
+ */
+ private int mRetryCount = 0;
+
IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) {
super(TAG);
mProfile = profile;
mIpSecManager = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this, mExecutor);
mSessionKey = UUID.randomUUID().toString();
+
+ // Set the policy so that cancelled tasks will be removed from the work queue
+ mExecutor.setRemoveOnCancelPolicy(true);
+
+ // Set the policy so that all delayed tasks will not be executed
+ mExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
+
+ // To avoid hitting RejectedExecutionException upon shutdown of the mExecutor */
+ mExecutor.setRejectedExecutionHandler(
+ (r, executor) -> {
+ Log.d(TAG, "Runnable " + r + " rejected by the mExecutor");
+ });
}
@Override
@@ -2709,22 +2784,64 @@ public class Vpn {
return Objects.equals(mActiveNetwork, network) && mIsRunning;
}
+ private boolean isActiveToken(int token) {
+ return (mCurrentToken == token) && mIsRunning;
+ }
+
+ /**
+ * Called when an IKE session has been opened
+ *
+ * <p>This method is only ever called once per IkeSession, and MUST run on the mExecutor
+ * thread in order to ensure consistency of the Ikev2VpnRunner fields.
+ */
+ public void onIkeOpened(int token, @NonNull IkeSessionConfiguration ikeConfiguration) {
+ if (!isActiveToken(token)) {
+ Log.d(TAG, "onIkeOpened called for obsolete token " + token);
+ return;
+ }
+
+ mMobikeEnabled =
+ ikeConfiguration.isIkeExtensionEnabled(
+ IkeSessionConfiguration.EXTENSION_TYPE_MOBIKE);
+ onIkeConnectionInfoChanged(token, ikeConfiguration.getIkeSessionConnectionInfo());
+ mRetryCount = 0;
+ }
+
+ /**
+ * Called when an IKE session's {@link IkeSessionConnectionInfo} is available or updated
+ *
+ * <p>This callback is usually fired when an IKE session has been opened or migrated.
+ *
+ * <p>This method is called multiple times over the lifetime of an IkeSession, and MUST run
+ * on the mExecutor thread in order to ensure consistency of the Ikev2VpnRunner fields.
+ */
+ public void onIkeConnectionInfoChanged(
+ int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
+ if (!isActiveToken(token)) {
+ Log.d(TAG, "onIkeConnectionInfoChanged called for obsolete token " + token);
+ return;
+ }
+
+ // The update on VPN and the IPsec tunnel will be done when migration is fully complete
+ // in onChildMigrated
+ mIkeConnectionInfo = ikeConnectionInfo;
+ }
+
/**
* Called when an IKE Child session has been opened, signalling completion of the startup.
*
* <p>This method is only ever called once per IkeSession, and MUST run on the mExecutor
* thread in order to ensure consistency of the Ikev2VpnRunner fields.
*/
- public void onChildOpened(
- @NonNull Network network, @NonNull ChildSessionConfiguration childConfig) {
- if (!isActiveNetwork(network)) {
- Log.d(TAG, "onOpened called for obsolete network " + network);
+ public void onChildOpened(int token, @NonNull ChildSessionConfiguration childConfig) {
+ if (!isActiveToken(token)) {
+ Log.d(TAG, "onChildOpened called for obsolete token " + token);
// Do nothing; this signals that either: (1) a new/better Network was found,
- // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
- // IKE session was already shut down (exited, or an error was encountered somewhere
- // else). In both cases, all resources and sessions are torn down via
- // resetIkeState().
+ // and the Ikev2VpnRunner has switched to it by restarting a new IKE session in
+ // onDefaultNetworkChanged, or (2) this IKE session was already shut down (exited,
+ // or an error was encountered somewhere else). In both cases, all resources and
+ // sessions are torn down via resetIkeState().
return;
}
@@ -2743,6 +2860,11 @@ public class Vpn {
dnsAddrStrings.add(addr.getHostAddress());
}
+ // The actual network of this IKE session has been set up with is
+ // mIkeConnectionInfo.getNetwork() instead of mActiveNetwork because
+ // mActiveNetwork might have been updated after the setup was triggered.
+ final Network network = mIkeConnectionInfo.getNetwork();
+
final NetworkAgent networkAgent;
final LinkProperties lp;
@@ -2785,8 +2907,8 @@ public class Vpn {
networkAgent.sendLinkProperties(lp);
} catch (Exception e) {
- Log.d(TAG, "Error in ChildOpened for network " + network, e);
- onSessionLost(network, e);
+ Log.d(TAG, "Error in ChildOpened for token " + token, e);
+ onSessionLost(token, e);
}
}
@@ -2794,19 +2916,19 @@ public class Vpn {
* Called when an IPsec transform has been created, and should be applied.
*
* <p>This method is called multiple times over the lifetime of an IkeSession (or default
- * network), and is MUST always be called on the mExecutor thread in order to ensure
+ * network), and MUST always be called on the mExecutor thread in order to ensure
* consistency of the Ikev2VpnRunner fields.
*/
public void onChildTransformCreated(
- @NonNull Network network, @NonNull IpSecTransform transform, int direction) {
- if (!isActiveNetwork(network)) {
- Log.d(TAG, "ChildTransformCreated for obsolete network " + network);
+ int token, @NonNull IpSecTransform transform, int direction) {
+ if (!isActiveToken(token)) {
+ Log.d(TAG, "ChildTransformCreated for obsolete token " + token);
// Do nothing; this signals that either: (1) a new/better Network was found,
- // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
- // IKE session was already shut down (exited, or an error was encountered somewhere
- // else). In both cases, all resources and sessions are torn down via
- // resetIkeState().
+ // and the Ikev2VpnRunner has switched to it by restarting a new IKE session in
+ // onDefaultNetworkChanged, or (2) this IKE session was already shut down (exited,
+ // or an error was encountered somewhere else). In both cases, all resources and
+ // sessions are torn down via resetIkeState().
return;
}
@@ -2815,36 +2937,127 @@ public class Vpn {
// them alive for us
mIpSecManager.applyTunnelModeTransform(mTunnelIface, direction, transform);
} catch (IOException e) {
- Log.d(TAG, "Transform application failed for network " + network, e);
- onSessionLost(network, e);
+ Log.d(TAG, "Transform application failed for token " + token, e);
+ onSessionLost(token, e);
+ }
+ }
+
+ /**
+ * Called when an IPsec transform has been created, and should be re-applied.
+ *
+ * <p>This method is called multiple times over the lifetime of an IkeSession (or default
+ * network), and MUST always be called on the mExecutor thread in order to ensure
+ * consistency of the Ikev2VpnRunner fields.
+ */
+ public void onChildMigrated(
+ int token,
+ @NonNull IpSecTransform inTransform,
+ @NonNull IpSecTransform outTransform) {
+ if (!isActiveToken(token)) {
+ Log.d(TAG, "onChildMigrated for obsolete token " + token);
+ return;
+ }
+
+ // The actual network of this IKE session has migrated to is
+ // mIkeConnectionInfo.getNetwork() instead of mActiveNetwork because mActiveNetwork
+ // might have been updated after the migration was triggered.
+ final Network network = mIkeConnectionInfo.getNetwork();
+
+ try {
+ synchronized (Vpn.this) {
+ mConfig.underlyingNetworks = new Network[] {network};
+ mNetworkCapabilities =
+ new NetworkCapabilities.Builder(mNetworkCapabilities)
+ .setUnderlyingNetworks(Collections.singletonList(network))
+ .build();
+ mNetworkAgent.setUnderlyingNetworks(Collections.singletonList(network));
+ }
+
+ mTunnelIface.setUnderlyingNetwork(network);
+
+ // Transforms do not need to be persisted; the IkeSession will keep them alive for
+ // us
+ mIpSecManager.applyTunnelModeTransform(
+ mTunnelIface, IpSecManager.DIRECTION_IN, inTransform);
+ mIpSecManager.applyTunnelModeTransform(
+ mTunnelIface, IpSecManager.DIRECTION_OUT, outTransform);
+ } catch (IOException e) {
+ Log.d(TAG, "Transform application failed for token " + token, e);
+ onSessionLost(token, e);
}
}
/**
* Called when a new default network is connected.
*
- * <p>The Ikev2VpnRunner will unconditionally switch to the new network, killing the old IKE
- * state in the process, and starting a new IkeSession instance.
+ * <p>The Ikev2VpnRunner will unconditionally switch to the new network. If the IKE session
+ * has mobility, Ikev2VpnRunner will migrate the existing IkeSession to the new network.
+ * Otherwise, Ikev2VpnRunner will kill the old IKE state, and start a new IkeSession
+ * instance.
*
* <p>This method MUST always be called on the mExecutor thread in order to ensure
* consistency of the Ikev2VpnRunner fields.
*/
public void onDefaultNetworkChanged(@NonNull Network network) {
- Log.d(TAG, "Starting IKEv2/IPsec session on new network: " + network);
+ Log.d(TAG, "onDefaultNetworkChanged: " + network);
+
+ // If there is a new default network brought up, cancel the retry task to prevent
+ // establishing an unnecessary IKE session.
+ cancelRetryNewIkeSessionFuture();
+
+ // If there is a new default network brought up, cancel the obsolete reset and retry
+ // task.
+ cancelHandleNetworkLostTimeout();
+
+ if (!mIsRunning) {
+ Log.d(TAG, "onDefaultNetworkChanged after exit");
+ return; // VPN has been shut down.
+ }
+
+ mActiveNetwork = network;
+ mRetryCount = 0;
+
+ startOrMigrateIkeSession(network);
+ }
+
+ /**
+ * Start a new IKE session.
+ *
+ * <p>This method MUST always be called on the mExecutor thread in order to ensure
+ * consistency of the Ikev2VpnRunner fields.
+ *
+ * @param underlyingNetwork if the value is {@code null}, which means there is no active
+ * network can be used, do nothing and return immediately. Otherwise, use the
+ * given network to start a new IKE session.
+ */
+ private void startOrMigrateIkeSession(@Nullable Network underlyingNetwork) {
+ if (underlyingNetwork == null) {
+ Log.d(TAG, "There is no active network for starting an IKE session");
+ return;
+ }
try {
- if (!mIsRunning) {
- Log.d(TAG, "onDefaultNetworkChanged after exit");
- return; // VPN has been shut down.
+ if (mSession != null && mMobikeEnabled) {
+ // IKE session can schedule a migration event only when IKE AUTH is finished
+ // and mMobikeEnabled is true.
+ Log.d(
+ TAG,
+ "Migrate IKE Session with token "
+ + mCurrentToken
+ + " to network "
+ + underlyingNetwork);
+ mSession.setNetwork(underlyingNetwork);
+ return;
}
+ Log.d(TAG, "Start new IKE session on network " + underlyingNetwork);
+
// Clear mInterface to prevent Ikev2VpnRunner being cleared when
// interfaceRemoved() is called.
mInterface = null;
// Without MOBIKE, we have no way to seamlessly migrate. Close on old
// (non-default) network, and start the new one.
resetIkeState();
- mActiveNetwork = network;
// Get Ike options from IkeTunnelConnectionParams if it's available in the
// profile.
@@ -2854,12 +3067,12 @@ public class Vpn {
final ChildSessionParams childSessionParams;
if (ikeTunConnParams != null) {
final IkeSessionParams.Builder builder = new IkeSessionParams.Builder(
- ikeTunConnParams.getIkeSessionParams()).setNetwork(network);
+ ikeTunConnParams.getIkeSessionParams()).setNetwork(underlyingNetwork);
ikeSessionParams = builder.build();
childSessionParams = ikeTunConnParams.getTunnelModeChildSessionParams();
} else {
ikeSessionParams = VpnIkev2Utils.buildIkeSessionParams(
- mContext, mProfile, network);
+ mContext, mProfile, underlyingNetwork);
childSessionParams = VpnIkev2Utils.buildChildSessionParams(
mProfile.getAllowedAlgorithms());
}
@@ -2867,29 +3080,50 @@ public class Vpn {
// TODO: Remove the need for adding two unused addresses with
// IPsec tunnels.
final InetAddress address = InetAddress.getLocalHost();
+
+ // When onChildOpened is called and transforms are applied, it is
+ // guaranteed that the underlying network is still "network", because the
+ // all the network switch events will be deferred before onChildOpened is
+ // called. Thus it is safe to build a mTunnelIface before IKE setup.
mTunnelIface =
mIpSecManager.createIpSecTunnelInterface(
- address /* unused */,
- address /* unused */,
- network);
+ address /* unused */, address /* unused */, underlyingNetwork);
NetdUtils.setInterfaceUp(mNetd, mTunnelIface.getInterfaceName());
- mSession = mIkev2SessionCreator.createIkeSession(
- mContext,
- ikeSessionParams,
- childSessionParams,
- mExecutor,
- new VpnIkev2Utils.IkeSessionCallbackImpl(
- TAG, IkeV2VpnRunner.this, network),
- new VpnIkev2Utils.ChildSessionCallbackImpl(
- TAG, IkeV2VpnRunner.this, network));
- Log.d(TAG, "Ike Session started for network " + network);
+ final int token = ++mCurrentToken;
+ mSession =
+ mIkev2SessionCreator.createIkeSession(
+ mContext,
+ ikeSessionParams,
+ childSessionParams,
+ mExecutor,
+ new VpnIkev2Utils.IkeSessionCallbackImpl(
+ TAG, IkeV2VpnRunner.this, token),
+ new VpnIkev2Utils.ChildSessionCallbackImpl(
+ TAG, IkeV2VpnRunner.this, token));
+ Log.d(TAG, "IKE session started for token " + token);
} catch (Exception e) {
- Log.i(TAG, "Setup failed for network " + network + ". Aborting", e);
- onSessionLost(network, e);
+ Log.i(TAG, "Setup failed for token " + mCurrentToken + ". Aborting", e);
+ onSessionLost(mCurrentToken, e);
}
}
+ private void scheduleRetryNewIkeSession() {
+ final long retryDelay = mDeps.getNextRetryDelaySeconds(mRetryCount++);
+ Log.d(TAG, "Retry new IKE session after " + retryDelay + " seconds.");
+ // If the default network is lost during the retry delay, the mActiveNetwork will be
+ // null, and the new IKE session won't be established until there is a new default
+ // network bringing up.
+ mScheduledHandleRetryIkeSessionTimeout =
+ mExecutor.schedule(() -> {
+ startOrMigrateIkeSession(mActiveNetwork);
+
+ // Reset mScheduledHandleRetryIkeSessionTimeout since it's already run on
+ // executor thread.
+ mScheduledHandleRetryIkeSessionTimeout = null;
+ }, retryDelay, TimeUnit.SECONDS);
+ }
+
/** Called when the NetworkCapabilities of underlying network is changed */
public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) {
mUnderlyingNetworkCapabilities = nc;
@@ -2900,6 +3134,99 @@ public class Vpn {
mUnderlyingLinkProperties = lp;
}
+ /**
+ * Handles loss of the default underlying network
+ *
+ * <p>If the IKE Session has mobility, Ikev2VpnRunner will schedule a teardown event with a
+ * delay so that the IKE Session can migrate if a new network is available soon. Otherwise,
+ * Ikev2VpnRunner will kill the IKE session and reset the VPN.
+ *
+ * <p>This method MUST always be called on the mExecutor thread in order to ensure
+ * consistency of the Ikev2VpnRunner fields.
+ */
+ public void onDefaultNetworkLost(@NonNull Network network) {
+ // If the default network is torn down, there is no need to call
+ // startOrMigrateIkeSession() since it will always check if there is an active network
+ // can be used or not.
+ cancelRetryNewIkeSessionFuture();
+
+ if (!isActiveNetwork(network)) {
+ Log.d(TAG, "onDefaultNetworkLost called for obsolete network " + network);
+
+ // Do nothing; this signals that either: (1) a new/better Network was found,
+ // and the Ikev2VpnRunner has switched to it by restarting a new IKE session in
+ // onDefaultNetworkChanged, or (2) this IKE session was already shut down (exited,
+ // or an error was encountered somewhere else). In both cases, all resources and
+ // sessions are torn down via resetIkeState().
+ return;
+ } else {
+ mActiveNetwork = null;
+ }
+
+ if (mScheduledHandleNetworkLostTimeout != null
+ && !mScheduledHandleNetworkLostTimeout.isCancelled()
+ && !mScheduledHandleNetworkLostTimeout.isDone()) {
+ final IllegalStateException exception =
+ new IllegalStateException(
+ "Found a pending mScheduledHandleNetworkLostTimeout");
+ Log.i(
+ TAG,
+ "Unexpected error in onDefaultNetworkLost. Tear down session",
+ exception);
+ handleSessionLost(exception, network);
+ return;
+ }
+
+ if (mSession != null && mMobikeEnabled) {
+ Log.d(
+ TAG,
+ "IKE Session has mobility. Delay handleSessionLost for losing network "
+ + network
+ + " on session with token "
+ + mCurrentToken);
+
+ // Delay the teardown in case a new network will be available soon. For example,
+ // during handover between two WiFi networks, Android will disconnect from the
+ // first WiFi and then connects to the second WiFi.
+ mScheduledHandleNetworkLostTimeout =
+ mExecutor.schedule(
+ () -> {
+ handleSessionLost(null, network);
+ },
+ NETWORK_LOST_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS);
+ } else {
+ Log.d(TAG, "Call handleSessionLost for losing network " + network);
+ handleSessionLost(null, network);
+ }
+ }
+
+ private void cancelHandleNetworkLostTimeout() {
+ if (mScheduledHandleNetworkLostTimeout != null
+ && !mScheduledHandleNetworkLostTimeout.isDone()) {
+ // It does not matter what to put in #cancel(boolean), because it is impossible
+ // that the task tracked by mScheduledHandleNetworkLostTimeout is
+ // in-progress since both that task and onDefaultNetworkChanged are submitted to
+ // mExecutor who has only one thread.
+ Log.d(TAG, "Cancel the task for handling network lost timeout");
+ mScheduledHandleNetworkLostTimeout.cancel(false /* mayInterruptIfRunning */);
+ mScheduledHandleNetworkLostTimeout = null;
+ }
+ }
+
+ private void cancelRetryNewIkeSessionFuture() {
+ if (mScheduledHandleRetryIkeSessionTimeout != null
+ && !mScheduledHandleRetryIkeSessionTimeout.isDone()) {
+ // It does not matter what to put in #cancel(boolean), because it is impossible
+ // that the task tracked by mScheduledHandleRetryIkeSessionTimeout is
+ // in-progress since both that task and onDefaultNetworkChanged are submitted to
+ // mExecutor who has only one thread.
+ Log.d(TAG, "Cancel the task for handling new ike session timeout");
+ mScheduledHandleRetryIkeSessionTimeout.cancel(false /* mayInterruptIfRunning */);
+ mScheduledHandleRetryIkeSessionTimeout = null;
+ }
+ }
+
/** Marks the state as FAILED, and disconnects. */
private void markFailedAndDisconnect(Exception exception) {
synchronized (Vpn.this) {
@@ -2918,18 +3245,28 @@ public class Vpn {
* <p>This method MUST always be called on the mExecutor thread in order to ensure
* consistency of the Ikev2VpnRunner fields.
*/
- public void onSessionLost(@NonNull Network network, @Nullable Exception exception) {
- if (!isActiveNetwork(network)) {
- Log.d(TAG, "onSessionLost() called for obsolete network " + network);
+ public void onSessionLost(int token, @Nullable Exception exception) {
+ Log.d(TAG, "onSessionLost() called for token " + token);
+
+ if (!isActiveToken(token)) {
+ Log.d(TAG, "onSessionLost() called for obsolete token " + token);
// Do nothing; this signals that either: (1) a new/better Network was found,
- // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
- // IKE session was already shut down (exited, or an error was encountered somewhere
- // else). In both cases, all resources and sessions are torn down via
- // onSessionLost() and resetIkeState().
+ // and the Ikev2VpnRunner has switched to it by restarting a new IKE session in
+ // onDefaultNetworkChanged, or (2) this IKE session was already shut down (exited,
+ // or an error was encountered somewhere else). In both cases, all resources and
+ // sessions are torn down via resetIkeState().
return;
}
+ handleSessionLost(exception, mActiveNetwork);
+ }
+
+ private void handleSessionLost(@Nullable Exception exception, @Nullable Network network) {
+ // Cancel mScheduledHandleNetworkLostTimeout if the session it is going to terminate is
+ // already terminated due to other failures.
+ cancelHandleNetworkLostTimeout();
+
synchronized (Vpn.this) {
if (exception instanceof IkeProtocolException) {
final IkeProtocolException ikeException = (IkeProtocolException) exception;
@@ -2949,7 +3286,7 @@ public class Vpn {
VpnManager.ERROR_CLASS_NOT_RECOVERABLE,
ikeException.getErrorType(),
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
- mActiveNetwork,
+ network,
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
mUnderlyingNetworkCapabilities),
getRedactedLinkPropertiesOfUnderlyingNetwork(
@@ -2967,7 +3304,7 @@ public class Vpn {
VpnManager.ERROR_CLASS_RECOVERABLE,
ikeException.getErrorType(),
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
- mActiveNetwork,
+ network,
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
mUnderlyingNetworkCapabilities),
getRedactedLinkPropertiesOfUnderlyingNetwork(
@@ -2986,7 +3323,7 @@ public class Vpn {
VpnManager.ERROR_CLASS_RECOVERABLE,
VpnManager.ERROR_CODE_NETWORK_LOST,
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
- mActiveNetwork,
+ network,
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
mUnderlyingNetworkCapabilities),
getRedactedLinkPropertiesOfUnderlyingNetwork(
@@ -3001,7 +3338,7 @@ public class Vpn {
VpnManager.ERROR_CLASS_RECOVERABLE,
VpnManager.ERROR_CODE_NETWORK_UNKNOWN_HOST,
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
- mActiveNetwork,
+ network,
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
mUnderlyingNetworkCapabilities),
getRedactedLinkPropertiesOfUnderlyingNetwork(
@@ -3015,7 +3352,7 @@ public class Vpn {
VpnManager.ERROR_CLASS_RECOVERABLE,
VpnManager.ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT,
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
- mActiveNetwork,
+ network,
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
mUnderlyingNetworkCapabilities),
getRedactedLinkPropertiesOfUnderlyingNetwork(
@@ -3029,7 +3366,7 @@ public class Vpn {
VpnManager.ERROR_CLASS_RECOVERABLE,
VpnManager.ERROR_CODE_NETWORK_IO,
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
- mActiveNetwork,
+ network,
getRedactedNetworkCapabilitiesOfUnderlyingNetwork(
mUnderlyingNetworkCapabilities),
getRedactedLinkPropertiesOfUnderlyingNetwork(
@@ -3039,15 +3376,16 @@ public class Vpn {
} else if (exception != null) {
Log.wtf(TAG, "onSessionLost: exception = " + exception);
}
+
+ scheduleRetryNewIkeSession();
}
- mActiveNetwork = null;
mUnderlyingNetworkCapabilities = null;
mUnderlyingLinkProperties = null;
// Close all obsolete state, but keep VPN alive incase a usable network comes up.
// (Mirrors VpnService behavior)
- Log.d(TAG, "Resetting state for network: " + network);
+ Log.d(TAG, "Resetting state for token: " + mCurrentToken);
synchronized (Vpn.this) {
// Since this method handles non-fatal errors only, set mInterface to null to
@@ -3092,6 +3430,8 @@ public class Vpn {
mSession.kill(); // Kill here to make sure all resources are released immediately
mSession = null;
}
+ mIkeConnectionInfo = null;
+ mMobikeEnabled = false;
}
/**
diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
index 17058282d947..857c86de57ca 100644
--- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
+++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
@@ -68,6 +68,7 @@ import android.net.ipsec.ike.IkeRfc822AddrIdentification;
import android.net.ipsec.ike.IkeSaProposal;
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTrafficSelector;
import android.net.ipsec.ike.TunnelModeChildSessionParams;
@@ -107,6 +108,7 @@ public class VpnIkev2Utils {
new IkeSessionParams.Builder(context)
.setServerHostname(profile.getServerAddr())
.setNetwork(network)
+ .addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
.setLocalIdentification(localId)
.setRemoteIdentification(remoteId);
setIkeAuth(profile, ikeOptionsBuilder);
@@ -298,72 +300,79 @@ public class VpnIkev2Utils {
static class IkeSessionCallbackImpl implements IkeSessionCallback {
private final String mTag;
private final Vpn.IkeV2VpnRunnerCallback mCallback;
- private final Network mNetwork;
+ private final int mToken;
- IkeSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) {
+ IkeSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, int token) {
mTag = tag;
mCallback = callback;
- mNetwork = network;
+ mToken = token;
}
@Override
public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) {
- Log.d(mTag, "IkeOpened for network " + mNetwork);
- // Nothing to do here.
+ Log.d(mTag, "IkeOpened for token " + mToken);
+ mCallback.onIkeOpened(mToken, ikeSessionConfig);
}
@Override
public void onClosed() {
- Log.d(mTag, "IkeClosed for network " + mNetwork);
- mCallback.onSessionLost(mNetwork, null); // Server requested session closure. Retry?
+ Log.d(mTag, "IkeClosed for token " + mToken);
+ mCallback.onSessionLost(mToken, null); // Server requested session closure. Retry?
}
@Override
public void onClosedExceptionally(@NonNull IkeException exception) {
- Log.d(mTag, "IkeClosedExceptionally for network " + mNetwork, exception);
- mCallback.onSessionLost(mNetwork, exception);
+ Log.d(mTag, "IkeClosedExceptionally for token " + mToken, exception);
+ mCallback.onSessionLost(mToken, exception);
}
@Override
public void onError(@NonNull IkeProtocolException exception) {
- Log.d(mTag, "IkeError for network " + mNetwork, exception);
+ Log.d(mTag, "IkeError for token " + mToken, exception);
// Non-fatal, log and continue.
}
+
+ @Override
+ public void onIkeSessionConnectionInfoChanged(
+ @NonNull IkeSessionConnectionInfo connectionInfo) {
+ Log.d(mTag, "onIkeSessionConnectionInfoChanged for token " + mToken);
+ mCallback.onIkeConnectionInfoChanged(mToken, connectionInfo);
+ }
}
static class ChildSessionCallbackImpl implements ChildSessionCallback {
private final String mTag;
private final Vpn.IkeV2VpnRunnerCallback mCallback;
- private final Network mNetwork;
+ private final int mToken;
- ChildSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) {
+ ChildSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, int token) {
mTag = tag;
mCallback = callback;
- mNetwork = network;
+ mToken = token;
}
@Override
public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
- Log.d(mTag, "ChildOpened for network " + mNetwork);
- mCallback.onChildOpened(mNetwork, childConfig);
+ Log.d(mTag, "ChildOpened for token " + mToken);
+ mCallback.onChildOpened(mToken, childConfig);
}
@Override
public void onClosed() {
- Log.d(mTag, "ChildClosed for network " + mNetwork);
- mCallback.onSessionLost(mNetwork, null);
+ Log.d(mTag, "ChildClosed for token " + mToken);
+ mCallback.onSessionLost(mToken, null);
}
@Override
public void onClosedExceptionally(@NonNull IkeException exception) {
- Log.d(mTag, "ChildClosedExceptionally for network " + mNetwork, exception);
- mCallback.onSessionLost(mNetwork, exception);
+ Log.d(mTag, "ChildClosedExceptionally for token " + mToken, exception);
+ mCallback.onSessionLost(mToken, exception);
}
@Override
public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) {
- Log.d(mTag, "ChildTransformCreated; Direction: " + direction + "; network " + mNetwork);
- mCallback.onChildTransformCreated(mNetwork, transform, direction);
+ Log.d(mTag, "ChildTransformCreated; Direction: " + direction + "; token " + mToken);
+ mCallback.onChildTransformCreated(mToken, transform, direction);
}
@Override
@@ -371,8 +380,15 @@ public class VpnIkev2Utils {
// Nothing to be done; no references to the IpSecTransform are held by the
// Ikev2VpnRunner (or this callback class), and this transform will be closed by the
// IKE library.
- Log.d(mTag,
- "ChildTransformDeleted; Direction: " + direction + "; for network " + mNetwork);
+ Log.d(mTag, "ChildTransformDeleted; Direction: " + direction + "; for token " + mToken);
+ }
+
+ @Override
+ public void onIpSecTransformsMigrated(
+ @NonNull IpSecTransform inIpSecTransform,
+ @NonNull IpSecTransform outIpSecTransform) {
+ Log.d(mTag, "ChildTransformsMigrated; token " + mToken);
+ mCallback.onChildMigrated(mToken, inIpSecTransform, outIpSecTransform);
}
}
@@ -390,7 +406,7 @@ public class VpnIkev2Utils {
@Override
public void onAvailable(@NonNull Network network) {
- Log.d(mTag, "Starting IKEv2/IPsec session on new network: " + network);
+ Log.d(mTag, "onAvailable called for network: " + network);
mExecutor.execute(() -> mCallback.onDefaultNetworkChanged(network));
}
@@ -412,8 +428,8 @@ public class VpnIkev2Utils {
@Override
public void onLost(@NonNull Network network) {
- Log.d(mTag, "Tearing down; lost network: " + network);
- mExecutor.execute(() -> mCallback.onSessionLost(network, null));
+ Log.d(mTag, "onLost called for network: " + network);
+ mExecutor.execute(() -> mCallback.onDefaultNetworkLost(network));
}
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 03e18a04c2d5..8e00ccf68fb1 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -40,6 +40,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.SystemProperties;
+import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
@@ -144,15 +146,30 @@ public final class DeviceStateManagerService extends SystemService {
private Set<Integer> mDeviceStatesAvailableForAppRequests;
+ @VisibleForTesting
+ interface SystemPropertySetter {
+ void setDebugTracingDeviceStateProperty(String value);
+ }
+ @NonNull
+ private final SystemPropertySetter mSystemPropertySetter;
+
public DeviceStateManagerService(@NonNull Context context) {
this(context, DeviceStatePolicy.Provider
.fromResources(context.getResources())
.instantiate(context));
}
+ private DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy) {
+ this(context, policy, (value) -> {
+ SystemProperties.set("debug.tracing.device_state", value);
+ });
+ }
+
@VisibleForTesting
- DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy) {
+ DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy,
+ @NonNull SystemPropertySetter systemPropertySetter) {
super(context);
+ mSystemPropertySetter = systemPropertySetter;
// We use the DisplayThread because this service indirectly drives
// display (on/off) and window (position) events through its callbacks.
DisplayThread displayThread = DisplayThread.get();
@@ -462,6 +479,10 @@ public final class DeviceStateManagerService extends SystemService {
FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_STATE_CHANGED,
newState.getIdentifier(), !mCommittedState.isPresent());
+ String traceString = newState.getIdentifier() + ":" + newState.getName();
+ Trace.instantForTrack(
+ Trace.TRACE_TAG_SYSTEM_SERVER, "DeviceStateChanged", traceString);
+ mSystemPropertySetter.setDebugTracingDeviceStateProperty(traceString);
mCommittedState = Optional.of(newState);
mPendingState = Optional.empty();
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
index 767b2d18a69a..eccee52f37aa 100644
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -16,21 +16,31 @@
package com.android.server.display;
+import android.annotation.NonNull;
import android.content.Context;
import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManager;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.Temperature;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
import android.util.Slog;
-import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData;
+import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.Executor;
/**
* This class monitors various conditions, such as skin temperature throttling status, and limits
@@ -44,28 +54,54 @@ class BrightnessThrottler {
private final Injector mInjector;
private final Handler mHandler;
- private BrightnessThrottlingData mThrottlingData;
+ // We need a separate handler for unit testing. These two handlers are the same throughout the
+ // non-test code.
+ private final Handler mDeviceConfigHandler;
private final Runnable mThrottlingChangeCallback;
private final SkinThermalStatusObserver mSkinThermalStatusObserver;
+ private final DeviceConfigListener mDeviceConfigListener;
+ private final DeviceConfigInterface mDeviceConfig;
+
private int mThrottlingStatus;
+ private BrightnessThrottlingData mThrottlingData;
+ private BrightnessThrottlingData mDdcThrottlingData;
private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
private @BrightnessInfo.BrightnessMaxReason int mBrightnessMaxReason =
BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+ private String mUniqueDisplayId;
+
+ // The most recent string that has been set from DeviceConfig
+ private String mBrightnessThrottlingDataString;
+
+ // This is a collection of brightness throttling data that has been written as overrides from
+ // the DeviceConfig. This will always take priority over the display device config data.
+ private HashMap<String, BrightnessThrottlingData> mBrightnessThrottlingDataOverride =
+ new HashMap<>(1);
BrightnessThrottler(Handler handler, BrightnessThrottlingData throttlingData,
- Runnable throttlingChangeCallback) {
- this(new Injector(), handler, throttlingData, throttlingChangeCallback);
+ Runnable throttlingChangeCallback, String uniqueDisplayId) {
+ this(new Injector(), handler, handler, throttlingData, throttlingChangeCallback,
+ uniqueDisplayId);
}
- BrightnessThrottler(Injector injector, Handler handler, BrightnessThrottlingData throttlingData,
- Runnable throttlingChangeCallback) {
+ @VisibleForTesting
+ BrightnessThrottler(Injector injector, Handler handler, Handler deviceConfigHandler,
+ BrightnessThrottlingData throttlingData, Runnable throttlingChangeCallback,
+ String uniqueDisplayId) {
mInjector = injector;
+
mHandler = handler;
+ mDeviceConfigHandler = deviceConfigHandler;
mThrottlingData = throttlingData;
+ mDdcThrottlingData = throttlingData;
mThrottlingChangeCallback = throttlingChangeCallback;
mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
- resetThrottlingData(mThrottlingData);
+ mUniqueDisplayId = uniqueDisplayId;
+ mDeviceConfig = injector.getDeviceConfig();
+ mDeviceConfigListener = new DeviceConfigListener();
+
+ resetThrottlingData(mThrottlingData, mUniqueDisplayId);
}
boolean deviceSupportsThrottling() {
@@ -86,7 +122,7 @@ class BrightnessThrottler {
void stop() {
mSkinThermalStatusObserver.stopObserving();
-
+ mDeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigListener);
// We're asked to stop throttling, so reset brightness restrictions.
mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
mBrightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
@@ -97,9 +133,19 @@ class BrightnessThrottler {
mThrottlingStatus = THROTTLING_INVALID;
}
- void resetThrottlingData(BrightnessThrottlingData throttlingData) {
+ private void resetThrottlingData() {
+ resetThrottlingData(mDdcThrottlingData, mUniqueDisplayId);
+ }
+
+ void resetThrottlingData(BrightnessThrottlingData throttlingData, String displayId) {
stop();
- mThrottlingData = throttlingData;
+
+ mUniqueDisplayId = displayId;
+ mDdcThrottlingData = throttlingData;
+ mDeviceConfigListener.startListening();
+ reloadBrightnessThrottlingDataOverride();
+ mThrottlingData = mBrightnessThrottlingDataOverride.getOrDefault(mUniqueDisplayId,
+ throttlingData);
if (deviceSupportsThrottling()) {
mSkinThermalStatusObserver.startObserving();
@@ -173,14 +219,148 @@ class BrightnessThrottler {
private void dumpLocal(PrintWriter pw) {
pw.println("BrightnessThrottler:");
pw.println(" mThrottlingData=" + mThrottlingData);
+ pw.println(" mDdcThrottlingData=" + mDdcThrottlingData);
+ pw.println(" mUniqueDisplayId=" + mUniqueDisplayId);
pw.println(" mThrottlingStatus=" + mThrottlingStatus);
pw.println(" mBrightnessCap=" + mBrightnessCap);
pw.println(" mBrightnessMaxReason=" +
BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason));
+ pw.println(" mBrightnessThrottlingDataOverride=" + mBrightnessThrottlingDataOverride);
+ pw.println(" mBrightnessThrottlingDataString=" + mBrightnessThrottlingDataString);
mSkinThermalStatusObserver.dump(pw);
}
+ private String getBrightnessThrottlingDataString() {
+ return mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA,
+ /* defaultValue= */ null);
+ }
+
+ private boolean parseAndSaveData(@NonNull String strArray,
+ @NonNull HashMap<String, BrightnessThrottlingData> tempBrightnessThrottlingData) {
+ boolean validConfig = true;
+ String[] items = strArray.split(",");
+ int i = 0;
+
+ try {
+ String uniqueDisplayId = items[i++];
+
+ // number of throttling points
+ int noOfThrottlingPoints = Integer.parseInt(items[i++]);
+ List<ThrottlingLevel> throttlingLevels = new ArrayList<>(noOfThrottlingPoints);
+
+ // throttling level and point
+ for (int j = 0; j < noOfThrottlingPoints; j++) {
+ String severity = items[i++];
+ int status = parseThermalStatus(severity);
+
+ float brightnessPoint = parseBrightness(items[i++]);
+
+ throttlingLevels.add(new ThrottlingLevel(status, brightnessPoint));
+ }
+ BrightnessThrottlingData toSave =
+ DisplayDeviceConfig.BrightnessThrottlingData.create(throttlingLevels);
+ tempBrightnessThrottlingData.put(uniqueDisplayId, toSave);
+ } catch (NumberFormatException | IndexOutOfBoundsException
+ | UnknownThermalStatusException e) {
+ validConfig = false;
+ Slog.e(TAG, "Throttling data is invalid array: '" + strArray + "'", e);
+ }
+
+ if (i != items.length) {
+ validConfig = false;
+ }
+
+ return validConfig;
+ }
+
+ public void reloadBrightnessThrottlingDataOverride() {
+ HashMap<String, BrightnessThrottlingData> tempBrightnessThrottlingData =
+ new HashMap<>(1);
+ mBrightnessThrottlingDataString = getBrightnessThrottlingDataString();
+ boolean validConfig = true;
+ mBrightnessThrottlingDataOverride.clear();
+ if (mBrightnessThrottlingDataString != null) {
+ String[] throttlingDataSplits = mBrightnessThrottlingDataString.split(";");
+ for (String s : throttlingDataSplits) {
+ if (!parseAndSaveData(s, tempBrightnessThrottlingData)) {
+ validConfig = false;
+ break;
+ }
+ }
+
+ if (validConfig) {
+ mBrightnessThrottlingDataOverride.putAll(tempBrightnessThrottlingData);
+ tempBrightnessThrottlingData.clear();
+ }
+
+ } else {
+ Slog.w(TAG, "DeviceConfig BrightnessThrottlingData is null");
+ }
+ }
+
+ /**
+ * Listens to config data change and updates the brightness throttling data using
+ * DisplayManager#KEY_BRIGHTNESS_THROTTLING_DATA.
+ * The format should be a string similar to: "local:4619827677550801152,2,moderate,0.5,severe,
+ * 0.379518072;local:4619827677550801151,1,moderate,0.75"
+ * In this order:
+ * <displayId>,<no of throttling levels>,[<severity as string>,<brightness cap>]
+ * Where the latter part is repeated for each throttling level, and the entirety is repeated
+ * for each display, separated by a semicolon.
+ */
+ public class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
+ public Executor mExecutor = new HandlerExecutor(mDeviceConfigHandler);
+
+ public void startListening() {
+ mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ mExecutor, this);
+ }
+
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ reloadBrightnessThrottlingDataOverride();
+ resetThrottlingData();
+ }
+ }
+
+ private float parseBrightness(String intVal) throws NumberFormatException {
+ float value = Float.parseFloat(intVal);
+ if (value < PowerManager.BRIGHTNESS_MIN || value > PowerManager.BRIGHTNESS_MAX) {
+ throw new NumberFormatException("Brightness constraint value out of bounds.");
+ }
+ return value;
+ }
+
+ @PowerManager.ThermalStatus private int parseThermalStatus(@NonNull String value)
+ throws UnknownThermalStatusException {
+ switch (value) {
+ case "none":
+ return PowerManager.THERMAL_STATUS_NONE;
+ case "light":
+ return PowerManager.THERMAL_STATUS_LIGHT;
+ case "moderate":
+ return PowerManager.THERMAL_STATUS_MODERATE;
+ case "severe":
+ return PowerManager.THERMAL_STATUS_SEVERE;
+ case "critical":
+ return PowerManager.THERMAL_STATUS_CRITICAL;
+ case "emergency":
+ return PowerManager.THERMAL_STATUS_EMERGENCY;
+ case "shutdown":
+ return PowerManager.THERMAL_STATUS_SHUTDOWN;
+ default:
+ throw new UnknownThermalStatusException("Invalid Thermal Status: " + value);
+ }
+ }
+
+ private static class UnknownThermalStatusException extends Exception {
+ UnknownThermalStatusException(String message) {
+ super(message);
+ }
+ }
+
private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
private final Injector mInjector;
private final Handler mHandler;
@@ -258,5 +438,10 @@ class BrightnessThrottler {
return IThermalService.Stub.asInterface(
ServiceManager.getService(Context.THERMAL_SERVICE));
}
+
+ @NonNull
+ public DeviceConfigInterface getDeviceConfig() {
+ return DeviceConfigInterface.REAL;
+ }
}
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index a25ac210f9c8..b5aa7b14792b 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -279,11 +279,16 @@ public class DisplayDeviceConfig {
private HighBrightnessModeData mHbmData;
private DensityMapping mDensityMapping;
private String mLoadedFrom = null;
+ private Spline mSdrToHdrRatioSpline;
+ // Brightness Throttling data may be updated via the DeviceConfig. Here we store the original
+ // data, which comes from the ddc, and the current one, which may be the DeviceConfig
+ // overwritten value.
private BrightnessThrottlingData mBrightnessThrottlingData;
- private Spline mSdrToHdrRatioSpline;
+ private BrightnessThrottlingData mOriginalBrightnessThrottlingData;
- private DisplayDeviceConfig(Context context) {
+ @VisibleForTesting
+ DisplayDeviceConfig(Context context) {
mContext = context;
}
@@ -422,6 +427,10 @@ public class DisplayDeviceConfig {
return config;
}
+ void setBrightnessThrottlingData(BrightnessThrottlingData brightnessThrottlingData) {
+ mBrightnessThrottlingData = brightnessThrottlingData;
+ }
+
/**
* Return the brightness mapping nits array.
*
@@ -637,6 +646,7 @@ public class DisplayDeviceConfig {
+ ", mHbmData=" + mHbmData
+ ", mSdrToHdrRatioSpline=" + mSdrToHdrRatioSpline
+ ", mBrightnessThrottlingData=" + mBrightnessThrottlingData
+ + ", mOriginalBrightnessThrottlingData=" + mOriginalBrightnessThrottlingData
+ ", mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease
+ ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
+ ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease
@@ -682,7 +692,8 @@ public class DisplayDeviceConfig {
return config;
}
- private boolean initFromFile(File configFile) {
+ @VisibleForTesting
+ boolean initFromFile(File configFile) {
if (!configFile.exists()) {
// Display configuration files aren't required to exist.
return false;
@@ -932,6 +943,7 @@ public class DisplayDeviceConfig {
if (!badConfig) {
mBrightnessThrottlingData = BrightnessThrottlingData.create(throttlingLevels);
+ mOriginalBrightnessThrottlingData = mBrightnessThrottlingData;
}
}
@@ -1407,7 +1419,9 @@ public class DisplayDeviceConfig {
/**
* Container for brightness throttling data.
*/
- static class BrightnessThrottlingData {
+ public static class BrightnessThrottlingData {
+ public List<ThrottlingLevel> throttlingLevels;
+
static class ThrottlingLevel {
public @PowerManager.ThermalStatus int thermalStatus;
public float brightness;
@@ -1421,9 +1435,25 @@ public class DisplayDeviceConfig {
public String toString() {
return "[" + thermalStatus + "," + brightness + "]";
}
- }
- public List<ThrottlingLevel> throttlingLevels;
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ThrottlingLevel)) {
+ return false;
+ }
+ ThrottlingLevel otherThrottlingLevel = (ThrottlingLevel) obj;
+
+ return otherThrottlingLevel.thermalStatus == this.thermalStatus
+ && otherThrottlingLevel.brightness == this.brightness;
+ }
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result = 31 * result + thermalStatus;
+ result = 31 * result + Float.hashCode(brightness);
+ return result;
+ }
+ }
static public BrightnessThrottlingData create(List<ThrottlingLevel> throttlingLevels)
{
@@ -1482,12 +1512,30 @@ public class DisplayDeviceConfig {
+ "} ";
}
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof BrightnessThrottlingData)) {
+ return false;
+ }
+
+ BrightnessThrottlingData otherBrightnessThrottlingData = (BrightnessThrottlingData) obj;
+ return throttlingLevels.equals(otherBrightnessThrottlingData.throttlingLevels);
+ }
+
+ @Override
+ public int hashCode() {
+ return throttlingLevels.hashCode();
+ }
+
private BrightnessThrottlingData(List<ThrottlingLevel> inLevels) {
throttlingLevels = new ArrayList<>(inLevels.size());
for (ThrottlingLevel level : inLevels) {
throttlingLevels.add(new ThrottlingLevel(level.thermalStatus, level.brightness));
}
}
-
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d3e2966c6df0..6285ef1fdabd 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -485,7 +485,7 @@ public final class DisplayManagerService extends SystemService {
mUiHandler = UiThread.getHandler();
mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
mLogicalDisplayMapper = new LogicalDisplayMapper(mContext, mDisplayDeviceRepo,
- new LogicalDisplayListener(), mSyncRoot, mHandler, new DeviceStateToLayoutMap());
+ new LogicalDisplayListener(), mSyncRoot, mHandler);
mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
mBrightnessSynchronizer = new BrightnessSynchronizer(mContext);
Resources resources = mContext.getResources();
@@ -945,8 +945,7 @@ public final class DisplayManagerService extends SystemService {
private DisplayInfo getDisplayInfoInternal(int displayId, int callingUid) {
synchronized (mSyncRoot) {
- final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId,
- /* includeDisabledDisplays= */ true);
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
final DisplayInfo info =
getDisplayInfoForFrameRateOverride(display.getFrameRateOverrides(),
@@ -2129,18 +2128,16 @@ public final class DisplayManagerService extends SystemService {
}
void resetBrightnessConfigurations() {
- synchronized (mSyncRoot) {
- mPersistentDataStore.setBrightnessConfigurationForUser(null, mContext.getUserId(),
+ mPersistentDataStore.setBrightnessConfigurationForUser(null, mContext.getUserId(),
+ mContext.getPackageName());
+ mLogicalDisplayMapper.forEachLocked((logicalDisplay -> {
+ if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
+ return;
+ }
+ final String uniqueId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+ setBrightnessConfigurationForDisplayInternal(null, uniqueId, mContext.getUserId(),
mContext.getPackageName());
- mLogicalDisplayMapper.forEachLocked((logicalDisplay -> {
- if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
- return;
- }
- String uniqueId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
- setBrightnessConfigurationForDisplayInternal(null, uniqueId, mContext.getUserId(),
- mContext.getPackageName());
- }));
- }
+ }));
}
void setAutoBrightnessLoggingEnabled(boolean enabled) {
@@ -2817,16 +2814,15 @@ public final class DisplayManagerService extends SystemService {
}
/**
- * Returns the list of all enabled display ids, and disabled ones if specified.
+ * Returns the list of all display ids.
*/
@Override // Binder call
- public int[] getDisplayIds(boolean includeDisabledDisplays) {
+ public int[] getDisplayIds() {
final int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid,
- includeDisabledDisplays);
+ return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid);
}
} finally {
Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 6a57e4070f65..95c8fef12976 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -873,7 +873,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
});
mBrightnessThrottler.resetThrottlingData(
- mDisplayDeviceConfig.getBrightnessThrottlingData());
+ mDisplayDeviceConfig.getBrightnessThrottlingData(), mUniqueDisplayId);
}
private void sendUpdatePowerState() {
@@ -1842,7 +1842,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
() -> {
sendUpdatePowerStateLocked();
postBrightnessChangeRunnable();
- });
+ }, mUniqueDisplayId);
}
private void blockScreenOn() {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 90b9967eef59..d14902eaf8f5 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -522,10 +522,12 @@ final class LogicalDisplay {
// Set the layer stack.
device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack);
// Also inform whether the device is the same one sent to inputflinger for its layerstack.
+ // Prevent displays that are disabled from receiving input.
// TODO(b/188914255): Remove once input can dispatch against device vs layerstack.
device.setDisplayFlagsLocked(t,
- device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE
- ? SurfaceControl.DISPLAY_RECEIVES_INPUT : 0);
+ (isEnabled() && device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE)
+ ? SurfaceControl.DISPLAY_RECEIVES_INPUT
+ : 0);
// Set the color mode and allowed display mode.
if (device == mPrimaryDisplayDevice) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 34e8e75314ce..70c9e23c6af8 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -79,12 +79,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
private static final int MSG_TRANSITION_TO_PENDING_DEVICE_STATE = 1;
private static final int UPDATE_STATE_NEW = 0;
- private static final int UPDATE_STATE_UPDATED = 1;
- private static final int UPDATE_STATE_DISABLED = 2;
-
- private static final int UPDATE_STATE_MASK = 0x3;
-
- private static final int UPDATE_STATE_FLAG_TRANSITION = 0x100;
+ private static final int UPDATE_STATE_TRANSITION = 1;
+ private static final int UPDATE_STATE_UPDATED = 2;
/**
* Temporary display info, used for comparing display configurations.
@@ -170,7 +166,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
@NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
- @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap) {
+ @NonNull Handler handler) {
mSyncRoot = syncRoot;
mPowerManager = context.getSystemService(PowerManager.class);
mInteractive = mPowerManager.isInteractive();
@@ -185,7 +181,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
mDeviceStatesOnWhichToSleep = toSparseBooleanArray(context.getResources().getIntArray(
com.android.internal.R.array.config_deviceStatesOnWhichToSleep));
mDisplayDeviceRepo.addListener(this);
- mDeviceStateToLayoutMap = deviceStateToLayoutMap;
+ mDeviceStateToLayoutMap = new DeviceStateToLayoutMap();
}
@Override
@@ -222,29 +218,10 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
public LogicalDisplay getDisplayLocked(int displayId) {
- return getDisplayLocked(displayId, /* includeDisabled= */ false);
- }
-
- LogicalDisplay getDisplayLocked(int displayId, boolean includeDisabled) {
- LogicalDisplay display = mLogicalDisplays.get(displayId);
- if (display != null && (display.isEnabled() || includeDisabled)) {
- return display;
- }
- return null;
+ return mLogicalDisplays.get(displayId);
}
public LogicalDisplay getDisplayLocked(DisplayDevice device) {
- return getDisplayLocked(device, /* includeDisabled= */ false);
- }
-
- /**
- * Loops through the existing list of displays and returns one that is associated with the
- * specified display device.
- *
- * @param device The display device that should be associated with the LogicalDisplay.
- * @param includeDisabled True if this method should return disabled displays as well.
- */
- private LogicalDisplay getDisplayLocked(DisplayDevice device, boolean includeDisabled) {
if (device == null) {
return null;
}
@@ -252,32 +229,18 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
for (int i = 0; i < count; i++) {
final LogicalDisplay display = mLogicalDisplays.valueAt(i);
if (display.getPrimaryDisplayDeviceLocked() == device) {
- if (display.isEnabled() || includeDisabled) {
- return display;
- } else {
- return null;
- }
+ return display;
}
}
return null;
}
- // Returns display Ids, defaults to enabled only.
public int[] getDisplayIdsLocked(int callingUid) {
- return getDisplayIdsLocked(callingUid, /* includeDisabledDisplays= */ false);
- }
-
- // Returns display Ids, specified whether enabled only, or all displays.
- public int[] getDisplayIdsLocked(int callingUid, boolean includeDisabledDisplays) {
final int count = mLogicalDisplays.size();
int[] displayIds = new int[count];
int n = 0;
for (int i = 0; i < count; i++) {
LogicalDisplay display = mLogicalDisplays.valueAt(i);
- if (!includeDisabledDisplays && !display.isEnabled()) {
- continue; // Ignore disabled displays.
- }
-
DisplayInfo info = display.getDisplayInfoLocked();
if (info.hasAccess(callingUid)) {
displayIds[n++] = mLogicalDisplays.keyAt(i);
@@ -292,10 +255,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
public void forEachLocked(Consumer<LogicalDisplay> consumer) {
final int count = mLogicalDisplays.size();
for (int i = 0; i < count; i++) {
- LogicalDisplay display = mLogicalDisplays.valueAt(i);
- if (display.isEnabled()) {
- consumer.accept(display);
- }
+ consumer.accept(mLogicalDisplays.valueAt(i));
}
}
@@ -356,8 +316,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
// Find or create the LogicalDisplay to map the DisplayDevice to.
final int logicalDisplayId = displayLayout.getLogicalDisplayId();
- final LogicalDisplay logicalDisplay =
- getDisplayLocked(logicalDisplayId, /* includeDisabled= */ true);
+ final LogicalDisplay logicalDisplay = getDisplayLocked(logicalDisplayId);
if (logicalDisplay == null) {
Slog.w(TAG, "The logical display (" + address + "), is not available"
+ " for the display state " + deviceState);
@@ -493,7 +452,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
/**
- * Returns true if the device should be put to sleep or not.
+ * Returns if the device should be put to sleep or not.
*
* Includes a check to verify that the device state that we are moving to, {@code pendingState},
* is the same as the physical state of the device, {@code baseState}. Different values for
@@ -639,12 +598,9 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
display.updateLocked(mDisplayDeviceRepo);
- DisplayInfo newDisplayInfo = display.getDisplayInfoLocked();
-
- final int storedState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW);
- final int updateState = storedState & UPDATE_STATE_MASK;
- final boolean isTransitioning = (storedState & UPDATE_STATE_FLAG_TRANSITION) != 0;
- final boolean wasPreviouslyUpdated = updateState == UPDATE_STATE_UPDATED;
+ final DisplayInfo newDisplayInfo = display.getDisplayInfoLocked();
+ final int updateState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW);
+ final boolean wasPreviouslyUpdated = updateState != UPDATE_STATE_NEW;
// The display is no longer valid and needs to be removed.
if (!display.isValidLocked()) {
@@ -668,35 +624,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
continue;
- // The display has been newly disabled, we report this as a removed display but
- // don't actually remove it from our internal list in LogicalDisplayMapper. The reason
- // is that LogicalDisplayMapper assumes and relies on the fact that every DisplayDevice
- // has a LogicalDisplay wrapper, but certain displays that are unusable (like the inner
- // display on a folded foldable device) are not available for use by the system and
- // we keep them hidden. To do this, we mark those LogicalDisplays as "disabled".
- // Also, if the display is in TRANSITION but was previously reported as disabled
- // then keep it unreported.
- } else if (!display.isEnabled()
- || (display.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION
- && updateState == UPDATE_STATE_DISABLED)) {
- mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_DISABLED);
-
- // If we never told anyone about this display, nothing to do
- if (!wasPreviouslyUpdated) {
- continue;
- }
-
- // Remove from group
- final DisplayGroup displayGroup = getDisplayGroupLocked(
- getDisplayGroupIdFromDisplayIdLocked(displayId));
- if (displayGroup != null) {
- displayGroup.removeDisplayLocked(display);
- }
-
- Slog.i(TAG, "Removing (disabled) display: " + displayId);
- mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_REMOVED);
- continue;
-
// The display is new.
} else if (!wasPreviouslyUpdated) {
Slog.i(TAG, "Adding new display: " + displayId + ": " + newDisplayInfo);
@@ -716,7 +643,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
// The display is involved in a display layout transition
- } else if (isTransitioning) {
+ } else if (updateState == UPDATE_STATE_TRANSITION) {
mLogicalDisplaysToUpdate.put(displayId,
LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);
@@ -790,7 +717,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
final int id = mLogicalDisplaysToUpdate.keyAt(i);
- final LogicalDisplay display = getDisplayLocked(id, /* includeDisabled= */ true);
+ final LogicalDisplay display = getDisplayLocked(id);
if (DEBUG) {
final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
final String uniqueId = device == null ? "null" : device.getUniqueId();
@@ -798,7 +725,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
+ " with device=" + uniqueId);
}
mListener.onLogicalDisplayEventLocked(display, msg);
- if (msg == LOGICAL_DISPLAY_EVENT_REMOVED && !display.isValidLocked()) {
+ if (msg == LOGICAL_DISPLAY_EVENT_REMOVED) {
// We wait until we sent the EVENT_REMOVED event before actually removing the
// display.
mLogicalDisplays.delete(id);
@@ -918,8 +845,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
if (isTransitioning) {
setDisplayPhase(logicalDisplay, phase);
if (phase == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) {
- int oldState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW);
- mUpdatedLogicalDisplays.put(displayId, oldState | UPDATE_STATE_FLAG_TRANSITION);
+ mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_TRANSITION);
}
}
}
@@ -953,15 +879,14 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
// Now that we have a display-device, we need a LogicalDisplay to map it to. Find the
// right one, if it doesn't exist, create a new one.
final int logicalDisplayId = displayLayout.getLogicalDisplayId();
- LogicalDisplay newDisplay =
- getDisplayLocked(logicalDisplayId, /* includeDisabled= */ true);
+ LogicalDisplay newDisplay = getDisplayLocked(logicalDisplayId);
if (newDisplay == null) {
newDisplay = createNewLogicalDisplayLocked(
- /* displayDevice= */ null, logicalDisplayId);
+ null /*displayDevice*/, logicalDisplayId);
}
// Now swap the underlying display devices between the old display and the new display
- final LogicalDisplay oldDisplay = getDisplayLocked(device, /* includeDisabled= */ true);
+ final LogicalDisplay oldDisplay = getDisplayLocked(device);
if (newDisplay != oldDisplay) {
newDisplay.swapDisplaysLocked(oldDisplay);
}
@@ -978,14 +903,13 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
* Creates a new logical display for the specified device and display Id and adds it to the list
* of logical displays.
*
- * @param displayDevice The displayDevice to associate with the LogicalDisplay.
+ * @param device The device to associate with the LogicalDisplay.
* @param displayId The display ID to give the new display. If invalid, a new ID is assigned.
* @return The new logical display if created, null otherwise.
*/
- private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice displayDevice,
- int displayId) {
+ private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice device, int displayId) {
final int layerStack = assignLayerStackLocked(displayId);
- final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, displayDevice);
+ final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
display.updateLocked(mDisplayDeviceRepo);
mLogicalDisplays.put(displayId, display);
setDisplayPhase(display, LogicalDisplay.DISPLAY_PHASE_ENABLED);
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 8de150ac4124..223b8c181fea 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -956,6 +956,8 @@ public final class ColorDisplayService extends SystemService {
R.array.config_availableColorModes);
if (availableColorModes.length > 0) {
colorMode = availableColorModes[0];
+ } else {
+ colorMode = NOT_SET;
}
}
}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 63c5456c972b..7b60345caf87 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static com.android.server.wm.ActivityInterceptorCallback.DREAM_MANAGER_ORDERED_ID;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.TaskInfo;
@@ -45,6 +46,9 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -221,6 +225,10 @@ public final class DreamManagerService extends SystemService {
}
}
+ protected void requestStartDreamFromShell() {
+ requestDreamInternal();
+ }
+
private void requestDreamInternal() {
// Ask the power manager to nap. It will eventually call back into
// startDream() if/when it is appropriate to start dreaming.
@@ -275,6 +283,10 @@ public final class DreamManagerService extends SystemService {
}
}
+ protected void requestStopDreamFromShell() {
+ stopDreamInternal(true, "stopping dream from shell");
+ }
+
private void stopDreamInternal(boolean immediate, String reason) {
synchronized (mLock) {
stopDreamLocked(immediate, reason);
@@ -593,6 +605,14 @@ public final class DreamManagerService extends SystemService {
}
}
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err,
+ @NonNull String[] args, @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) throws RemoteException {
+ new DreamShellCommand(DreamManagerService.this, mPowerManager)
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
@Override // Binder call
public ComponentName[] getDreamComponents() {
return getDreamComponentsForUser(UserHandle.getCallingUserId());
diff --git a/services/core/java/com/android/server/dreams/DreamShellCommand.java b/services/core/java/com/android/server/dreams/DreamShellCommand.java
new file mode 100644
index 000000000000..eae7e80a89f1
--- /dev/null
+++ b/services/core/java/com/android/server/dreams/DreamShellCommand.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dreams;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+
+/**
+ * {@link DreamShellCommand} allows accessing dream functionality, including toggling dream state.
+ */
+public class DreamShellCommand extends ShellCommand {
+ private static final boolean DEBUG = true;
+ private static final String TAG = "DreamShellCommand";
+ private final @NonNull DreamManagerService mService;
+ private final @NonNull PowerManager mPowerManager;
+
+ DreamShellCommand(@NonNull DreamManagerService service, @NonNull PowerManager powerManager) {
+ mService = service;
+ mPowerManager = powerManager;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.ROOT_UID) {
+ Slog.e(TAG, "Must be root before calling Dream shell commands");
+ return -1;
+ }
+
+ if (TextUtils.isEmpty(cmd)) {
+ return super.handleDefaultCommands(cmd);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "onCommand:" + cmd);
+ }
+
+ switch (cmd) {
+ case "start-dreaming":
+ return startDreaming();
+ case "stop-dreaming":
+ return stopDreaming();
+ default:
+ return super.handleDefaultCommands(cmd);
+ }
+ }
+
+ private int startDreaming() {
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_PLUGGED_IN, "shell:cmd:android.service.dreams:DREAM");
+ mService.requestStartDreamFromShell();
+ return 0;
+ }
+
+ private int stopDreaming() {
+ mService.requestStopDreamFromShell();
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Dream manager (dreams) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" start-dreaming");
+ pw.println(" Start the currently configured dream.");
+ pw.println(" stop-dreaming");
+ pw.println(" Stops any active dream");
+ }
+}
diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
index e222c644da9e..d238dae634ad 100644
--- a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
+++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
@@ -27,8 +27,6 @@ import android.view.InputWindowHandle;
import android.view.SurfaceControl;
import android.view.WindowManager;
-import com.android.server.policy.WindowManagerPolicy;
-
/**
* An internal implementation of an {@link InputMonitor} that uses a spy window.
*
@@ -69,9 +67,7 @@ class GestureMonitorSpyWindow {
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.setInputWindowInfo(mInputSurface, mWindowHandle);
- // Gesture monitor should be above handwriting event surface, hence setting it to
- // WindowManagerPolicy.INPUT_DISPLAY_OVERLAY_LAYER + 1
- t.setLayer(mInputSurface, WindowManagerPolicy.INPUT_DISPLAY_OVERLAY_LAYER + 1);
+ t.setLayer(mInputSurface, Integer.MAX_VALUE);
t.setPosition(mInputSurface, 0, 0);
t.setCrop(mInputSurface, null /* crop to parent surface */);
t.show(mInputSurface);
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index 5438faa8793e..8180e66166d9 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -27,8 +27,6 @@ import android.view.InputWindowHandle;
import android.view.SurfaceControl;
import android.view.WindowManager;
-import com.android.server.policy.WindowManagerPolicy;
-
final class HandwritingEventReceiverSurface {
public static final String TAG = HandwritingEventReceiverSurface.class.getSimpleName();
@@ -38,8 +36,7 @@ final class HandwritingEventReceiverSurface {
// is above gesture monitors, then edge-back and swipe-up gestures won't work when this surface
// is intercepting.
// TODO(b/217538817): Specify the ordering in WM by usage.
- private static final int HANDWRITING_SURFACE_LAYER =
- WindowManagerPolicy.INPUT_DISPLAY_OVERLAY_LAYER;
+ private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE - 1;
private final InputWindowHandle mWindowHandle;
private final InputChannel mClientChannel;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1a1c265f568d..9d15ed33797f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5754,10 +5754,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public void onImeParentChanged() {
synchronized (ImfLock.class) {
- // Hide the IME method menu when the IME surface parent will change in
- // case seeing the dialog dismiss flickering during the next focused window
- // starting the input connection.
- mMenuController.hideInputMethodMenu();
+ // Hide the IME method menu only when the IME surface parent is changed by the
+ // input target changed, in case seeing the dialog dismiss flickering during
+ // the next focused window starting the input connection.
+ if (mLastImeTargetWindow != mCurFocusedWindow) {
+ mMenuController.hideInputMethodMenu();
+ }
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 111621da06ce..5b2188ac078e 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -1183,11 +1183,11 @@ public class ContextHubService extends IContextHubService.Stub {
}
} else {
Log.d(TAG, "BT adapter not available. Defaulting to disabled");
- if (mIsBtMainEnabled) {
+ if (forceUpdate || mIsBtMainEnabled) {
mIsBtMainEnabled = false;
mContextHubWrapper.onBtMainSettingChanged(mIsBtMainEnabled);
}
- if (mIsBtScanningEnabled) {
+ if (forceUpdate || mIsBtScanningEnabled) {
mIsBtScanningEnabled = false;
mContextHubWrapper.onBtScanningSettingChanged(mIsBtScanningEnabled);
}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 098e8f74749c..7d12ede754ef 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -46,7 +46,6 @@ import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.ContentRecordingSession;
-import android.window.WindowContainerToken;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -433,7 +432,7 @@ public final class MediaProjectionManagerService extends SystemService
private IBinder mToken;
private IBinder.DeathRecipient mDeathEater;
private boolean mRestoreSystemAlertWindow;
- private WindowContainerToken mTaskRecordingWindowContainerToken = null;
+ private IBinder mLaunchCookie = null;
MediaProjection(int type, int uid, String packageName, int targetSdkVersion,
boolean isPrivileged) {
@@ -609,14 +608,13 @@ public final class MediaProjectionManagerService extends SystemService
}
@Override // Binder call
- public void setTaskRecordingWindowContainerToken(WindowContainerToken token) {
- // TODO(b/221417940) set the task id to record from sysui, for the package chosen.
- mTaskRecordingWindowContainerToken = token;
+ public void setLaunchCookie(IBinder launchCookie) {
+ mLaunchCookie = launchCookie;
}
@Override // Binder call
- public WindowContainerToken getTaskRecordingWindowContainerToken() {
- return mTaskRecordingWindowContainerToken;
+ public IBinder getLaunchCookie() {
+ return mLaunchCookie;
}
public MediaProjectionInfo getProjectionInfo() {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index e6bc79679a8f..5a40b30551b6 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -1447,6 +1447,16 @@ abstract public class ManagedServices {
}
}
+ @VisibleForTesting
+ void reregisterService(final ComponentName cn, final int userId) {
+ // If rebinding a package that died, ensure it still has permission
+ // after the rebind delay
+ if (isPackageOrComponentAllowed(cn.getPackageName(), userId)
+ || isPackageOrComponentAllowed(cn.flattenToString(), userId)) {
+ registerService(cn, userId);
+ }
+ }
+
/**
* Inject a system service into the management list.
*/
@@ -1545,12 +1555,9 @@ abstract public class ManagedServices {
unbindService(this, name, userid);
if (!mServicesRebinding.contains(servicesBindingTag)) {
mServicesRebinding.add(servicesBindingTag);
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- registerService(name, userid);
- }
- }, ON_BINDING_DIED_REBIND_DELAY_MS);
+ mHandler.postDelayed(() ->
+ reregisterService(name, userid),
+ ON_BINDING_DIED_REBIND_DELAY_MS);
} else {
Slog.v(TAG, getCaption() + " not rebinding in user " + userid
+ " as a previous rebind attempt was made: " + name);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d1e0b0474b61..e589080786b5 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -517,7 +517,7 @@ public class NotificationManagerService extends SystemService {
private ActivityManagerInternal mAmi;
private IPackageManager mPackageManager;
private PackageManager mPackageManagerClient;
- private PackageManagerInternal mPackageManagerInternal;
+ PackageManagerInternal mPackageManagerInternal;
private PermissionPolicyInternal mPermissionPolicyInternal;
AudioManager mAudioManager;
AudioManagerInternal mAudioManagerInternal;
@@ -1794,6 +1794,7 @@ public class NotificationManagerService extends SystemService {
if (userHandle >= 0) {
cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle,
REASON_PROFILE_TURNED_OFF, null);
+ mSnoozeHelper.clearData(userHandle);
}
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
// turn off LED when user passes through lock screen
@@ -2458,11 +2459,11 @@ public class NotificationManagerService extends SystemService {
SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), (userId, r, muteOnReturn) -> {
try {
if (DBG) {
- Slog.d(TAG, "Reposting " + r.getKey());
+ Slog.d(TAG, "Reposting " + r.getKey() + " " + muteOnReturn);
}
enqueueNotificationInternal(r.getSbn().getPackageName(), r.getSbn().getOpPkg(),
r.getSbn().getUid(), r.getSbn().getInitialPid(), r.getSbn().getTag(),
- r.getSbn().getId(), r.getSbn().getNotification(), userId, true);
+ r.getSbn().getId(), r.getSbn().getNotification(), userId, muteOnReturn);
} catch (Exception e) {
Slog.e(TAG, "Cannot un-snooze notification", e);
}
@@ -7030,6 +7031,7 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mNotificationLock")
void snoozeLocked(NotificationRecord r) {
+ final List<NotificationRecord> recordsToSnooze = new ArrayList<>();
if (r.getSbn().isGroup()) {
final List<NotificationRecord> groupNotifications =
findCurrentAndSnoozedGroupNotificationsLocked(
@@ -7038,8 +7040,8 @@ public class NotificationManagerService extends SystemService {
if (r.getNotification().isGroupSummary()) {
// snooze all children
for (int i = 0; i < groupNotifications.size(); i++) {
- if (mKey != groupNotifications.get(i).getKey()) {
- snoozeNotificationLocked(groupNotifications.get(i));
+ if (!mKey.equals(groupNotifications.get(i).getKey())) {
+ recordsToSnooze.add(groupNotifications.get(i));
}
}
} else {
@@ -7049,8 +7051,8 @@ public class NotificationManagerService extends SystemService {
if (groupNotifications.size() == 2) {
// snooze summary and the one child
for (int i = 0; i < groupNotifications.size(); i++) {
- if (mKey != groupNotifications.get(i).getKey()) {
- snoozeNotificationLocked(groupNotifications.get(i));
+ if (!mKey.equals(groupNotifications.get(i).getKey())) {
+ recordsToSnooze.add(groupNotifications.get(i));
}
}
}
@@ -7058,7 +7060,15 @@ public class NotificationManagerService extends SystemService {
}
}
// snooze the notification
- snoozeNotificationLocked(r);
+ recordsToSnooze.add(r);
+
+ if (mSnoozeHelper.canSnooze(recordsToSnooze.size())) {
+ for (int i = 0; i < recordsToSnooze.size(); i++) {
+ snoozeNotificationLocked(recordsToSnooze.get(i));
+ }
+ } else {
+ Log.w(TAG, "Cannot snooze " + r.getKey() + ": too many snoozed notifications");
+ }
}
@@ -9798,7 +9808,7 @@ public class NotificationManagerService extends SystemService {
* notifications visible to the given listener.
*/
@GuardedBy("mNotificationLock")
- private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
+ NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
final int N = mNotificationList.size();
final ArrayList<NotificationListenerService.Ranking> rankings = new ArrayList<>();
@@ -10913,7 +10923,7 @@ public class NotificationManagerService extends SystemService {
TrimCache trimCache = new TrimCache(sbn);
for (final ManagedServiceInfo info : getServices()) {
- boolean sbnVisible = isVisibleToListener(sbn, r. getNotificationType(), info);
+ boolean sbnVisible = isVisibleToListener(sbn, r.getNotificationType(), info);
boolean oldSbnVisible = (oldSbn != null)
&& isVisibleToListener(oldSbn, old.getNotificationType(), info);
// This notification hasn't been and still isn't visible -> ignore.
@@ -10943,12 +10953,17 @@ public class NotificationManagerService extends SystemService {
info, oldSbnLightClone, update, null, REASON_USER_STOPPED));
continue;
}
-
// Grant access before listener is notified
final int targetUserId = (info.userid == UserHandle.USER_ALL)
? UserHandle.USER_SYSTEM : info.userid;
updateUriPermissions(r, old, info.component.getPackageName(), targetUserId);
+ mPackageManagerInternal.grantImplicitAccess(
+ targetUserId, null /* intent */,
+ UserHandle.getAppId(info.uid),
+ sbn.getUid(),
+ false /* direct */, false /* retainOnUpdate */);
+
final StatusBarNotification sbnToPost = trimCache.ForListener(info);
mHandler.post(() -> notifyPosted(info, sbnToPost, update));
}
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index 09ed56745e54..12324bff0c7a 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -16,6 +16,8 @@
package com.android.server.notification;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -175,12 +177,14 @@ public final class PermissionHelper {
mPermManager.revokeRuntimePermission(packageName, NOTIFICATION_PERMISSION,
userId, TAG);
}
+ int flagMask = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED;
+ flagMask = userSet || !grant ? flagMask | FLAG_PERMISSION_GRANTED_BY_DEFAULT : flagMask;
if (userSet) {
mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION,
- FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, userId);
+ flagMask, FLAG_PERMISSION_USER_SET, true, userId);
} else {
mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION,
- 0, FLAG_PERMISSION_USER_SET, true, userId);
+ flagMask, 0, true, userId);
}
} catch (RemoteException e) {
Slog.e(TAG, "Could not reach system server", e);
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 477b8da61e0f..066692d374d0 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -86,6 +86,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -1327,16 +1328,17 @@ public class PreferencesHelper implements RankingConfig {
return null;
}
NotificationChannelGroup group = r.groups.get(groupId).clone();
- group.setChannels(new ArrayList<>());
+ ArrayList channels = new ArrayList();
int N = r.channels.size();
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
if (includeDeleted || !nc.isDeleted()) {
if (groupId.equals(nc.getGroup())) {
- group.addChannel(nc);
+ channels.add(nc);
}
}
}
+ group.setChannels(channels);
return group;
}
}
@@ -1357,44 +1359,48 @@ public class PreferencesHelper implements RankingConfig {
public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
Objects.requireNonNull(pkg);
- Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
+ List<NotificationChannelGroup> groups = new ArrayList<>();
synchronized (mPackagePreferences) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
return ParceledListSlice.emptyList();
}
- NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
+ Map<String, ArrayList<NotificationChannel>> groupedChannels = new HashMap();
int N = r.channels.size();
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
if (includeDeleted || !nc.isDeleted()) {
if (nc.getGroup() != null) {
if (r.groups.get(nc.getGroup()) != null) {
- NotificationChannelGroup ncg = groups.get(nc.getGroup());
- if (ncg == null) {
- ncg = r.groups.get(nc.getGroup()).clone();
- ncg.setChannels(new ArrayList<>());
- groups.put(nc.getGroup(), ncg);
-
- }
- ncg.addChannel(nc);
+ ArrayList<NotificationChannel> channels = groupedChannels.getOrDefault(
+ nc.getGroup(), new ArrayList<>());
+ channels.add(nc);
+ groupedChannels.put(nc.getGroup(), channels);
}
} else {
- nonGrouped.addChannel(nc);
+ ArrayList<NotificationChannel> channels = groupedChannels.getOrDefault(
+ null, new ArrayList<>());
+ channels.add(nc);
+ groupedChannels.put(null, channels);
}
}
}
- if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
- groups.put(null, nonGrouped);
- }
- if (includeEmpty) {
- for (NotificationChannelGroup group : r.groups.values()) {
- if (!groups.containsKey(group.getId())) {
- groups.put(group.getId(), group);
- }
+ for (NotificationChannelGroup group : r.groups.values()) {
+ ArrayList<NotificationChannel> channels =
+ groupedChannels.getOrDefault(group.getId(), new ArrayList<>());
+ if (includeEmpty || !channels.isEmpty()) {
+ NotificationChannelGroup clone = group.clone();
+ clone.setChannels(channels);
+ groups.add(clone);
}
}
- return new ParceledListSlice<>(new ArrayList<>(groups.values()));
+
+ if (includeNonGrouped && groupedChannels.containsKey(null)) {
+ NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
+ nonGrouped.setChannels(groupedChannels.get(null));
+ groups.add(nonGrouped);
+ }
+ return new ParceledListSlice<>(groups);
}
}
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 7f265df3f416..61936df4a124 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -16,7 +16,6 @@
package com.android.server.notification;
import android.annotation.NonNull;
-import android.annotation.UserIdInt;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -25,7 +24,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Binder;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
@@ -38,18 +36,15 @@ import android.util.TypedXmlSerializer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.util.XmlUtils;
import com.android.server.pm.PackageManagerService;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -59,15 +54,15 @@ import java.util.Set;
/**
* NotificationManagerService helper for handling snoozed notifications.
*/
-public class SnoozeHelper {
+public final class SnoozeHelper {
public static final int XML_SNOOZED_NOTIFICATION_VERSION = 1;
+ static final int CONCURRENT_SNOOZE_LIMIT = 500;
+
protected static final String XML_TAG_NAME = "snoozed-notifications";
private static final String XML_SNOOZED_NOTIFICATION = "notification";
private static final String XML_SNOOZED_NOTIFICATION_CONTEXT = "context";
- private static final String XML_SNOOZED_NOTIFICATION_PKG = "pkg";
- private static final String XML_SNOOZED_NOTIFICATION_USER_ID = "user-id";
private static final String XML_SNOOZED_NOTIFICATION_KEY = "key";
//the time the snoozed notification should be reposted
private static final String XML_SNOOZED_NOTIFICATION_TIME = "time";
@@ -89,24 +84,19 @@ public class SnoozeHelper {
private AlarmManager mAm;
private final ManagedServices.UserProfiles mUserProfiles;
- // User id | package name : notification key : record.
- private ArrayMap<String, ArrayMap<String, NotificationRecord>>
- mSnoozedNotifications = new ArrayMap<>();
- // User id | package name : notification key : time-milliseconds .
+ // notification key : record.
+ private ArrayMap<String, NotificationRecord> mSnoozedNotifications = new ArrayMap<>();
+ // notification key : time-milliseconds .
// This member stores persisted snoozed notification trigger times. it persists through reboots
// It should have the notifications that haven't expired or re-posted yet
- private final ArrayMap<String, ArrayMap<String, Long>>
- mPersistedSnoozedNotifications = new ArrayMap<>();
- // User id | package name : notification key : creation ID .
+ private final ArrayMap<String, Long> mPersistedSnoozedNotifications = new ArrayMap<>();
+ // notification key : creation ID.
// This member stores persisted snoozed notification trigger context for the assistant
// it persists through reboots.
// It should have the notifications that haven't expired or re-posted yet
- private final ArrayMap<String, ArrayMap<String, String>>
+ private final ArrayMap<String, String>
mPersistedSnoozedNotificationsWithContext = new ArrayMap<>();
- // notification key : package.
- private ArrayMap<String, String> mPackages = new ArrayMap<>();
- // key : userId
- private ArrayMap<String, Integer> mUsers = new ArrayMap<>();
+
private Callback mCallback;
private final Object mLock = new Object();
@@ -123,27 +113,20 @@ public class SnoozeHelper {
mUserProfiles = userProfiles;
}
- private String getPkgKey(@UserIdInt int userId, String pkg) {
- return userId + "|" + pkg;
- }
-
- void cleanupPersistedContext(String key){
+ protected boolean canSnooze(int numberToSnooze) {
synchronized (mLock) {
- int userId = mUsers.get(key);
- String pkg = mPackages.get(key);
- removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
+ if ((mSnoozedNotifications.size() + numberToSnooze) > CONCURRENT_SNOOZE_LIMIT) {
+ return false;
+ }
}
+ return true;
}
@NonNull
protected Long getSnoozeTimeForUnpostedNotification(int userId, String pkg, String key) {
Long time = null;
synchronized (mLock) {
- ArrayMap<String, Long> snoozed =
- mPersistedSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (snoozed != null) {
- time = snoozed.get(key);
- }
+ time = mPersistedSnoozedNotifications.get(key);
}
if (time == null) {
time = 0L;
@@ -153,29 +136,26 @@ public class SnoozeHelper {
protected String getSnoozeContextForUnpostedNotification(int userId, String pkg, String key) {
synchronized (mLock) {
- ArrayMap<String, String> snoozed =
- mPersistedSnoozedNotificationsWithContext.get(getPkgKey(userId, pkg));
- if (snoozed != null) {
- return snoozed.get(key);
- }
+ return mPersistedSnoozedNotificationsWithContext.get(key);
}
- return null;
}
protected boolean isSnoozed(int userId, String pkg, String key) {
synchronized (mLock) {
- return mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))
- && mSnoozedNotifications.get(getPkgKey(userId, pkg)).containsKey(key);
+ return mSnoozedNotifications.containsKey(key);
}
}
protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) {
synchronized (mLock) {
- if (mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))) {
- return mSnoozedNotifications.get(getPkgKey(userId, pkg)).values();
+ ArrayList snoozed = new ArrayList();
+ for (NotificationRecord r : mSnoozedNotifications.values()) {
+ if (r.getUserId() == userId && r.getSbn().getPackageName().equals(pkg)) {
+ snoozed.add(r);
+ }
}
+ return snoozed;
}
- return Collections.EMPTY_LIST;
}
@NonNull
@@ -183,15 +163,11 @@ public class SnoozeHelper {
String groupKey, Integer userId) {
ArrayList<NotificationRecord> records = new ArrayList<>();
synchronized (mLock) {
- ArrayMap<String, NotificationRecord> allRecords =
- mSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (allRecords != null) {
- for (int i = 0; i < allRecords.size(); i++) {
- NotificationRecord r = allRecords.valueAt(i);
- String currentGroupKey = r.getSbn().getGroup();
- if (Objects.equals(currentGroupKey, groupKey)) {
- records.add(r);
- }
+ for (int i = 0; i < mSnoozedNotifications.size(); i++) {
+ NotificationRecord r = mSnoozedNotifications.valueAt(i);
+ if (r.getSbn().getPackageName().equals(pkg) && r.getUserId() == userId
+ && Objects.equals(r.getSbn().getGroup(), groupKey)) {
+ records.add(r);
}
}
}
@@ -200,31 +176,16 @@ public class SnoozeHelper {
protected NotificationRecord getNotification(String key) {
synchronized (mLock) {
- if (!mUsers.containsKey(key) || !mPackages.containsKey(key)) {
- Slog.w(TAG, "Snoozed data sets no longer agree for " + key);
- return null;
- }
- int userId = mUsers.get(key);
- String pkg = mPackages.get(key);
- ArrayMap<String, NotificationRecord> snoozed =
- mSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (snoozed == null) {
- return null;
- }
- return snoozed.get(key);
+ return mSnoozedNotifications.get(key);
}
}
protected @NonNull List<NotificationRecord> getSnoozed() {
synchronized (mLock) {
- // caller filters records based on the current user profiles and listener access, so just
- // return everything
+ // caller filters records based on the current user profiles and listener access,
+ // so just return everything
List<NotificationRecord> snoozed = new ArrayList<>();
- for (String userPkgKey : mSnoozedNotifications.keySet()) {
- ArrayMap<String, NotificationRecord> snoozedRecords =
- mSnoozedNotifications.get(userPkgKey);
- snoozed.addAll(snoozedRecords.values());
- }
+ snoozed.addAll(mSnoozedNotifications.values());
return snoozed;
}
}
@@ -233,15 +194,13 @@ public class SnoozeHelper {
* Snoozes a notification and schedules an alarm to repost at that time.
*/
protected void snooze(NotificationRecord record, long duration) {
- String pkg = record.getSbn().getPackageName();
String key = record.getKey();
- int userId = record.getUser().getIdentifier();
snooze(record);
- scheduleRepost(pkg, key, userId, duration);
+ scheduleRepost(key, duration);
Long activateAt = System.currentTimeMillis() + duration;
synchronized (mLock) {
- storeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications, activateAt);
+ mPersistedSnoozedNotifications.put(key, activateAt);
}
}
@@ -249,66 +208,33 @@ public class SnoozeHelper {
* Records a snoozed notification.
*/
protected void snooze(NotificationRecord record, String contextId) {
- int userId = record.getUser().getIdentifier();
if (contextId != null) {
synchronized (mLock) {
- storeRecordLocked(record.getSbn().getPackageName(), record.getKey(),
- userId, mPersistedSnoozedNotificationsWithContext, contextId);
+ mPersistedSnoozedNotificationsWithContext.put(record.getKey(), contextId);
}
}
snooze(record);
}
private void snooze(NotificationRecord record) {
- int userId = record.getUser().getIdentifier();
if (DEBUG) {
Slog.d(TAG, "Snoozing " + record.getKey());
}
synchronized (mLock) {
- storeRecordLocked(record.getSbn().getPackageName(), record.getKey(),
- userId, mSnoozedNotifications, record);
- }
- }
-
- private <T> void storeRecordLocked(String pkg, String key, Integer userId,
- ArrayMap<String, ArrayMap<String, T>> targets, T object) {
-
- mPackages.put(key, pkg);
- mUsers.put(key, userId);
- ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg));
- if (keyToValue == null) {
- keyToValue = new ArrayMap<>();
- }
- keyToValue.put(key, object);
- targets.put(getPkgKey(userId, pkg), keyToValue);
- }
-
- private <T> T removeRecordLocked(String pkg, String key, Integer userId,
- ArrayMap<String, ArrayMap<String, T>> targets) {
- T object = null;
- ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg));
- if (keyToValue == null) {
- return null;
- }
- object = keyToValue.remove(key);
- if (keyToValue.size() == 0) {
- targets.remove(getPkgKey(userId, pkg));
+ mSnoozedNotifications.put(record.getKey(), record);
}
- return object;
}
protected boolean cancel(int userId, String pkg, String tag, int id) {
synchronized (mLock) {
- ArrayMap<String, NotificationRecord> recordsForPkg =
- mSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (recordsForPkg != null) {
- final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
- for (Map.Entry<String, NotificationRecord> record : records) {
- final StatusBarNotification sbn = record.getValue().getSbn();
- if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
- record.getValue().isCanceled = true;
- return true;
- }
+ final Set<Map.Entry<String, NotificationRecord>> records =
+ mSnoozedNotifications.entrySet();
+ for (Map.Entry<String, NotificationRecord> record : records) {
+ final StatusBarNotification sbn = record.getValue().getSbn();
+ if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
+ && Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
+ record.getValue().isCanceled = true;
+ return true;
}
}
}
@@ -325,11 +251,9 @@ public class SnoozeHelper {
if (includeCurrentProfiles) {
userIds = mUserProfiles.getCurrentProfileIds();
}
- for (ArrayMap<String, NotificationRecord> snoozedRecords : mSnoozedNotifications.values()) {
- for (NotificationRecord r : snoozedRecords.values()) {
- if (userIds.binarySearch(r.getUserId()) >= 0) {
- r.isCanceled = true;
- }
+ for (NotificationRecord r : mSnoozedNotifications.values()) {
+ if (userIds.binarySearch(r.getUserId()) >= 0) {
+ r.isCanceled = true;
}
}
}
@@ -337,14 +261,12 @@ public class SnoozeHelper {
protected boolean cancel(int userId, String pkg) {
synchronized (mLock) {
- ArrayMap<String, NotificationRecord> records =
- mSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (records == null) {
- return false;
- }
- int N = records.size();
- for (int i = 0; i < N; i++) {
- records.valueAt(i).isCanceled = true;
+ int n = mSnoozedNotifications.size();
+ for (int i = 0; i < n; i++) {
+ final NotificationRecord r = mSnoozedNotifications.valueAt(i);
+ if (r.getSbn().getPackageName().equals(pkg) && r.getUserId() == userId) {
+ r.isCanceled = true;
+ }
}
return true;
}
@@ -355,20 +277,17 @@ public class SnoozeHelper {
*/
protected void update(int userId, NotificationRecord record) {
synchronized (mLock) {
- ArrayMap<String, NotificationRecord> records =
- mSnoozedNotifications.get(getPkgKey(userId, record.getSbn().getPackageName()));
- if (records == null) {
- return;
+ if (mSnoozedNotifications.containsKey(record.getKey())) {
+ mSnoozedNotifications.put(record.getKey(), record);
}
- records.put(record.getKey(), record);
}
}
protected void repost(String key, boolean muteOnReturn) {
synchronized (mLock) {
- Integer userId = mUsers.get(key);
- if (userId != null) {
- repost(key, userId, muteOnReturn);
+ final NotificationRecord r = mSnoozedNotifications.get(key);
+ if (r != null) {
+ repost(key, r.getUserId(), muteOnReturn);
}
}
}
@@ -376,43 +295,30 @@ public class SnoozeHelper {
protected void repost(String key, int userId, boolean muteOnReturn) {
NotificationRecord record;
synchronized (mLock) {
- final String pkg = mPackages.remove(key);
- mUsers.remove(key);
- removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications);
- removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
- ArrayMap<String, NotificationRecord> records =
- mSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (records == null) {
- return;
- }
- record = records.remove(key);
-
+ mPersistedSnoozedNotifications.remove(key);
+ mPersistedSnoozedNotificationsWithContext.remove(key);
+ record = mSnoozedNotifications.remove(key);
}
if (record != null && !record.isCanceled) {
- final PendingIntent pi = createPendingIntent(
- record.getSbn().getPackageName(), record.getKey(), userId);
+ final PendingIntent pi = createPendingIntent(record.getKey());
mAm.cancel(pi);
MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
.setType(MetricsProto.MetricsEvent.TYPE_OPEN));
- mCallback.repost(userId, record, muteOnReturn);
+ mCallback.repost(record.getUserId(), record, muteOnReturn);
}
}
protected void repostGroupSummary(String pkg, int userId, String groupKey) {
synchronized (mLock) {
- ArrayMap<String, NotificationRecord> recordsByKey
- = mSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (recordsByKey == null) {
- return;
- }
-
String groupSummaryKey = null;
- int N = recordsByKey.size();
- for (int i = 0; i < N; i++) {
- final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i);
- if (potentialGroupSummary.getSbn().isGroup()
+ int n = mSnoozedNotifications.size();
+ for (int i = 0; i < n; i++) {
+ final NotificationRecord potentialGroupSummary = mSnoozedNotifications.valueAt(i);
+ if (potentialGroupSummary.getSbn().getPackageName().equals(pkg)
+ && potentialGroupSummary.getUserId() == userId
+ && potentialGroupSummary.getSbn().isGroup()
&& potentialGroupSummary.getNotification().isGroupSummary()
&& groupKey.equals(potentialGroupSummary.getGroupKey())) {
groupSummaryKey = potentialGroupSummary.getKey();
@@ -421,16 +327,14 @@ public class SnoozeHelper {
}
if (groupSummaryKey != null) {
- NotificationRecord record = recordsByKey.remove(groupSummaryKey);
- mPackages.remove(groupSummaryKey);
- mUsers.remove(groupSummaryKey);
+ NotificationRecord record = mSnoozedNotifications.remove(groupSummaryKey);
if (record != null && !record.isCanceled) {
Runnable runnable = () -> {
MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
.setType(MetricsProto.MetricsEvent.TYPE_OPEN));
- mCallback.repost(userId, record, false);
+ mCallback.repost(record.getUserId(), record, false);
};
runnable.run();
}
@@ -440,20 +344,40 @@ public class SnoozeHelper {
protected void clearData(int userId, String pkg) {
synchronized (mLock) {
- ArrayMap<String, NotificationRecord> records =
- mSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (records == null) {
- return;
+ int n = mSnoozedNotifications.size();
+ for (int i = n - 1; i >= 0; i--) {
+ final NotificationRecord record = mSnoozedNotifications.valueAt(i);
+ if (record.getUserId() == userId && record.getSbn().getPackageName().equals(pkg)) {
+ mSnoozedNotifications.removeAt(i);
+ mPersistedSnoozedNotificationsWithContext.remove(record.getKey());
+ mPersistedSnoozedNotifications.remove(record.getKey());
+ Runnable runnable = () -> {
+ final PendingIntent pi = createPendingIntent(record.getKey());
+ mAm.cancel(pi);
+ MetricsLogger.action(record.getLogMaker()
+ .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
+ .setType(MetricsProto.MetricsEvent.TYPE_DISMISS));
+ };
+ runnable.run();
+ }
}
- for (int i = records.size() - 1; i >= 0; i--) {
- final NotificationRecord r = records.removeAt(i);
- if (r != null) {
- mPackages.remove(r.getKey());
- mUsers.remove(r.getKey());
+ }
+ }
+
+ protected void clearData(int userId) {
+ synchronized (mLock) {
+ int n = mSnoozedNotifications.size();
+ for (int i = n - 1; i >= 0; i--) {
+ final NotificationRecord record = mSnoozedNotifications.valueAt(i);
+ if (record.getUserId() == userId) {
+ mSnoozedNotifications.removeAt(i);
+ mPersistedSnoozedNotificationsWithContext.remove(record.getKey());
+ mPersistedSnoozedNotifications.remove(record.getKey());
+
Runnable runnable = () -> {
- final PendingIntent pi = createPendingIntent(pkg, r.getKey(), userId);
+ final PendingIntent pi = createPendingIntent(record.getKey());
mAm.cancel(pi);
- MetricsLogger.action(r.getLogMaker()
+ MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
.setType(MetricsProto.MetricsEvent.TYPE_DISMISS));
};
@@ -463,47 +387,38 @@ public class SnoozeHelper {
}
}
- private PendingIntent createPendingIntent(String pkg, String key, int userId) {
+ private PendingIntent createPendingIntent(String key) {
return PendingIntent.getBroadcast(mContext,
REQUEST_CODE_REPOST,
new Intent(REPOST_ACTION)
.setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
.setData(new Uri.Builder().scheme(REPOST_SCHEME).appendPath(key).build())
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .putExtra(EXTRA_KEY, key)
- .putExtra(EXTRA_USER_ID, userId),
+ .putExtra(EXTRA_KEY, key),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
}
public void scheduleRepostsForPersistedNotifications(long currentTime) {
synchronized (mLock) {
- for (ArrayMap<String, Long> snoozed : mPersistedSnoozedNotifications.values()) {
- for (int i = 0; i < snoozed.size(); i++) {
- String key = snoozed.keyAt(i);
- Long time = snoozed.valueAt(i);
- String pkg = mPackages.get(key);
- Integer userId = mUsers.get(key);
- if (time == null || pkg == null || userId == null) {
- Slog.w(TAG, "data out of sync: " + time + "|" + pkg + "|" + userId);
- continue;
- }
- if (time != null && time > currentTime) {
- scheduleRepostAtTime(pkg, key, userId, time);
- }
+ for (int i = 0; i < mPersistedSnoozedNotifications.size(); i++) {
+ String key = mPersistedSnoozedNotifications.keyAt(i);
+ Long time = mPersistedSnoozedNotifications.valueAt(i);
+ if (time != null && time > currentTime) {
+ scheduleRepostAtTime(key, time);
}
}
}
}
- private void scheduleRepost(String pkg, String key, int userId, long duration) {
- scheduleRepostAtTime(pkg, key, userId, System.currentTimeMillis() + duration);
+ private void scheduleRepost(String key, long duration) {
+ scheduleRepostAtTime(key, System.currentTimeMillis() + duration);
}
- private void scheduleRepostAtTime(String pkg, String key, int userId, long time) {
+ private void scheduleRepostAtTime(String key, long time) {
Runnable runnable = () -> {
final long identity = Binder.clearCallingIdentity();
try {
- final PendingIntent pi = createPendingIntent(pkg, key, userId);
+ final PendingIntent pi = createPendingIntent(key);
mAm.cancel(pi);
if (DEBUG) Slog.d(TAG, "Scheduling evaluate for " + new Date(time));
mAm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, pi);
@@ -517,37 +432,14 @@ public class SnoozeHelper {
public void dump(PrintWriter pw, NotificationManagerService.DumpFilter filter) {
synchronized (mLock) {
pw.println("\n Snoozed notifications:");
- for (String userPkgKey : mSnoozedNotifications.keySet()) {
+ for (String key : mSnoozedNotifications.keySet()) {
pw.print(INDENT);
- pw.println("key: " + userPkgKey);
- ArrayMap<String, NotificationRecord> snoozedRecords =
- mSnoozedNotifications.get(userPkgKey);
- Set<String> snoozedKeys = snoozedRecords.keySet();
- for (String key : snoozedKeys) {
- pw.print(INDENT);
- pw.print(INDENT);
- pw.print(INDENT);
- pw.println(key);
- }
+ pw.println("key: " + key);
}
pw.println("\n Pending snoozed notifications");
- for (String userPkgKey : mPersistedSnoozedNotifications.keySet()) {
+ for (String key : mPersistedSnoozedNotifications.keySet()) {
pw.print(INDENT);
- pw.println("key: " + userPkgKey);
- ArrayMap<String, Long> snoozedRecords =
- mPersistedSnoozedNotifications.get(userPkgKey);
- if (snoozedRecords == null) {
- continue;
- }
- Set<String> snoozedKeys = snoozedRecords.keySet();
- for (String key : snoozedKeys) {
- pw.print(INDENT);
- pw.print(INDENT);
- pw.print(INDENT);
- pw.print(key);
- pw.print(INDENT);
- pw.println(snoozedRecords.get(key));
- }
+ pw.println("key: " + key + " until: " + mPersistedSnoozedNotifications.get(key));
}
}
}
@@ -578,37 +470,22 @@ public class SnoozeHelper {
void insert(T t) throws IOException;
}
- private <T> void writeXml(TypedXmlSerializer out,
- ArrayMap<String, ArrayMap<String, T>> targets, String tag,
- Inserter<T> attributeInserter)
- throws IOException {
- final int M = targets.size();
- for (int i = 0; i < M; i++) {
+ private <T> void writeXml(TypedXmlSerializer out, ArrayMap<String, T> targets, String tag,
+ Inserter<T> attributeInserter) throws IOException {
+ for (int j = 0; j < targets.size(); j++) {
+ String key = targets.keyAt(j);
// T is a String (snoozed until context) or Long (snoozed until time)
- ArrayMap<String, T> keyToValue = targets.valueAt(i);
- for (int j = 0; j < keyToValue.size(); j++) {
- String key = keyToValue.keyAt(j);
- T value = keyToValue.valueAt(j);
- String pkg = mPackages.get(key);
- Integer userId = mUsers.get(key);
-
- if (pkg == null || userId == null) {
- Slog.w(TAG, "pkg " + pkg + " or user " + userId + " missing for " + key);
- continue;
- }
+ T value = targets.valueAt(j);
- out.startTag(null, tag);
+ out.startTag(null, tag);
- attributeInserter.insert(value);
+ attributeInserter.insert(value);
- out.attributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
- XML_SNOOZED_NOTIFICATION_VERSION);
- out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key);
- out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg);
- out.attributeInt(null, XML_SNOOZED_NOTIFICATION_USER_ID, userId);
+ out.attributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
+ XML_SNOOZED_NOTIFICATION_VERSION);
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key);
- out.endTag(null, tag);
- }
+ out.endTag(null, tag);
}
}
@@ -628,16 +505,12 @@ public class SnoozeHelper {
== XML_SNOOZED_NOTIFICATION_VERSION) {
try {
final String key = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_KEY);
- final String pkg = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_PKG);
- final int userId = parser.getAttributeInt(
- null, XML_SNOOZED_NOTIFICATION_USER_ID, UserHandle.USER_ALL);
if (tag.equals(XML_SNOOZED_NOTIFICATION)) {
final Long time = parser.getAttributeLong(
null, XML_SNOOZED_NOTIFICATION_TIME, 0);
if (time > currentTime) { //only read new stuff
synchronized (mLock) {
- storeRecordLocked(
- pkg, key, userId, mPersistedSnoozedNotifications, time);
+ mPersistedSnoozedNotifications.put(key, time);
}
}
}
@@ -645,9 +518,7 @@ public class SnoozeHelper {
final String creationId = parser.getAttributeValue(
null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID);
synchronized (mLock) {
- storeRecordLocked(
- pkg, key, userId, mPersistedSnoozedNotificationsWithContext,
- creationId);
+ mPersistedSnoozedNotificationsWithContext.put(key, creationId);
}
}
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 9e0c97502c4f..6135fe8acbed 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -121,6 +121,7 @@ public class ZenModeHelper {
protected final RingerModeDelegate mRingerModeDelegate = new
RingerModeDelegate();
@VisibleForTesting protected final ZenModeConditions mConditions;
+ Object mConfigsLock = new Object();
@VisibleForTesting final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>();
private final Metrics mMetrics = new Metrics();
private final ConditionProviders.Config mServiceConfig;
@@ -153,7 +154,9 @@ public class ZenModeHelper {
mDefaultConfig = readDefaultConfig(mContext.getResources());
updateDefaultAutomaticRuleNames();
mConfig = mDefaultConfig.copy();
- mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
+ synchronized (mConfigsLock) {
+ mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
+ }
mConsolidatedPolicy = mConfig.toNotificationPolicy();
mSettingsObserver = new SettingsObserver(mHandler);
@@ -233,7 +236,9 @@ public class ZenModeHelper {
public void onUserRemoved(int user) {
if (user < UserHandle.USER_SYSTEM) return;
if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user);
- mConfigs.remove(user);
+ synchronized (mConfigsLock) {
+ mConfigs.remove(user);
+ }
}
public void onUserUnlocked(int user) {
@@ -248,7 +253,12 @@ public class ZenModeHelper {
if (mUser == user || user < UserHandle.USER_SYSTEM) return;
mUser = user;
if (DEBUG) Log.d(TAG, reason + " u=" + user);
- ZenModeConfig config = mConfigs.get(user);
+ ZenModeConfig config = null;
+ synchronized (mConfigsLock) {
+ if (mConfigs.get(user) != null) {
+ config = mConfigs.get(user).copy();
+ }
+ }
if (config == null) {
if (DEBUG) Log.d(TAG, reason + " generating default config for user " + user);
config = mDefaultConfig.copy();
@@ -330,7 +340,8 @@ public class ZenModeHelper {
int newRuleInstanceCount = getCurrentInstanceCount(automaticZenRule.getOwner())
+ getCurrentInstanceCount(automaticZenRule.getConfigurationActivity())
+ 1;
- if (newRuleInstanceCount > RULE_LIMIT_PER_PACKAGE
+ int newPackageRuleCount = getPackageRuleCount(pkg) + 1;
+ if (newPackageRuleCount > RULE_LIMIT_PER_PACKAGE
|| (ruleInstanceLimit > 0 && ruleInstanceLimit < newRuleInstanceCount)) {
throw new IllegalArgumentException("Rule instance limit exceeded");
}
@@ -511,6 +522,23 @@ public class ZenModeHelper {
return count;
}
+ // Equivalent method to getCurrentInstanceCount, but for all rules associated with a specific
+ // package rather than a condition provider service or activity.
+ private int getPackageRuleCount(String pkg) {
+ if (pkg == null) {
+ return 0;
+ }
+ int count = 0;
+ synchronized (mConfig) {
+ for (ZenRule rule : mConfig.automaticRules.values()) {
+ if (pkg.equals(rule.getPkg())) {
+ count++;
+ }
+ }
+ }
+ return count;
+ }
+
public boolean canManageAutomaticZenRule(ZenRule rule) {
final int callingUid = Binder.getCallingUid();
if (callingUid == 0 || callingUid == Process.SYSTEM_UID) {
@@ -685,9 +713,11 @@ public class ZenModeHelper {
pw.println(Global.zenModeToString(mZenMode));
pw.print(prefix);
pw.println("mConsolidatedPolicy=" + mConsolidatedPolicy.toString());
- final int N = mConfigs.size();
- for (int i = 0; i < N; i++) {
- dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
+ synchronized(mConfigsLock) {
+ final int N = mConfigs.size();
+ for (int i = 0; i < N; i++) {
+ dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
+ }
}
pw.print(prefix); pw.print("mUser="); pw.println(mUser);
synchronized (mConfig) {
@@ -787,7 +817,7 @@ public class ZenModeHelper {
public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId)
throws IOException {
- synchronized (mConfigs) {
+ synchronized (mConfigsLock) {
final int n = mConfigs.size();
for (int i = 0; i < n; i++) {
if (forBackup && mConfigs.keyAt(i) != userId) {
@@ -883,14 +913,18 @@ public class ZenModeHelper {
}
if (config.user != mUser) {
// simply store away for background users
- mConfigs.put(config.user, config);
+ synchronized (mConfigsLock) {
+ mConfigs.put(config.user, config);
+ }
if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
return true;
}
// handle CPS backed conditions - danger! may modify config
mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
- mConfigs.put(config.user, config);
+ synchronized (mConfigsLock) {
+ mConfigs.put(config.user, config);
+ }
if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
ZenLog.traceConfig(reason, mConfig, config);
@@ -1211,7 +1245,7 @@ public class ZenModeHelper {
* Generate pulled atoms about do not disturb configurations.
*/
public void pullRules(List<StatsEvent> events) {
- synchronized (mConfig) {
+ synchronized (mConfigsLock) {
final int numConfigs = mConfigs.size();
for (int i = 0; i < numConfigs; i++) {
final int user = mConfigs.keyAt(i);
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 0dc188b75d5e..46f0dbcdfe10 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -427,8 +427,7 @@ final class ScanPackageUtils {
pkgSetting.setLastModifiedTime(scanFileTime);
// TODO(b/135203078): Remove, move to constructor
pkgSetting.setPkg(parsedPackage)
- .setFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting))
- .setPrivateFlags(
+ .setPkgFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting));
if (parsedPackage.getLongVersionCode() != pkgSetting.getVersionCode()) {
pkgSetting.setLongVersionCode(parsedPackage.getLongVersionCode());
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index b952f80850bb..61a251e19db7 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -146,6 +146,17 @@ public abstract class SettingBase implements Watchable, Snappable {
return this;
}
+ /**
+ * Unconditionally set both mPkgFlags and mPkgPrivateFlags.
+ * Should not be used outside pkgSetting initialization or update.
+ */
+ SettingBase setPkgFlags(int flags, int privateFlags) {
+ this.mPkgFlags = flags;
+ this.mPkgPrivateFlags = privateFlags;
+ onChanged();
+ return this;
+ }
+
public int getFlags() {
return mPkgFlags;
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6400502f1a89..7437b145189f 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -987,8 +987,7 @@ public final class Settings implements Watchable, Snappable {
// Update new package state.
.setLastModifiedTime(codePath.lastModified())
.setDomainSetId(domainSetId);
- pkgSetting.setFlags(pkgFlags)
- .setPrivateFlags(pkgPrivateFlags);
+ pkgSetting.setPkgFlags(pkgFlags, pkgPrivateFlags);
} else {
pkgSetting = new PackageSetting(pkgName, realPkgName, codePath,
legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
@@ -1175,15 +1174,15 @@ public final class Settings implements Watchable, Snappable {
.setUsesStaticLibrariesVersions(null);
}
- // These two flags are preserved from the existing PackageSetting. Copied from prior code,
- // unclear if this is actually necessary.
- boolean wasExternalStorage = (pkgSetting.getFlags()
- & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
- if (wasExternalStorage) {
- pkgFlags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
- } else {
- pkgFlags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE;
- }
+ // If what we are scanning is a system (and possibly privileged) package,
+ // then make it so, regardless of whether it was previously installed only
+ // in the data partition. Reset first.
+ int newPkgFlags = pkgSetting.getFlags();
+ newPkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
+ newPkgFlags |= pkgFlags & ApplicationInfo.FLAG_SYSTEM;
+ // Only set pkgFlags.
+ pkgSetting.setPkgFlags(newPkgFlags, pkgSetting.getPrivateFlags());
+
boolean wasRequiredForSystemUser = (pkgSetting.getPrivateFlags()
& ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0;
if (wasRequiredForSystemUser) {
@@ -1191,9 +1190,7 @@ public final class Settings implements Watchable, Snappable {
} else {
pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
}
-
- pkgSetting.setFlags(pkgFlags)
- .setPrivateFlags(pkgPrivateFlags);
+ pkgSetting.setPrivateFlags(pkgPrivateFlags);
}
/**
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index c0c234953297..c6a7dd7499f1 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -144,8 +144,8 @@ class ShortcutLauncher extends ShortcutPackageItem {
final ArraySet<String> prevSet = mPinnedShortcuts.get(pu);
// Actually pin shortcuts.
- // This logic here is to make sure a launcher cannot pin a shortcut that is floating
- // (i.e. not dynamic nor manifest but is pinned) and pinned by another launcher.
+ // This logic here is to make sure a launcher cannot pin a shortcut that is not dynamic
+ // nor long-lived nor manifest but is pinned.
// In this case, technically the shortcut doesn't exist to this launcher, so it can't
// pin it.
// (Maybe unnecessarily strict...)
@@ -158,7 +158,7 @@ class ShortcutLauncher extends ShortcutPackageItem {
if (si == null) {
continue;
}
- if (si.isDynamic()
+ if (si.isDynamic() || si.isLongLived()
|| si.isManifestShortcut()
|| (prevSet != null && prevSet.contains(id))
|| forPinRequest) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 9de485b28479..409ca03f4180 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1878,6 +1878,44 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
+ public boolean setUserEphemeral(@UserIdInt int userId, boolean enableEphemeral) {
+ checkCreateUsersPermission("update ephemeral user flag");
+ UserData userToUpdate = null;
+ synchronized (mPackagesLock) {
+ synchronized (mUsersLock) {
+ final UserData userData = mUsers.get(userId);
+ if (userData == null) {
+ Slog.e(LOG_TAG, "User not found for setting ephemeral mode: u" + userId);
+ return false;
+ }
+ boolean isEphemeralUser = (userData.info.flags & UserInfo.FLAG_EPHEMERAL) != 0;
+ boolean isEphemeralOnCreateUser =
+ (userData.info.flags & UserInfo.FLAG_EPHEMERAL_ON_CREATE) != 0;
+ // when user is created in ephemeral mode via FLAG_EPHEMERAL
+ // its state cannot be changed to non ephemeral.
+ // FLAG_EPHEMERAL_ON_CREATE is used to keep track of this state
+ if (isEphemeralOnCreateUser && !enableEphemeral) {
+ Slog.e(LOG_TAG, "Failed to change user state to non-ephemeral for user "
+ + userId);
+ return false;
+ }
+ if (isEphemeralUser != enableEphemeral) {
+ if (enableEphemeral) {
+ userData.info.flags |= UserInfo.FLAG_EPHEMERAL;
+ } else {
+ userData.info.flags &= ~UserInfo.FLAG_EPHEMERAL;
+ }
+ userToUpdate = userData;
+ }
+ }
+ if (userToUpdate != null) {
+ writeUserLP(userToUpdate);
+ }
+ }
+ return true;
+ }
+
+ @Override
public void setUserIcon(@UserIdInt int userId, Bitmap bitmap) {
try {
checkManageUsersPermission("update users");
@@ -3984,6 +4022,10 @@ public class UserManagerService extends IUserManager.Stub {
flags &= ~UserInfo.FLAG_EPHEMERAL;
}
+ if ((flags & UserInfo.FLAG_EPHEMERAL) != 0) {
+ flags |= UserInfo.FLAG_EPHEMERAL_ON_CREATE;
+ }
+
userInfo = new UserInfo(userId, name, null, flags, userType);
userInfo.serialNumber = mNextSerialNumber++;
userInfo.creationTime = getCreationTime();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d8e7fbe8b296..d88949bcc0ec 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -169,7 +169,6 @@ import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.IDisplayFoldListener;
-import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyCharacterMap.FallbackAction;
@@ -381,7 +380,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private final SparseArray<ScreenOnListener> mScreenOnListeners = new SparseArray<>();
Context mContext;
- IWindowManager mWindowManager;
WindowManagerFuncs mWindowManagerFuncs;
WindowManagerInternal mWindowManagerInternal;
PowerManager mPowerManager;
@@ -1870,10 +1868,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/** {@inheritDoc} */
@Override
- public void init(Context context, IWindowManager windowManager,
- WindowManagerFuncs windowManagerFuncs) {
+ public void init(Context context, WindowManagerFuncs windowManagerFuncs) {
mContext = context;
- mWindowManager = windowManager;
mWindowManagerFuncs = windowManagerFuncs;
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
@@ -4534,7 +4530,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pmWakeReason)) + ")");
}
- mActivityTaskManagerInternal.notifyWakingUp();
mDefaultDisplayPolicy.setAwake(true);
// Since goToSleep performs these functions synchronously, we must
@@ -4802,10 +4797,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
if (enableScreen) {
- try {
- mWindowManager.enableScreenIfNeeded();
- } catch (RemoteException unhandled) {
- }
+ mWindowManagerFuncs.enableScreenIfNeeded();
}
}
@@ -5262,12 +5254,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
void updateRotation(boolean alwaysSendConfiguration) {
- try {
- // Set orientation on WindowManager.
- mWindowManager.updateRotation(alwaysSendConfiguration, false /* forceRelayout */);
- } catch (RemoteException e) {
- // Ignore
- }
+ mWindowManagerFuncs.updateRotation(alwaysSendConfiguration, false /* forceRelayout */);
}
/**
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 89178139ffec..e8a3dcd5635f 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -78,7 +78,6 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.IDisplayFoldListener;
-import android.view.IWindowManager;
import android.view.KeyEvent;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -156,10 +155,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
int FINISH_LAYOUT_REDO_ANIM = 0x0008;
/** Layer for the screen off animation */
int COLOR_FADE_LAYER = 0x40000001;
- /** Layer for Input overlays for capturing inputs for gesture detection, etc. */
- int INPUT_DISPLAY_OVERLAY_LAYER = 0x7f000000;
- /** Layer for Screen Decoration: The top most visible layer just below input overlay layers */
- int SCREEN_DECOR_DISPLAY_OVERLAY_LAYER = INPUT_DISPLAY_OVERLAY_LAYER - 1;
/**
* Register shortcuts for window manager to dispatch.
@@ -347,6 +342,22 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
* @return {@code true} if app transition state is idle on the default display.
*/
boolean isAppTransitionStateIdle();
+
+ /**
+ * Enables the screen if all conditions are met.
+ */
+ void enableScreenIfNeeded();
+
+ /**
+ * Updates the current screen rotation based on the current state of the world.
+ *
+ * @param alwaysSendConfiguration Flag to force a new configuration to be evaluated.
+ * This can be used when there are other parameters in
+ * configuration that are changing.
+ * @param forceRelayout If true, the window manager will always do a relayout of its
+ * windows even if the rotation hasn't changed.
+ */
+ void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout);
}
/**
@@ -395,8 +406,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
*
* @param context The system context we are running in.
*/
- public void init(Context context, IWindowManager windowManager,
- WindowManagerFuncs windowManagerFuncs);
+ void init(Context context, WindowManagerFuncs windowManagerFuncs);
/**
* Check permissions when adding a window.
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8c52717b1a20..50d1bd6c3e33 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -112,6 +112,7 @@ import com.android.internal.app.IBatteryStats;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.LatencyTracker;
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
@@ -4960,6 +4961,7 @@ public final class PowerManagerService extends SystemService
int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
Intent.EXTRA_DOCK_STATE_UNDOCKED);
if (mDockState != dockState) {
+ FrameworkStatsLog.write(FrameworkStatsLog.DOCK_STATE_CHANGED, dockState);
mDockState = dockState;
mDirty |= DIRTY_DOCK_STATE;
updatePowerStateLocked();
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
index 13fe14caa1f9..6318a99fc9a2 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -21,6 +21,7 @@ import static android.Manifest.permission.RECORD_AUDIO;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.PermissionChecker;
import android.media.permission.Identity;
@@ -115,8 +116,10 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware
* originator temporarily doesn't have the right permissions to use this service.
*/
private void enforcePermissionsForPreflight(@NonNull Identity identity) {
- enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO);
- enforcePermissionForPreflight(mContext, identity, CAPTURE_AUDIO_HOTWORD);
+ enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO,
+ /* allowSoftDenial= */ true);
+ enforcePermissionForPreflight(mContext, identity, CAPTURE_AUDIO_HOTWORD,
+ /* allowSoftDenial= */ true);
}
/**
@@ -124,8 +127,7 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware
*/
void enforcePermissionsForDataDelivery(@NonNull Identity identity, @NonNull String reason) {
enforceSoundTriggerRecordAudioPermissionForDataDelivery(identity, reason);
- enforcePermissionForDataDelivery(mContext, identity, CAPTURE_AUDIO_HOTWORD,
- reason);
+ enforcePermissionForDataDelivery(mContext, identity, CAPTURE_AUDIO_HOTWORD, reason);
}
/**
@@ -165,20 +167,25 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware
/**
* Throws a {@link SecurityException} if originator permanently doesn't have the given
* permission.
- * Soft (temporary) denials are considered OK for preflight purposes.
*
- * @param context A {@link Context}, used for permission checks.
- * @param identity The identity to check.
- * @param permission The identifier of the permission we want to check.
+ * @param context A {@link Context}, used for permission checks.
+ * @param identity The identity to check.
+ * @param permission The identifier of the permission we want to check.
+ * @param allowSoftDenial If true, the operation succeeds even for soft (temporary) denials.
*/
+ // TODO: Consider splitting up this method instead of using `allowSoftDenial`, to make it
+ // clearer when soft denials are not allowed.
private static void enforcePermissionForPreflight(@NonNull Context context,
- @NonNull Identity identity, @NonNull String permission) {
+ @NonNull Identity identity, @NonNull String permission, boolean allowSoftDenial) {
final int status = PermissionUtil.checkPermissionForPreflight(context, identity,
permission);
switch (status) {
case PermissionChecker.PERMISSION_GRANTED:
- case PermissionChecker.PERMISSION_SOFT_DENIED:
return;
+ case PermissionChecker.PERMISSION_SOFT_DENIED:
+ if (allowSoftDenial) {
+ return;
+ } // else fall through
case PermissionChecker.PERMISSION_HARD_DENIED:
throw new SecurityException(
String.format("Failed to obtain permission %s for identity %s", permission,
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index b00d8b47906a..53b8b53e2b6c 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -27,6 +27,7 @@ import android.view.InsetsVisibilities;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
+import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.view.AppearanceRegion;
import com.android.server.notification.NotificationDelegate;
@@ -133,7 +134,8 @@ public interface StatusBarManagerInternal {
/** @see com.android.internal.statusbar.IStatusBar#onSystemBarAttributesChanged */
void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName);
+ @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName,
+ LetterboxDetails[] letterboxDetails);
/** @see com.android.internal.statusbar.IStatusBar#showTransient */
void showTransient(int displayId, @InternalInsetsType int[] types,
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 46e7574e1c8a..71b1bc2e24bc 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -96,6 +96,7 @@ import com.android.internal.statusbar.ISessionListener;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.IUndoMediaTransferCallback;
+import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.statusbar.RegisterStatusBarResult;
import com.android.internal.statusbar.StatusBarIcon;
@@ -596,13 +597,15 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
@Behavior int behavior, InsetsVisibilities requestedVisibilities,
- String packageName) {
+ String packageName, LetterboxDetails[] letterboxDetails) {
getUiState(displayId).setBarAttributes(appearance, appearanceRegions,
- navbarColorManagedByIme, behavior, requestedVisibilities, packageName);
+ navbarColorManagedByIme, behavior, requestedVisibilities, packageName,
+ letterboxDetails);
if (mBar != null) {
try {
mBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
- navbarColorManagedByIme, behavior, requestedVisibilities, packageName);
+ navbarColorManagedByIme, behavior, requestedVisibilities, packageName,
+ letterboxDetails);
} catch (RemoteException ex) { }
}
}
@@ -1204,17 +1207,20 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
private int mImeBackDisposition = 0;
private boolean mShowImeSwitcher = false;
private IBinder mImeToken = null;
+ private LetterboxDetails[] mLetterboxDetails;
private void setBarAttributes(@Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
@Behavior int behavior, InsetsVisibilities requestedVisibilities,
- String packageName) {
+ String packageName,
+ LetterboxDetails[] letterboxDetails) {
mAppearance = appearance;
mAppearanceRegions = appearanceRegions;
mNavbarColorManagedByIme = navbarColorManagedByIme;
mBehavior = behavior;
mRequestedVisibilities = requestedVisibilities;
mPackageName = packageName;
+ mLetterboxDetails = letterboxDetails;
}
private void showTransient(@InternalInsetsType int[] types) {
@@ -1341,7 +1347,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
state.mImeBackDisposition, state.mShowImeSwitcher,
gatherDisableActionsLocked(mCurrentUserId, 2), state.mImeToken,
state.mNavbarColorManagedByIme, state.mBehavior, state.mRequestedVisibilities,
- state.mPackageName, transientBarTypes);
+ state.mPackageName, transientBarTypes, state.mLetterboxDetails);
}
}
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 78b1c20ac4b2..d79837be3583 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -61,12 +61,11 @@ final class Vibration {
IGNORED_BACKGROUND,
IGNORED_UNKNOWN_VIBRATION,
IGNORED_UNSUPPORTED,
- IGNORED_FOR_ALARM,
IGNORED_FOR_EXTERNAL,
+ IGNORED_FOR_HIGHER_IMPORTANCE,
IGNORED_FOR_ONGOING,
IGNORED_FOR_POWER,
IGNORED_FOR_RINGER_MODE,
- IGNORED_FOR_RINGTONE,
IGNORED_FOR_SETTINGS,
IGNORED_SUPERSEDED,
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index f0911ca62027..5ac2f4f27452 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -713,14 +713,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
case IGNORED_ERROR_APP_OPS:
Slog.w(TAG, "Would be an error: vibrate from uid " + uid);
break;
- case IGNORED_FOR_ALARM:
+ case IGNORED_FOR_EXTERNAL:
if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
+ Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
}
break;
- case IGNORED_FOR_EXTERNAL:
+ case IGNORED_FOR_HIGHER_IMPORTANCE:
if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
+ Slog.d(TAG, "Ignoring incoming vibration in favor of ongoing vibration"
+ + " with higher importance");
}
break;
case IGNORED_FOR_ONGOING:
@@ -734,12 +735,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
+ attrs);
}
break;
- case IGNORED_FOR_RINGTONE:
- if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration in favor of ringtone vibration");
- }
- break;
-
default:
if (DEBUG) {
Slog.d(TAG, "Vibration for uid=" + uid + " and with attrs=" + attrs
@@ -812,20 +807,43 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return null;
}
- if (currentVibration.attrs.getUsage() == VibrationAttributes.USAGE_ALARM) {
- return Vibration.Status.IGNORED_FOR_ALARM;
- }
-
- if (currentVibration.attrs.getUsage() == VibrationAttributes.USAGE_RINGTONE) {
- return Vibration.Status.IGNORED_FOR_RINGTONE;
+ int currentUsage = currentVibration.attrs.getUsage();
+ int newUsage = vib.attrs.getUsage();
+ if (getVibrationImportance(currentUsage) > getVibrationImportance(newUsage)) {
+ // Current vibration has higher importance than this one and should not be cancelled.
+ return Vibration.Status.IGNORED_FOR_HIGHER_IMPORTANCE;
}
if (currentVibration.isRepeating()) {
+ // Current vibration is repeating, assume it's more important.
return Vibration.Status.IGNORED_FOR_ONGOING;
}
+
return null;
}
+ private static int getVibrationImportance(@VibrationAttributes.Usage int usage) {
+ switch (usage) {
+ case VibrationAttributes.USAGE_RINGTONE:
+ return 5;
+ case VibrationAttributes.USAGE_ALARM:
+ return 4;
+ case VibrationAttributes.USAGE_NOTIFICATION:
+ return 3;
+ case VibrationAttributes.USAGE_COMMUNICATION_REQUEST:
+ case VibrationAttributes.USAGE_ACCESSIBILITY:
+ return 2;
+ case VibrationAttributes.USAGE_HARDWARE_FEEDBACK:
+ case VibrationAttributes.USAGE_PHYSICAL_EMULATION:
+ return 1;
+ case VibrationAttributes.USAGE_MEDIA:
+ case VibrationAttributes.USAGE_TOUCH:
+ case VibrationAttributes.USAGE_UNKNOWN:
+ default:
+ return 0;
+ }
+ }
+
/**
* Check if given vibration should be ignored by this service.
*
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index f6748de660e2..d2a00af245f6 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -43,6 +43,7 @@ import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_N
import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller;
+import android.Manifest;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -462,8 +463,8 @@ class ActivityClientController extends IActivityClientController.Stub {
// Explicitly dismissing the activity so reset its relaunch flag.
r.mRelaunchReason = RELAUNCH_REASON_NONE;
} else {
- r.finishIfPossible(resultCode, resultData, resultGrants,
- "app-request", true /* oomAdj */);
+ r.finishIfPossible(resultCode, resultData, resultGrants, "app-request",
+ true /* oomAdj */);
res = r.finishing;
if (!res) {
Slog.i(TAG, "Failed to finish by app-request");
@@ -525,6 +526,23 @@ class ActivityClientController extends IActivityClientController.Stub {
}
@Override
+ public void setForceSendResultForMediaProjection(IBinder token) {
+ // Require that this is invoked only during MediaProjection setup.
+ mService.mAmInternal.enforceCallingPermission(
+ Manifest.permission.MANAGE_MEDIA_PROJECTION,
+ "setForceSendResultForMediaProjection");
+
+ final ActivityRecord r;
+ synchronized (mGlobalLock) {
+ r = ActivityRecord.isInRootTaskLocked(token);
+ if (r == null || !r.isInHistory()) {
+ return;
+ }
+ r.setForceSendResultForMediaProjection();
+ }
+ }
+
+ @Override
public boolean isTopOfTask(IBinder token) {
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
@@ -559,6 +577,21 @@ class ActivityClientController extends IActivityClientController.Stub {
}
}
+ /**
+ * Returns the windowing mode of the task that hosts the activity, or {@code -1} if task is not
+ * found.
+ */
+ @Override
+ public int getTaskWindowingMode(IBinder activityToken) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord ar = ActivityRecord.isInAnyTask(activityToken);
+ if (ar == null) {
+ return -1;
+ }
+ return ar.getTask().getWindowingMode();
+ }
+ }
+
@Override
@Nullable
public IBinder getActivityTokenBelow(IBinder activityToken) {
@@ -737,7 +770,7 @@ class ActivityClientController extends IActivityClientController.Stub {
synchronized (mGlobalLock) {
final ActivityRecord r = ensureValidPictureInPictureActivityParams(
"enterPictureInPictureMode", token, params);
- return mService.enterPictureInPictureMode(r, params);
+ return mService.enterPictureInPictureMode(r, params, true /* fromClient */);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -853,22 +886,23 @@ class ActivityClientController extends IActivityClientController.Stub {
/**
* Requests that an activity should enter picture-in-picture mode if possible. This method may
* be used by the implementation of non-phone form factors.
+ *
+ * @return false if the activity cannot enter PIP mode.
*/
- void requestPictureInPictureMode(@NonNull ActivityRecord r) {
+ boolean requestPictureInPictureMode(@NonNull ActivityRecord r) {
if (r.inPinnedWindowingMode()) {
- throw new IllegalStateException("Activity is already in PIP mode");
+ return false;
}
final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState(
"requestPictureInPictureMode", /* beforeStopping */ false);
if (!canEnterPictureInPicture) {
- throw new IllegalStateException(
- "Requested PIP on an activity that doesn't support it");
+ return false;
}
if (r.pictureInPictureArgs.isAutoEnterEnabled()) {
- mService.enterPictureInPictureMode(r, r.pictureInPictureArgs);
- return;
+ return mService.enterPictureInPictureMode(r, r.pictureInPictureArgs,
+ false /* fromClient */);
}
try {
@@ -876,9 +910,11 @@ class ActivityClientController extends IActivityClientController.Stub {
r.app.getThread(), r.token);
transaction.addCallback(EnterPipRequestedItem.obtain());
mService.getLifecycleManager().scheduleTransaction(transaction);
+ return true;
} catch (Exception e) {
Slog.w(TAG, "Failed to send enter pip requested item: "
+ r.intent.getComponent(), e);
+ return false;
}
}
@@ -928,6 +964,7 @@ class ActivityClientController extends IActivityClientController.Stub {
if (rootTask.inFreeformWindowingMode()) {
rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ rootTask.setBounds(null);
} else if (!r.supportsFreeform()) {
throw new IllegalStateException(
"This activity is currently not freeform-enabled");
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index d6c0ab6b124b..891654979965 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -56,6 +56,10 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_T
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__NOT_LETTERBOXED_POSITION;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
import static com.android.internal.util.FrameworkStatsLog.CAMERA_COMPAT_CONTROL_EVENT_REPORTED__EVENT__APPEARED_APPLY_TREATMENT;
@@ -68,6 +72,7 @@ import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
import static com.android.server.wm.EventLogTags.WM_ACTIVITY_LAUNCH_TIME;
@@ -101,6 +106,7 @@ import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.LatencyTracker;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -143,6 +149,12 @@ class ActivityMetricsLogger {
private static final long UNKNOWN_VISIBILITY_CHECK_DELAY_MS = 3000;
/**
+ * If the recents animation is finished before the delay since the window drawn, do not log the
+ * action because the duration is too small that may be just an accidentally touch.
+ */
+ private static final long LATENCY_TRACKER_RECENTS_DELAY_MS = 300;
+
+ /**
* The flag for {@link #notifyActivityLaunching} to skip associating a new launch with an active
* transition, in the case the launch is standalone (e.g. from recents).
*/
@@ -732,10 +744,6 @@ class ActivityMetricsLogger {
if (info.mLoggedTransitionStarting) {
done(false /* abort */, info, "notifyWindowsDrawn", timestampNs);
}
- if (r.mWmService.isRecentsAnimationTarget(r)) {
- r.mWmService.getRecentsAnimationController().logRecentsAnimationStartTime(
- info.mSourceEventDelayMs + info.mWindowsDrawnDelayMs);
- }
return infoSnapshot;
}
@@ -947,6 +955,9 @@ class ActivityMetricsLogger {
launchObserverNotifyActivityLaunchFinished(info, timestampNs);
}
logAppTransitionFinished(info, isHibernating != null ? isHibernating : false);
+ if (info.mReason == APP_TRANSITION_RECENTS_ANIM) {
+ logRecentsAnimationLatency(info);
+ }
}
mTransitionInfoList.remove(info);
}
@@ -1102,6 +1113,22 @@ class ActivityMetricsLogger {
Log.i(TAG, sb.toString());
}
+ private void logRecentsAnimationLatency(TransitionInfo info) {
+ final int duration = info.mSourceEventDelayMs + info.mWindowsDrawnDelayMs;
+ final ActivityRecord r = info.mLastLaunchedActivity;
+ final long lastTopLossTime = r.topResumedStateLossTime;
+ final WindowManagerService wm = mSupervisor.mService.mWindowManager;
+ final Object controller = wm.getRecentsAnimationController();
+ mLoggerHandler.postDelayed(() -> {
+ if (lastTopLossTime != r.topResumedStateLossTime
+ || controller != wm.getRecentsAnimationController()) {
+ // Skip if the animation was finished in a short time.
+ return;
+ }
+ wm.mLatencyTracker.logAction(LatencyTracker.ACTION_START_RECENTS_ANIMATION, duration);
+ }, LATENCY_TRACKER_RECENTS_DELAY_MS);
+ }
+
private static int getAppStartTransitionType(int tronType, boolean relaunched) {
if (tronType == TYPE_TRANSITION_COLD_LAUNCH) {
return FrameworkStatsLog.APP_START_OCCURRED__TYPE__COLD;
@@ -1368,7 +1395,7 @@ class ActivityMetricsLogger {
return;
}
- logAppCompatStateInternal(activity, state, packageUid, compatStateInfo);
+ logAppCompatStateInternal(activity, state, compatStateInfo);
}
/**
@@ -1408,18 +1435,61 @@ class ActivityMetricsLogger {
}
}
if (activityToLog != null && stateToLog != APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE) {
- logAppCompatStateInternal(activityToLog, stateToLog, packageUid, compatStateInfo);
+ logAppCompatStateInternal(activityToLog, stateToLog, compatStateInfo);
}
}
+ private static boolean isAppCompateStateChangedToLetterboxed(int state) {
+ return state == APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO
+ || state == APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION
+ || state == APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
+ }
+
private void logAppCompatStateInternal(@NonNull ActivityRecord activity, int state,
- int packageUid, PackageCompatStateInfo compatStateInfo) {
+ PackageCompatStateInfo compatStateInfo) {
compatStateInfo.mLastLoggedState = state;
compatStateInfo.mLastLoggedActivity = activity;
- FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPAT_STATE_CHANGED, packageUid, state);
+ int packageUid = activity.info.applicationInfo.uid;
+
+ int positionToLog = APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__NOT_LETTERBOXED_POSITION;
+ if (isAppCompateStateChangedToLetterboxed(state)) {
+ positionToLog = activity.mLetterboxUiController.getLetterboxPositionForLogging();
+ }
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPAT_STATE_CHANGED,
+ packageUid, state, positionToLog);
+
+ if (DEBUG_METRICS) {
+ Slog.i(TAG, String.format("APP_COMPAT_STATE_CHANGED(%s, %s, %s)",
+ packageUid, state, positionToLog));
+ }
+ }
+
+ /**
+ * Logs the changing of the letterbox position along with its package UID
+ */
+ void logLetterboxPositionChange(@NonNull ActivityRecord activity, int position) {
+ int packageUid = activity.info.applicationInfo.uid;
+ FrameworkStatsLog.write(FrameworkStatsLog.LETTERBOX_POSITION_CHANGED, packageUid, position);
+
+ if (!mPackageUidToCompatStateInfo.contains(packageUid)) {
+ // There is no last logged activity for this packageUid so we should not log the
+ // position change as we can only log the position change for the current activity
+ return;
+ }
+ final PackageCompatStateInfo compatStateInfo = mPackageUidToCompatStateInfo.get(packageUid);
+ final ActivityRecord lastLoggedActivity = compatStateInfo.mLastLoggedActivity;
+ if (activity != lastLoggedActivity) {
+ // Only log the position change for the current activity to be consistent with
+ // findAppCompatStateToLog and ensure that metrics for the state changes are computed
+ // correctly
+ return;
+ }
+ int state = activity.getAppCompatState();
+ logAppCompatStateInternal(activity, state, compatStateInfo);
if (DEBUG_METRICS) {
- Slog.i(TAG, String.format("APP_COMPAT_STATE_CHANGED(%s, %s)", packageUid, state));
+ Slog.i(TAG, String.format("LETTERBOX_POSITION_CHANGED(%s, %s)",
+ packageUid, position));
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4f222a4f8b83..a044e60b40ae 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -224,6 +224,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WIND
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
+import static com.android.server.wm.WindowManagerService.sEnableShellTransitions;
import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
@@ -595,6 +596,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// pre-NYC apps that don't have a sense of being resized.
int mRelaunchReason = RELAUNCH_REASON_NONE;
+ private boolean mForceSendResultForMediaProjection = false;
+
TaskDescription taskDescription; // the recents information for this activity
// The locusId associated with this activity, if set.
@@ -925,7 +928,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// SystemUi sets the pinned mode on activity after transition is done.
boolean mWaitForEnteringPinnedMode;
- private final ActivityRecordInputSink mActivityRecordInputSink;
+ final ActivityRecordInputSink mActivityRecordInputSink;
// Activities with this uid are allowed to not create an input sink while being in the same
// task and directly above this ActivityRecord. This field is updated whenever a new activity
@@ -1990,6 +1993,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (options.getLaunchIntoPipParams() != null) {
pictureInPictureArgs = options.getLaunchIntoPipParams();
+ if (sourceRecord != null) {
+ adjustPictureInPictureParamsIfNeeded(sourceRecord.getBounds());
+ }
}
mOverrideTaskTransition = options.getOverrideTaskTransition();
@@ -2066,6 +2072,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
launchMode = aInfo.launchMode;
+ // Don't move below setActivityType since it triggers onConfigurationChange ->
+ // resolveOverrideConfiguration that requires having mLetterboxUiController initialised.
+ mLetterboxUiController = new LetterboxUiController(mWmService, this);
+ mCameraCompatControlEnabled = mWmService.mContext.getResources()
+ .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);
+
setActivityType(_componentSpecified, _launchedFromUid, _intent, options, sourceRecord);
immersive = (aInfo.flags & FLAG_IMMERSIVE) != 0;
@@ -2094,9 +2106,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mPersistentState = persistentState;
taskDescription = _taskDescription;
- mLetterboxUiController = new LetterboxUiController(mWmService, this);
- mCameraCompatControlEnabled = mWmService.mContext.getResources()
- .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);
shouldDockBigOverlays = mWmService.mContext.getResources()
.getBoolean(R.bool.config_dockBigOverlayWindows);
@@ -2795,7 +2804,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@VisibleForTesting
boolean canLaunchHomeActivity(int uid, ActivityRecord sourceRecord) {
- if (uid == Process.myUid() || uid == 0) {
+ if (uid == SYSTEM_UID || uid == 0) {
// System process can launch home activity.
return true;
}
@@ -3014,25 +3023,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
&& info.supportsPictureInPicture();
}
- /**
- * @return whether this activity supports split-screen multi-window and can be put in
- * split-screen.
- */
- @Override
- public boolean supportsSplitScreenWindowingMode() {
- return supportsSplitScreenWindowingModeInDisplayArea(getDisplayArea());
- }
-
- /**
- * @return whether this activity supports split-screen multi-window and can be put in
- * split-screen if it is in the given {@link TaskDisplayArea}.
- */
- boolean supportsSplitScreenWindowingModeInDisplayArea(@Nullable TaskDisplayArea tda) {
- return super.supportsSplitScreenWindowingMode()
- && mAtmService.mSupportsSplitScreenMultiWindow
- && supportsMultiWindowInDisplayArea(tda);
- }
-
boolean supportsFreeform() {
return supportsFreeformInDisplayArea(getDisplayArea());
}
@@ -3163,15 +3153,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mWillCloseOrEnterPip = willCloseOrEnterPip;
}
- /**
- * Returns whether this {@link ActivityRecord} is considered closing. Conditions are either
- * 1. Is this app animating and was requested to be hidden
- * 2. App is delayed closing since it might enter PIP.
- */
- boolean isClosingOrEnteringPip() {
- return (isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_APP_TRANSITION)
- && !mVisibleRequested) || mWillCloseOrEnterPip;
+ boolean willCloseOrEnterPip() {
+ return mWillCloseOrEnterPip;
}
+
/**
* @return Whether AppOps allows this package to enter picture-in-picture.
*/
@@ -3229,12 +3214,29 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return false;
}
- if (mRootWindowContainer.getTopResumedActivity() == this
- && getDisplayContent().mFocusedApp == this) {
- ProtoLog.d(WM_DEBUG_FOCUS, "moveFocusableActivityToTop: already on top, "
- + "activity=%s", this);
- return !isState(RESUMED);
+ // If this activity already positions on the top focused task, moving the task to front
+ // is not needed. But we still need to ensure this activity is focused because the
+ // current focused activity could be another activity in the same Task if activities are
+ // displayed on adjacent TaskFragments.
+ final ActivityRecord currentFocusedApp = mDisplayContent.mFocusedApp;
+ if (currentFocusedApp != null && currentFocusedApp.task == task) {
+ final Task topFocusableTask = mDisplayContent.getTask(
+ (t) -> t.isLeafTask() && t.isFocusable(), true /* traverseTopToBottom */);
+ if (task == topFocusableTask) {
+ if (currentFocusedApp == this) {
+ ProtoLog.d(WM_DEBUG_FOCUS, "moveFocusableActivityToTop: already on top "
+ + "and focused, activity=%s", this);
+ } else {
+ ProtoLog.d(WM_DEBUG_FOCUS, "moveFocusableActivityToTop: set focused, "
+ + "activity=%s", this);
+ mDisplayContent.setFocusedApp(this);
+ mAtmService.mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+ true /* updateInputWindows */);
+ }
+ return !isState(RESUMED);
+ }
}
+
ProtoLog.d(WM_DEBUG_FOCUS, "moveFocusableActivityToTop: activity=%s", this);
rootTask.moveToFront(reason, task);
@@ -3284,7 +3286,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(resultGrants,
resultTo.getUriPermissionsLocked());
}
- resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData);
+ if (mForceSendResultForMediaProjection) {
+ resultTo.sendResult(this.getUid(), resultWho, requestCode, resultCode,
+ resultData, resultGrants, true /* forceSendForMediaProjection */);
+ } else {
+ resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData);
+ }
resultTo = null;
} else if (DEBUG_RESULTS) {
Slog.v(TAG_RESULTS, "No result destination from " + this);
@@ -3478,6 +3485,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ void setForceSendResultForMediaProjection() {
+ mForceSendResultForMediaProjection = true;
+ }
+
private void prepareActivityHideTransitionAnimationIfOvarlay() {
if (mTaskOverlay) {
prepareActivityHideTransitionAnimation();
@@ -3517,7 +3528,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
final boolean isCurrentVisible = mVisibleRequested || isState(PAUSED, STARTED);
- if (updateVisibility && isCurrentVisible) {
+ if (updateVisibility && isCurrentVisible
+ // Avoid intermediate lifecycle change when launching with clearing task.
+ && !task.isClearingToReuseTask()) {
boolean ensureVisibility = false;
if (occludesParent(true /* includingFinishing */)) {
// If the current activity is not opaque, we need to make sure the visibilities of
@@ -4489,12 +4502,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/**
* @return Whether we are allowed to show non-starting windows at the moment. We disallow
- * showing windows during transitions in case we have windows that have wide-color-gamut
- * color mode set to avoid jank in the middle of the transition.
+ * showing windows while the transition animation is playing in case we have windows
+ * that have wide-color-gamut color mode set to avoid jank in the middle of the
+ * animation.
*/
boolean canShowWindows() {
- return allDrawn && !(isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION)
- && hasNonDefaultColorWindow());
+ final boolean drawn = mTransitionController.isShellTransitionsEnabled()
+ ? mSyncState != SYNC_STATE_WAITING_FOR_DRAW : allDrawn;
+ final boolean animating = mTransitionController.isShellTransitionsEnabled()
+ ? mTransitionController.inPlayingTransition(this)
+ : isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION);
+ return drawn && !(animating && hasNonDefaultColorWindow());
}
/**
@@ -4569,6 +4587,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void sendResult(int callingUid, String resultWho, int requestCode, int resultCode,
Intent data, NeededUriGrants dataGrants) {
+ sendResult(callingUid, resultWho, requestCode, resultCode, data, dataGrants,
+ false /* forceSendForMediaProjection */);
+ }
+
+ private void sendResult(int callingUid, String resultWho, int requestCode, int resultCode,
+ Intent data, NeededUriGrants dataGrants, boolean forceSendForMediaProjection) {
if (callingUid > 0) {
mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(dataGrants,
getUriPermissionsLocked());
@@ -4577,8 +4601,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (DEBUG_RESULTS) {
Slog.v(TAG, "Send activity result to " + this
+ " : who=" + resultWho + " req=" + requestCode
- + " res=" + resultCode + " data=" + data);
+ + " res=" + resultCode + " data=" + data
+ + " forceSendForMediaProjection=" + forceSendForMediaProjection);
}
+
if (isState(RESUMED) && attachedToProcess()) {
try {
final ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
@@ -4591,9 +4617,63 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ // Schedule sending results now for Media Projection setup.
+ if (forceSendForMediaProjection && attachedToProcess() && isState(STARTED, PAUSING, PAUSED,
+ STOPPING, STOPPED)) {
+ final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), token);
+ // Build result to be returned immediately.
+ transaction.addCallback(ActivityResultItem.obtain(
+ List.of(new ResultInfo(resultWho, requestCode, resultCode, data))));
+ // When the activity result is delivered, the activity will transition to RESUMED.
+ // Since the activity is only resumed so the result can be immediately delivered,
+ // return it to its original lifecycle state.
+ ActivityLifecycleItem lifecycleItem = getLifecycleItemForCurrentStateForResult();
+ if (lifecycleItem != null) {
+ transaction.setLifecycleStateRequest(lifecycleItem);
+ } else {
+ Slog.w(TAG, "Unable to get the lifecycle item for state " + mState
+ + " so couldn't immediately send result");
+ }
+ try {
+ mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception thrown sending result to " + this, e);
+ }
+ }
+
addResultLocked(null /* from */, resultWho, requestCode, resultCode, data);
}
+ /**
+ * Provides a lifecycle item for the current stat. Only to be used when force sending an
+ * activity result (as part of MeidaProjection setup). Does not support the following states:
+ * {@link State#INITIALIZING}, {@link State#RESTARTING_PROCESS},
+ * {@link State#FINISHING}, {@link State#DESTROYING}, {@link State#DESTROYED}. It does not make
+ * sense to force send a result to an activity in these states. Does not support
+ * {@link State#RESUMED} since a resumed activity will end in the resumed state after handling
+ * the result.
+ *
+ * @return an {@link ActivityLifecycleItem} for the current state, or {@code null} if the
+ * state is not valid.
+ */
+ @Nullable
+ private ActivityLifecycleItem getLifecycleItemForCurrentStateForResult() {
+ switch (mState) {
+ case STARTED:
+ return StartActivityItem.obtain(null);
+ case PAUSING:
+ case PAUSED:
+ return PauseActivityItem.obtain();
+ case STOPPING:
+ case STOPPED:
+ return StopActivityItem.obtain(configChangeFlags);
+ default:
+ // Do not send a result immediately if the activity is in state INITIALIZING,
+ // RESTARTING_PROCESS, FINISHING, DESTROYING, or DESTROYED.
+ return null;
+ }
+ }
+
private void addNewIntentLocked(ReferrerIntent intent) {
if (newIntents == null) {
newIntents = new ArrayList<>();
@@ -4673,6 +4753,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (mPendingRemoteAnimation != null) {
mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(
mPendingRemoteAnimation);
+ mTransitionController.setStatusBarTransitionDelay(
+ mPendingRemoteAnimation.getStatusBarTransitionDelay());
} else {
if (mPendingOptions == null
|| mPendingOptions.getAnimationType() == ANIM_SCENE_TRANSITION) {
@@ -5191,12 +5273,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
final int windowsCount = mChildren.size();
+ // With Shell-Transition, the activity will running a transition when it is visible.
+ // It won't be included when fromTransition is true means the call from finishTransition.
+ final boolean runningAnimation = sEnableShellTransitions ? visible
+ : isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION);
for (int i = 0; i < windowsCount; i++) {
- mChildren.get(i).onAppVisibilityChanged(visible, isAnimating(PARENTS,
- ANIMATION_TYPE_APP_TRANSITION));
+ mChildren.get(i).onAppVisibilityChanged(visible, runningAnimation);
}
setVisible(visible);
setVisibleRequested(visible);
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "commitVisibility: %s: visible=%b"
+ + " visibleRequested=%b, isInTransition=%b, runningAnimation=%b, caller=%s",
+ this, isVisible(), mVisibleRequested, isInTransition(), runningAnimation,
+ Debug.getCallers(5));
if (!visible) {
stopFreezingScreen(true, true);
} else {
@@ -5219,9 +5308,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
task.dispatchTaskInfoChangedIfNeeded(false /* force */);
task = task.getParent().asTask();
}
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "commitVisibility: %s: visible=%b mVisibleRequested=%b", this,
- isVisible(), mVisibleRequested);
final DisplayContent displayContent = getDisplayContent();
displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
if (performLayout) {
@@ -5256,17 +5342,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final boolean delayed = isAnimating(PARENTS | CHILDREN,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION
| ANIMATION_TYPE_RECENTS);
- if (!delayed && !usingShellTransitions) {
- // We aren't delayed anything, but exiting windows rely on the animation finished
- // callback being called in case the ActivityRecord was pretending to be delayed,
- // which we might have done because we were in closing/opening apps list.
- onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, null /* AnimationAdapter */);
- if (visible) {
- // The token was made immediately visible, there will be no entrance animation.
- // We need to inform the client the enter animation was finished.
- mEnteringAnimation = true;
- mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
- token);
+ if (!delayed) {
+ if (!usingShellTransitions) {
+ if (visible) {
+ // The token was made immediately visible, there will be no entrance animation.
+ // We need to inform the client the enter animation was finished.
+ mEnteringAnimation = true;
+ mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
+ token);
+ }
+ } else {
+ // update wallpaper target
+ setAppLayoutChanges(FINISH_LAYOUT_REDO_WALLPAPER, "ActivityRecord");
}
}
@@ -5550,7 +5637,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
wasStopped, this);
mAppStopped = false;
// Allow the window to turn the screen on once the app is resumed again.
- setCurrentLaunchCanTurnScreenOn(true);
+ if (mAtmService.getActivityStartController().isInExecution()) {
+ setCurrentLaunchCanTurnScreenOn(true);
+ }
+
if (!wasStopped) {
destroySurfaces(true /*cleanupOnResume*/);
}
@@ -7726,11 +7816,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
newParentConfiguration.windowConfiguration.getWindowingMode();
final boolean isFixedOrientationLetterboxAllowed =
parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW
- || parentWindowingMode == WINDOWING_MODE_FULLSCREEN;
+ || parentWindowingMode == WINDOWING_MODE_FULLSCREEN
+ // Switching from PiP to fullscreen.
+ || (parentWindowingMode == WINDOWING_MODE_PINNED
+ && resolvedConfig.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_FULLSCREEN);
// TODO(b/181207944): Consider removing the if condition and always run
// resolveFixedOrientationConfiguration() since this should be applied for all cases.
if (isFixedOrientationLetterboxAllowed) {
- resolveFixedOrientationConfiguration(newParentConfiguration, parentWindowingMode);
+ resolveFixedOrientationConfiguration(newParentConfiguration);
}
if (mCompatDisplayInsets != null) {
@@ -7754,7 +7848,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (isFixedOrientationLetterboxAllowed || mCompatDisplayInsets != null
// In fullscreen, can be letterboxed for aspect ratio.
|| !inMultiWindowMode()) {
- updateResolvedBoundsHorizontalPosition(newParentConfiguration);
+ updateResolvedBoundsPosition(newParentConfiguration);
}
if (mVisibleRequested) {
@@ -7857,39 +7951,60 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
- * Adjusts horizontal position of resolved bounds if they doesn't fill the parent using gravity
+ * Adjusts position of resolved bounds if they doesn't fill the parent using gravity
* requested in the config or via an ADB command. For more context see {@link
- * LetterboxUiController#getHorizontalPositionMultiplier(Configuration)}.
+ * LetterboxUiController#getHorizontalPositionMultiplier(Configuration)} and
+ * {@link LetterboxUiController#getVerticalPositionMultiplier(Configuration)}
*/
- private void updateResolvedBoundsHorizontalPosition(Configuration newParentConfiguration) {
+ private void updateResolvedBoundsPosition(Configuration newParentConfiguration) {
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
final Rect screenResolvedBounds =
mSizeCompatBounds != null ? mSizeCompatBounds : resolvedBounds;
final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
- if (resolvedBounds.isEmpty() || parentBounds.width() == screenResolvedBounds.width()) {
+ if (resolvedBounds.isEmpty()) {
return;
}
-
+ // Horizontal position
int offsetX = 0;
- if (screenResolvedBounds.width() >= parentAppBounds.width()) {
- // If resolved bounds overlap with insets, center within app bounds.
- offsetX = getHorizontalCenterOffset(
- parentAppBounds.width(), screenResolvedBounds.width());
- } else {
- float positionMultiplier =
- mLetterboxUiController.getHorizontalPositionMultiplier(newParentConfiguration);
- offsetX = (int) Math.ceil((parentAppBounds.width() - screenResolvedBounds.width())
- * positionMultiplier);
+ if (parentBounds.width() != screenResolvedBounds.width()) {
+ if (screenResolvedBounds.width() >= parentAppBounds.width()) {
+ // If resolved bounds overlap with insets, center within app bounds.
+ offsetX = getCenterOffset(
+ parentAppBounds.width(), screenResolvedBounds.width());
+ } else {
+ float positionMultiplier =
+ mLetterboxUiController.getHorizontalPositionMultiplier(
+ newParentConfiguration);
+ offsetX = (int) Math.ceil((parentAppBounds.width() - screenResolvedBounds.width())
+ * positionMultiplier);
+ }
+ }
+
+ // Vertical position
+ int offsetY = 0;
+ if (parentBounds.height() != screenResolvedBounds.height()) {
+ if (screenResolvedBounds.height() >= parentAppBounds.height()) {
+ // If resolved bounds overlap with insets, center within app bounds.
+ offsetY = getCenterOffset(
+ parentAppBounds.height(), screenResolvedBounds.height());
+ } else {
+ float positionMultiplier =
+ mLetterboxUiController.getVerticalPositionMultiplier(
+ newParentConfiguration);
+ offsetY = (int) Math.ceil((parentAppBounds.height() - screenResolvedBounds.height())
+ * positionMultiplier);
+ }
}
if (mSizeCompatBounds != null) {
- mSizeCompatBounds.offset(offsetX, 0 /* offsetY */);
+ mSizeCompatBounds.offset(offsetX , offsetY);
+ final int dy = mSizeCompatBounds.top - resolvedBounds.top;
final int dx = mSizeCompatBounds.left - resolvedBounds.left;
- offsetBounds(resolvedConfig, dx, 0 /* offsetY */);
+ offsetBounds(resolvedConfig, dx, dy);
} else {
- offsetBounds(resolvedConfig, offsetX, 0 /* offsetY */);
+ offsetBounds(resolvedConfig, offsetX, offsetY);
}
// Since bounds has changed, the configuration needs to be computed accordingly.
@@ -7901,8 +8016,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
boolean isInTransition() {
- return mTransitionController.inTransition() // Shell transitions.
- || isAnimating(PARENTS | TRANSITION); // Legacy transitions.
+ return inTransitionSelfOrParent();
}
/**
@@ -7917,6 +8031,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return mLetterboxBoundsForFixedOrientationAndAspectRatio != null;
}
+ boolean isAspectRatioApplied() {
+ return mIsAspectRatioApplied;
+ }
+
/**
* Whether this activity is eligible for letterbox eduction.
*
@@ -7998,8 +8116,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* <p>If letterboxed due to fixed orientation then aspect ratio restrictions are also applied
* in this method.
*/
- private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig,
- int windowingMode) {
+ private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig) {
mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
mIsEligibleForFixedOrientationLetterbox = false;
final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
@@ -8019,11 +8136,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (organizedTf != null && !organizedTf.fillsParent()) {
return;
}
- if (windowingMode == WINDOWING_MODE_PINNED) {
- // PiP bounds have higher priority than the requested orientation. Otherwise the
- // activity may be squeezed into a small piece.
- return;
- }
final Rect resolvedBounds =
getResolvedOverrideConfiguration().windowConfiguration.getBounds();
@@ -8088,21 +8200,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
resolvedBounds.set(containingBounds);
final float letterboxAspectRatioOverride =
- mLetterboxUiController.getFixedOrientationLetterboxAspectRatio(newParentConfig);
+ mLetterboxUiController.getFixedOrientationLetterboxAspectRatio();
final float desiredAspectRatio =
letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
? letterboxAspectRatioOverride : computeAspectRatio(parentBounds);
// Apply aspect ratio to resolved bounds
mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingBoundsWithInsets,
- containingBounds, desiredAspectRatio, true);
-
- // Vertically center if orientation is landscape. Center within parent bounds with insets
- // to ensure that insets do not trim height. Bounds will later be horizontally centered in
- // {@link updateResolvedBoundsHorizontalPosition()} regardless of orientation.
- if (forcedOrientation == ORIENTATION_LANDSCAPE) {
- final int offsetY = parentBoundsWithInsets.centerY() - resolvedBounds.centerY();
- resolvedBounds.offset(0, offsetY);
- }
+ containingBounds, desiredAspectRatio);
if (mCompatDisplayInsets != null) {
mCompatDisplayInsets.getBoundsByRotation(
@@ -8126,10 +8230,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/**
* Resolves aspect ratio restrictions for an activity. If the bounds are restricted by
- * aspect ratio, the position will be adjusted later in {@link
- * updateResolvedBoundsHorizontalPosition} within parent's app bounds to balance the visual
- * appearance. The policy of aspect ratio has higher priority than the requested override
- * bounds.
+ * aspect ratio, the position will be adjusted later in {@link #updateResolvedBoundsPosition
+ * within parent's app bounds to balance the visual appearance. The policy of aspect ratio has
+ * higher priority than the requested override bounds.
*/
private void resolveAspectRatioRestriction(Configuration newParentConfiguration) {
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
@@ -8141,7 +8244,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mTmpBounds.setEmpty();
mIsAspectRatioApplied = applyAspectRatio(mTmpBounds, parentAppBounds, parentBounds);
// If the out bounds is not empty, it means the activity cannot fill parent's app bounds,
- // then they should be aligned later in #updateResolvedBoundsHorizontalPosition().
+ // then they should be aligned later in #updateResolvedBoundsPosition()
if (!mTmpBounds.isEmpty()) {
resolvedBounds.set(mTmpBounds);
}
@@ -8275,22 +8378,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
}
- // Vertically center within parent (bounds) - this is a UX choice and exclude the horizontal
- // decor if needed. Horizontal position is adjusted in
- // updateResolvedBoundsHorizontalPosition.
+ // The position will be later adjusted in updateResolvedBoundsPosition.
// Above coordinates are in "@" space, now place "*" and "#" to screen space.
final boolean fillContainer = resolvedBounds.equals(containingBounds);
final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left;
- // If the activity is not in size compat mode, calculate vertical centering
- // from the container and resolved bounds.
- // If the activity is in size compat mode, calculate vertical centering
- // from the container and size compat bounds.
- // The container bounds contain the parent bounds offset in the display, for
- // example when an activity is in the lower split of split screen.
- final int screenPosY = (mSizeCompatBounds == null
- ? (containerBounds.height() - resolvedBounds.height()) / 2
- : (containerBounds.height() - mSizeCompatBounds.height()) / 2)
- + containerBounds.top;
+ final int screenPosY = fillContainer ? containerBounds.top : containerAppBounds.top;
if (screenPosX != 0 || screenPosY != 0) {
if (mSizeCompatBounds != null) {
@@ -8350,9 +8442,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return true;
}
- /** @return The horizontal offset of putting the content in the center of viewport. */
- private static int getHorizontalCenterOffset(int viewportW, int contentW) {
- return (int) ((viewportW - contentW + 1) * 0.5f);
+ /** @return The horizontal / vertical offset of putting the content in the center of viewport.*/
+ private static int getCenterOffset(int viewportDim, int contentDim) {
+ return (int) ((viewportDim - contentDim + 1) * 0.5f);
}
private static void offsetBounds(Configuration inOutConfig, int offsetX, int offsetY) {
@@ -8540,7 +8632,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
Rect containingBounds) {
return applyAspectRatio(outBounds, containingAppBounds, containingBounds,
- 0 /* desiredAspectRatio */, false /* fixedOrientationLetterboxed */);
+ 0 /* desiredAspectRatio */);
}
/**
@@ -8551,23 +8643,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
- Rect containingBounds, float desiredAspectRatio, boolean fixedOrientationLetterboxed) {
+ Rect containingBounds, float desiredAspectRatio) {
final float maxAspectRatio = info.getMaxAspectRatio();
final Task rootTask = getRootTask();
final float minAspectRatio = getMinAspectRatio();
- // Not using ActivityRecord#isResizeable() directly because app compatibility testing
- // showed that android:supportsPictureInPicture="true" alone is not sufficient signal for
- // not letterboxing an app.
- // TODO(214602463): Remove multi-window check since orientation and aspect ratio
- // restrictions should always be applied in multi-window.
+ final TaskFragment organizedTf = getOrganizedTaskFragment();
if (task == null || rootTask == null
- || (inMultiWindowMode() && isResizeable(/* checkPictureInPictureSupport */ false)
- && !fixedOrientationLetterboxed)
|| (maxAspectRatio < 1 && minAspectRatio < 1 && desiredAspectRatio < 1)
- || isInVrUiMode(getConfiguration())) {
- // We don't enforce aspect ratio if the activity task is in multiwindow unless it is in
- // size-compat mode or is letterboxed from fixed orientation. We also don't set it if we
- // are in VR mode.
+ // Don't set aspect ratio if we are in VR mode.
+ || isInVrUiMode(getConfiguration())
+ // TODO(b/232898850): Always respect aspect ratio requests.
+ // Don't set aspect ratio for activity in ActivityEmbedding split.
+ || (organizedTf != null && !organizedTf.fillsParent())) {
return false;
}
@@ -9615,7 +9702,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
outBounds.bottom = dH;
outBounds.right = (int) ((float) dH * dH / dW);
}
- outBounds.offset(getHorizontalCenterOffset(mWidth, outBounds.width()), 0 /* dy */);
+ outBounds.offset(getCenterOffset(mWidth, outBounds.width()), 0 /* dy */);
}
outAppBounds.set(outBounds);
@@ -9663,6 +9750,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
false /*isNotInRecents*/,
record.mThumbnailAdapter != null ? record.mThumbnailAdapter.mCapturedLeash : null,
record.mStartBounds, task.getTaskInfo(), checkEnterPictureInPictureAppOpsState());
+ target.setShowBackdrop(record.mShowBackdrop);
target.hasAnimatingParent = record.hasAnimatingParent();
return target;
}
@@ -9684,6 +9772,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void setPictureInPictureParams(PictureInPictureParams p) {
pictureInPictureArgs.copyOnlySet(p);
+ adjustPictureInPictureParamsIfNeeded(getBounds());
getTask().getRootTask().onPictureInPictureParamsChanged();
}
@@ -9695,6 +9784,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Override
boolean isSyncFinished() {
if (!super.isSyncFinished()) return false;
+ if (mDisplayContent != null && mDisplayContent.mUnknownAppVisibilityController
+ .isVisibilityUnknown(this)) {
+ return false;
+ }
if (!isVisibleRequested()) return true;
// Wait for attach. That is the earliest time where we know if there will be an associated
// display rotation. If we don't wait, the starting-window can finishDrawing first and
@@ -9731,6 +9824,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return new Point(windowLayout.minWidth, windowLayout.minHeight);
}
+ /**
+ * Adjust the source rect hint in {@link #pictureInPictureArgs} by window bounds since
+ * it is relative to its root view (see also b/235599028).
+ * It is caller's responsibility to make sure this is called exactly once when we update
+ * {@link #pictureInPictureArgs} to avoid double offset.
+ */
+ private void adjustPictureInPictureParamsIfNeeded(Rect windowBounds) {
+ if (pictureInPictureArgs != null && pictureInPictureArgs.hasSourceBoundsHint()) {
+ pictureInPictureArgs.getSourceRectHint().offset(windowBounds.left, windowBounds.top);
+ }
+ }
+
static class Builder {
private final ActivityTaskManagerService mAtmService;
private WindowProcessController mCallerApp;
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index fc22e2decbef..5d038dcab73a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -19,7 +19,6 @@ package com.android.server.wm;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.os.InputConfig;
-import android.os.Process;
import android.view.InputWindowHandle;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -82,8 +81,8 @@ class ActivityRecordInputSink {
// Don't block touches from passing through to an activity below us in the same task, if
// that activity is either from the same uid or if that activity has launched an activity
// in our uid.
- final ActivityRecord activityBelowInTask =
- mActivityRecord.getTask().getActivityBelow(mActivityRecord);
+ final ActivityRecord activityBelowInTask = mActivityRecord.getTask() != null
+ ? mActivityRecord.getTask().getActivityBelow(mActivityRecord) : null;
final boolean allowPassthrough = activityBelowInTask != null && (
activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid()
|| activityBelowInTask.isUid(mActivityRecord.getUid()));
@@ -102,8 +101,8 @@ class ActivityRecordInputSink {
inputWindowHandle.replaceTouchableRegionWithCrop = true;
inputWindowHandle.name = mName;
inputWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
- inputWindowHandle.ownerUid = Process.myUid();
- inputWindowHandle.ownerPid = Process.myPid();
+ inputWindowHandle.ownerPid = WindowManagerService.MY_PID;
+ inputWindowHandle.ownerUid = WindowManagerService.MY_UID;
inputWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.NO_INPUT_CHANNEL;
return inputWindowHandle;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 32ed4725b3b4..9be93404dcf1 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -19,7 +19,9 @@ package com.android.server.wm;
import static android.app.ActivityManager.START_CANCELED;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
@@ -29,6 +31,7 @@ import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
import android.content.ComponentName;
@@ -46,6 +49,7 @@ import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
import android.view.RemoteAnimationAdapter;
+import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -90,6 +94,8 @@ public class ActivityStartController {
boolean mCheckedForSetup = false;
+ private boolean mInExecution = false;
+
/**
* TODO(b/64750076): Capture information necessary for dump and
* {@link #postStartActivityProcessingForLastStarter} rather than keeping the entire object
@@ -123,7 +129,15 @@ public class ActivityStartController {
return mFactory.obtain().setIntent(intent).setReason(reason);
}
+ void onExecutionStarted(ActivityStarter starter) {
+ mInExecution = true;
+ }
+
+ boolean isInExecution() {
+ return mInExecution;
+ }
void onExecutionComplete(ActivityStarter starter) {
+ mInExecution = false;
if (mLastStarter == null) {
mLastStarter = mFactory.obtain();
}
@@ -526,6 +540,42 @@ public class ActivityStartController {
.execute();
}
+ boolean startExistingRecentsIfPossible(Intent intent, ActivityOptions options) {
+ final int activityType = mService.getRecentTasks().getRecentsComponent()
+ .equals(intent.getComponent()) ? ACTIVITY_TYPE_RECENTS : ACTIVITY_TYPE_HOME;
+ final Task rootTask = mService.mRootWindowContainer.getDefaultTaskDisplayArea()
+ .getRootTask(WINDOWING_MODE_UNDEFINED, activityType);
+ if (rootTask == null) return false;
+ final ActivityRecord r = rootTask.topRunningActivity();
+ if (r == null || r.mVisibleRequested || !r.attachedToProcess()
+ || !r.mActivityComponent.equals(intent.getComponent())
+ // Recents keeps invisible while device is locked.
+ || r.mDisplayContent.isKeyguardLocked()) {
+ return false;
+ }
+ mService.mRootWindowContainer.startPowerModeLaunchIfNeeded(true /* forceSend */, r);
+ final ActivityMetricsLogger.LaunchingState launchingState =
+ mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
+ final Task task = r.getTask();
+ mService.deferWindowLayout();
+ try {
+ task.mTransitionController.requestTransitionIfNeeded(WindowManager.TRANSIT_TO_FRONT,
+ 0 /* flags */, task, task /* readyGroupRef */,
+ options.getRemoteTransition(), null /* displayChange */);
+ r.mTransitionController.setTransientLaunch(r,
+ TaskDisplayArea.getRootTaskAbove(rootTask));
+ task.moveToFront("startExistingRecents");
+ task.mInResumeTopActivity = true;
+ task.resumeTopActivity(null /* prev */, options, true /* deferPause */);
+ mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState,
+ ActivityManager.START_TASK_TO_FRONT, false, r, options);
+ } finally {
+ task.mInResumeTopActivity = false;
+ mService.continueWindowLayout();
+ }
+ return true;
+ }
+
void registerRemoteAnimationForNextActivityStart(String packageName,
RemoteAnimationAdapter adapter, @Nullable IBinder launchCookie) {
mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter, launchCookie);
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index a452013bf42a..7d84bdf78056 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -49,6 +49,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -64,7 +65,7 @@ import com.android.server.am.ActivityManagerService;
import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult;
/**
- * A class that contains activity intercepting logic for {@link ActivityStarter#startActivityLocked}
+ * A class that contains activity intercepting logic for {@link ActivityStarter#execute()}
* It's initialized via setStates and interception occurs via the intercept method.
*
* Note that this class is instantiated when {@link ActivityManagerService} gets created so there
@@ -104,6 +105,7 @@ class ActivityStartInterceptor {
ActivityInfo mAInfo;
String mResolvedType;
Task mInTask;
+ TaskFragment mInTaskFragment;
ActivityOptions mActivityOptions;
ActivityStartInterceptor(
@@ -135,15 +137,46 @@ class ActivityStartInterceptor {
}
private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) {
- Bundle activityOptions = deferCrossProfileAppsAnimationIfNecessary();
+ Bundle bOptions = deferCrossProfileAppsAnimationIfNecessary();
+ final TaskFragment taskFragment = getLaunchTaskFragment();
+ // If the original intent is going to be embedded, try to forward the embedding TaskFragment
+ // and its task id to embed back the original intent.
+ if (taskFragment != null) {
+ ActivityOptions activityOptions = bOptions != null
+ ? ActivityOptions.fromBundle(bOptions)
+ : ActivityOptions.makeBasic();
+ activityOptions.setLaunchTaskFragmentToken(taskFragment.getFragmentToken());
+ bOptions = activityOptions.toBundle();
+ }
final IIntentSender target = mService.getIntentSenderLocked(
INTENT_SENDER_ACTIVITY, mCallingPackage, mCallingFeatureId, callingUid, mUserId,
null /*token*/, null /*resultCode*/, 0 /*requestCode*/,
new Intent[] { mIntent }, new String[] { mResolvedType },
- flags, activityOptions);
+ flags, bOptions);
return new IntentSender(target);
}
+
+ /**
+ * A helper function to obtain the targeted {@link TaskFragment} during
+ * {@link #intercept(Intent, ResolveInfo, ActivityInfo, String, Task, TaskFragment, int, int,
+ * ActivityOptions)} if any.
+ */
+ @Nullable
+ private TaskFragment getLaunchTaskFragment() {
+ if (mInTaskFragment != null) {
+ return mInTaskFragment;
+ }
+ if (mActivityOptions == null) {
+ return null;
+ }
+ final IBinder taskFragToken = mActivityOptions.getLaunchTaskFragmentToken();
+ if (taskFragToken == null) {
+ return null;
+ }
+ return TaskFragment.fromTaskFragmentToken(taskFragToken, mService);
+ }
+
/**
* Intercept the launch intent based on various signals. If an interception happened the
* internal variables get assigned and need to be read explicitly by the caller.
@@ -151,7 +184,8 @@ class ActivityStartInterceptor {
* @return true if an interception occurred
*/
boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
- Task inTask, int callingPid, int callingUid, ActivityOptions activityOptions) {
+ Task inTask, TaskFragment inTaskFragment, int callingPid, int callingUid,
+ ActivityOptions activityOptions) {
mUserManager = UserManager.get(mServiceContext);
mIntent = intent;
@@ -161,6 +195,7 @@ class ActivityStartInterceptor {
mAInfo = aInfo;
mResolvedType = resolvedType;
mInTask = inTask;
+ mInTaskFragment = inTaskFragment;
mActivityOptions = activityOptions;
if (interceptQuietProfileIfNeeded()) {
@@ -332,12 +367,21 @@ class ActivityStartInterceptor {
mCallingPid = mRealCallingPid;
mCallingUid = mRealCallingUid;
mResolvedType = null;
+ final TaskFragment taskFragment = getLaunchTaskFragment();
// If we are intercepting and there was a task, convert it into an extra for the
// ConfirmCredentials intent and unassign it, as otherwise the task will move to
// front even if ConfirmCredentials is cancelled.
if (mInTask != null) {
mIntent.putExtra(EXTRA_TASK_ID, mInTask.mTaskId);
mInTask = null;
+ } else if (taskFragment != null) {
+ // If the original intent is started to an embedded TaskFragment, append its parent task
+ // id to extra. It is to embed back the original intent to the TaskFragment with the
+ // same task.
+ final Task parentTask = taskFragment.getTask();
+ if (parentTask != null) {
+ mIntent.putExtra(EXTRA_TASK_ID, parentTask.mTaskId);
+ }
}
if (mActivityOptions == null) {
mActivityOptions = ActivityOptions.makeBasic();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index ec9babf09ef3..890b91025151 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -51,6 +51,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
+import static android.content.pm.ActivityInfo.launchModeToString;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.INVALID_UID;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -216,6 +217,7 @@ class ActivityStarter {
// The task which was above the targetTask before starting this activity. null if the targetTask
// was already on top or if the activity is in a new task.
private Task mPriorAboveTask;
+ private boolean mDisplayLockAndOccluded;
// We must track when we deliver the new intent since multiple code paths invoke
// {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used
@@ -647,6 +649,8 @@ class ActivityStarter {
*/
int execute() {
try {
+ onExecutionStarted();
+
// Refuse possible leaked file descriptors
if (mRequest.intent != null && mRequest.intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -1056,8 +1060,8 @@ class ActivityStarter {
mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage,
callingFeatureId);
- if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid,
- callingUid, checkedOptions)) {
+ if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment,
+ callingPid, callingUid, checkedOptions)) {
// activity start was intercepted, e.g. because the target user is currently in quiet
// mode (turn off work) or the target application is suspended
intent = mInterceptor.mIntent;
@@ -1261,6 +1265,10 @@ class ActivityStarter {
mController.onExecutionComplete(this);
}
+ private void onExecutionStarted() {
+ mController.onExecutionStarted(this);
+ }
+
private boolean isHomeApp(int uid, @Nullable String packageName) {
if (mService.mHomeProcess != null) {
// Fast check
@@ -1657,14 +1665,17 @@ class ActivityStarter {
transitionController.collect(r);
try {
mService.deferWindowLayout();
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
- result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
- startFlags, doResume, options, inTask, inTaskFragment, restrictedBgActivity,
- intentGrants);
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
+ result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
+ startFlags, doResume, options, inTask, inTaskFragment, restrictedBgActivity,
+ intentGrants);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ startedActivityRootTask = handleStartResult(r, options, result, newTransition,
+ remoteTransition);
+ }
} finally {
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
- startedActivityRootTask = handleStartResult(r, options, result, newTransition,
- remoteTransition);
mService.continueWindowLayout();
}
postStartActivityProcessing(r, result, startedActivityRootTask);
@@ -1681,6 +1692,7 @@ class ActivityStarter {
private @Nullable Task handleStartResult(@NonNull ActivityRecord started,
ActivityOptions options, int result, Transition newTransition,
RemoteTransition remoteTransition) {
+ final boolean userLeaving = mSupervisor.mUserLeaving;
mSupervisor.mUserLeaving = false;
final Task currentRootTask = started.getRootTask();
final Task startedActivityRootTask =
@@ -1699,7 +1711,8 @@ class ActivityStarter {
// Root task should also be detached from display and be removed if it's empty.
if (startedActivityRootTask != null && startedActivityRootTask.isAttached()
&& !startedActivityRootTask.hasActivity()
- && !startedActivityRootTask.isActivityTypeHome()) {
+ && !startedActivityRootTask.isActivityTypeHome()
+ && !startedActivityRootTask.mCreatedByOrganizer) {
startedActivityRootTask.removeIfPossible("handleStartResult");
}
if (newTransition != null) {
@@ -1740,19 +1753,42 @@ class ActivityStarter {
// Transition housekeeping.
final TransitionController transitionController = started.mTransitionController;
final boolean isStarted = result == START_SUCCESS || result == START_TASK_TO_FRONT;
+ final boolean isTransientLaunch = options != null && options.getTransientLaunch();
+ // Start transient launch while keyguard locked and occluded by other app, for this
+ // condition we would like to play the remote transition without modify any visible state
+ // for the hierarchy in core, so here will force execute this transition.
+ final boolean forceTransientTransition = isTransientLaunch && mPriorAboveTask != null
+ && mDisplayLockAndOccluded;
if (isStarted) {
// The activity is started new rather than just brought forward, so record it as an
// existence change.
transitionController.collectExistenceChange(started);
} else if (result == START_DELIVERED_TO_TOP && newTransition != null) {
// We just delivered to top, so there isn't an actual transition here.
- newTransition.abort();
- newTransition = null;
+ if (!forceTransientTransition) {
+ newTransition.abort();
+ newTransition = null;
+ }
}
- if (options != null && options.getTransientLaunch()) {
+ if (isTransientLaunch) {
+ if (forceTransientTransition && newTransition != null) {
+ newTransition.collect(mLastStartActivityRecord);
+ newTransition.collect(mPriorAboveTask);
+ }
// `started` isn't guaranteed to be the actual relevant activity, so we must wait
// until after we launched to identify the relevant activity.
transitionController.setTransientLaunch(mLastStartActivityRecord, mPriorAboveTask);
+ if (forceTransientTransition && newTransition != null) {
+ final DisplayContent dc = mLastStartActivityRecord.getDisplayContent();
+ // update wallpaper target to TransientHide
+ dc.mWallpaperController.adjustWallpaperWindows();
+ // execute transition because there is no change
+ newTransition.setReady(dc, true /* ready */);
+ }
+ }
+ if (!userLeaving) {
+ // no-user-leaving implies not entering PiP.
+ transitionController.setCanPipOnFinish(false /* canPipOnFinish */);
}
if (newTransition != null) {
transitionController.requestStartTransition(newTransition,
@@ -2389,6 +2425,7 @@ class ActivityStarter {
mAvoidMoveToFront = false;
mFrozeTaskList = false;
mTransientLaunch = false;
+ mDisplayLockAndOccluded = false;
mVoiceSession = null;
mVoiceInteractor = null;
@@ -2492,6 +2529,16 @@ class ActivityStarter {
mAvoidMoveToFront = true;
}
mTransientLaunch = mOptions.getTransientLaunch();
+ final KeyguardController kc = mSupervisor.getKeyguardController();
+ final int displayId = mPreferredTaskDisplayArea.getDisplayId();
+ mDisplayLockAndOccluded = kc.isKeyguardLocked(displayId)
+ && kc.isDisplayOccluded(displayId);
+ // Recents animation on lock screen, do not resume & move launcher to top.
+ if (mTransientLaunch && mDisplayLockAndOccluded
+ && mService.getTransitionController().isShellTransitionsEnabled()) {
+ mDoResume = false;
+ mAvoidMoveToFront = true;
+ }
mTargetRootTask = Task.fromWindowContainerToken(mOptions.getLaunchRootTask());
if (inTaskFragment == null) {
@@ -2640,6 +2687,12 @@ class ActivityStarter {
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
}
+
+ if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
+ && ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 || mSourceRecord == null)) {
+ // ignore the flag if there is no the sourceRecord or without new_task flag
+ mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT;
+ }
}
private void computeSourceRootTask() {
@@ -2759,17 +2812,15 @@ class ActivityStarter {
mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, intentTask,
mOptions);
}
- } else {
- // If a launch target indicated, and the matching task is already in the adjacent task
- // of the launch target. Adjust to use the adjacent task as its launch target. So the
- // existing task will be launched into the closer one and won't be reparent redundantly.
- // TODO(b/231541706): Migrate the logic to wm-shell after having proper APIs to help
- // resolve target task without actually starting the activity.
- final Task adjacentTargetTask = mTargetRootTask.getAdjacentTaskFragment() != null
- ? mTargetRootTask.getAdjacentTaskFragment().asTask() : null;
- if (adjacentTargetTask != null && intentActivity.isDescendantOf(adjacentTargetTask)) {
- mTargetRootTask = adjacentTargetTask;
- }
+ }
+
+ // If the matching task is already in the adjacent task of the launch target. Adjust to use
+ // the adjacent task as its launch target. So the existing task will be launched into the
+ // closer one and won't be reparent redundantly.
+ final Task adjacentTargetTask = mTargetRootTask.getAdjacentTaskFragment() != null
+ ? mTargetRootTask.getAdjacentTaskFragment().asTask() : null;
+ if (adjacentTargetTask != null && intentActivity.isDescendantOf(adjacentTargetTask)) {
+ mTargetRootTask = adjacentTargetTask;
}
// If the target task is not in the front, then we need to bring it to the front...
@@ -2940,7 +2991,7 @@ class ActivityStarter {
switch(result) {
case EMBEDDING_DISALLOWED_NEW_TASK: {
errMsg = "Cannot embed " + mStartActivity + " that launched on another task"
- + ",mLaunchMode=" + mLaunchMode
+ + ",mLaunchMode=" + launchModeToString(mLaunchMode)
+ ",mLaunchFlag=" + Integer.toHexString(mLaunchFlags);
break;
}
@@ -3270,12 +3321,8 @@ class ActivityStarter {
pw.println(mOptions);
}
pw.print(prefix);
- pw.print("mLaunchSingleTop=");
- pw.print(LAUNCH_SINGLE_TOP == mLaunchMode);
- pw.print(" mLaunchSingleInstance=");
- pw.print(LAUNCH_SINGLE_INSTANCE == mLaunchMode);
- pw.print(" mLaunchSingleTask=");
- pw.println(LAUNCH_SINGLE_TASK == mLaunchMode);
+ pw.print("mLaunchMode=");
+ pw.print(launchModeToString(mLaunchMode));
pw.print(prefix);
pw.print("mLaunchFlags=0x");
pw.print(Integer.toHexString(mLaunchFlags));
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 8e7dde20955b..e7b62b0d66d3 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -675,9 +675,6 @@ public abstract class ActivityTaskManagerInternal {
public abstract boolean hasSystemAlertWindowPermission(int callingUid, int callingPid,
String callingPackage);
- /** Called when the device is waking up */
- public abstract void notifyWakingUp();
-
/**
* Registers a callback which can intercept activity starts.
* @throws IllegalArgumentException if duplicate ids are provided
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index aa154292fe7e..9d8e0877584f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -64,8 +64,8 @@ import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
import static android.provider.Settings.System.FONT_SCALE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT;
-import static android.view.WindowManager.TRANSIT_WAKE;
+import static android.view.WindowManager.TRANSIT_PIP;
+import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
@@ -73,7 +73,6 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR;
-import static com.android.server.am.ActivityManagerService.MY_PID;
import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
import static com.android.server.am.ActivityManagerService.dumpStackTraces;
import static com.android.server.am.ActivityManagerServiceDumpActivitiesProto.ROOT_WINDOW_CONTAINER;
@@ -95,6 +94,7 @@ import static com.android.server.am.EventLogTags.writeBootProgressEnableScreen;
import static com.android.server.am.EventLogTags.writeConfigurationChanged;
import static com.android.server.wm.ActivityInterceptorCallback.FIRST_ORDERED_ID;
import static com.android.server.wm.ActivityInterceptorCallback.LAST_ORDERED_ID;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
@@ -116,6 +116,7 @@ import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_O
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_ONLY;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
+import static com.android.server.wm.WindowManagerService.MY_PID;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import android.Manifest;
@@ -238,6 +239,7 @@ import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.TransferPipe;
import com.android.internal.policy.AttributeCache;
import com.android.internal.policy.KeyguardDismissCallback;
+import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
@@ -400,6 +402,26 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
/** The time at which the previous process was last visible. */
private long mPreviousProcessVisibleTime;
+ /** It is set from keyguard-going-away to set-keyguard-shown. */
+ static final int DEMOTE_TOP_REASON_DURING_UNLOCKING = 1;
+ /** It is set if legacy recents animation is running. */
+ static final int DEMOTE_TOP_REASON_ANIMATING_RECENTS = 1 << 1;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ DEMOTE_TOP_REASON_DURING_UNLOCKING,
+ DEMOTE_TOP_REASON_ANIMATING_RECENTS,
+ })
+ @interface DemoteTopReason {}
+
+ /**
+ * If non-zero, getTopProcessState() will
+ * return {@link ActivityManager#PROCESS_STATE_IMPORTANT_FOREGROUND} to avoid top app from
+ * preempting CPU while another process is running an important animation.
+ */
+ @DemoteTopReason
+ volatile int mDemoteTopAppReasons;
+
/** List of intents that were used to start the most recent tasks. */
private RecentTasks mRecentTasks;
/** State of external calls telling us if the device is awake or asleep. */
@@ -1209,6 +1231,28 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Nullable String callingFeatureId, Intent intent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
+
+ final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions);
+ // A quick path (skip general intent/task resolving) to start recents animation if the
+ // recents (or home) activity is available in background.
+ if (opts != null && opts.getOriginalOptions().getTransientLaunch()
+ && isCallerRecents(Binder.getCallingUid())) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "startExistingRecents");
+ if (mActivityStartController.startExistingRecentsIfPossible(
+ intent, opts.getOriginalOptions())) {
+ return ActivityManager.START_TASK_TO_FRONT;
+ }
+ // Else follow the standard launch procedure.
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
assertPackageMatchesCallingUid(callingPackage);
enforceNotIsolatedCaller("startActivityAsUser");
if (Process.isSdkSandboxUid(Binder.getCallingUid())) {
@@ -1235,7 +1279,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
- .setActivityOptions(bOptions)
+ .setActivityOptions(opts)
.setUserId(userId)
.execute();
@@ -2157,14 +2201,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
: null;
mTaskSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",
false /* forceNonResizable */);
-
- final ActivityRecord topActivity = task.getTopNonFinishingActivity();
- if (topActivity != null) {
-
- // We are reshowing a task, use a starting window to hide the initial draw delay
- // so the transition can start earlier.
- topActivity.showStartingWindow(true /* taskSwitch */);
- }
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -2830,12 +2866,24 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
keyguardShowing);
mH.sendMessage(msg);
}
+ // Always reset the state regardless of keyguard-showing change, because that means the
+ // unlock is either completed or canceled.
+ if ((mDemoteTopAppReasons & DEMOTE_TOP_REASON_DURING_UNLOCKING) != 0) {
+ mDemoteTopAppReasons &= ~DEMOTE_TOP_REASON_DURING_UNLOCKING;
+ // The scheduling group of top process was demoted by unlocking, so recompute
+ // to restore its real top priority if possible.
+ if (mTopApp != null) {
+ mTopApp.scheduleUpdateOomAdj();
+ }
+ }
try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "setLockScreenShown");
mRootWindowContainer.forAllDisplays(displayContent -> {
mKeyguardController.setKeyguardShown(displayContent.getDisplayId(),
keyguardShowing, aodShowing);
});
} finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
Binder.restoreCallingIdentity(ident);
}
}
@@ -2862,6 +2910,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// animation of system UI. Even if AOD is not enabled, it should be no harm.
final WindowProcessController proc;
synchronized (mGlobalLockWithoutBoost) {
+ mDemoteTopAppReasons &= ~DEMOTE_TOP_REASON_DURING_UNLOCKING;
final WindowState notificationShade = mRootWindowContainer.getDefaultDisplay()
.getDisplayPolicy().getNotificationShade();
proc = notificationShade != null
@@ -3399,8 +3448,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
try {
synchronized (mGlobalLock) {
// Keyguard asked us to clear the home task snapshot before going away, so do that.
- if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) {
+ if ((flags & KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) {
mActivityClientController.invalidateHomeTaskSnapshot(null /* token */);
+ } else if (mKeyguardShown) {
+ // Only set if it is not unlocking to launcher which may also animate.
+ mDemoteTopAppReasons |= DEMOTE_TOP_REASON_DURING_UNLOCKING;
}
mRootWindowContainer.forAllDisplays(displayContent -> {
@@ -3448,10 +3500,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
/**
* Puts the given activity in picture in picture mode if possible.
*
+ * @param fromClient true if this comes from a client call (eg. Activity.enterPip).
* @return true if the activity is now in picture-in-picture mode, or false if it could not
* enter picture-in-picture mode.
*/
- boolean enterPictureInPictureMode(@NonNull ActivityRecord r, PictureInPictureParams params) {
+ boolean enterPictureInPictureMode(@NonNull ActivityRecord r,
+ @NonNull PictureInPictureParams params, boolean fromClient) {
// If the activity is already in picture in picture mode, then just return early
if (r.inPinnedWindowingMode()) {
return true;
@@ -3464,6 +3518,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return false;
}
+ // If the app is using legacy-entry (not auto-enter), then we will get a client-request
+ // that was actually a server-request (via pause(userLeaving=true)). This happens when
+ // the app is PAUSING, so detect that case here.
+ boolean originallyFromClient = fromClient
+ && (!r.isState(PAUSING) || params.isAutoEnterEnabled());
+
+ // Create a transition only for this pip entry if it is coming from the app without the
+ // system requesting that the app enter-pip. If the system requested it, that means it
+ // should be part of that transition if possible.
+ final Transition transition =
+ (getTransitionController().isShellTransitionsEnabled() && originallyFromClient)
+ ? new Transition(TRANSIT_PIP, 0 /* flags */,
+ getTransitionController(), mWindowManager.mSyncEngine)
+ : null;
+
final Runnable enterPipRunnable = () -> {
synchronized (mGlobalLock) {
if (r.getParent() == null) {
@@ -3472,11 +3541,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
r.setPictureInPictureParams(params);
mRootWindowContainer.moveActivityToPinnedRootTask(r,
- null /* launchIntoPipHostActivity */, "enterPictureInPictureMode");
- final Task task = r.getTask();
+ null /* launchIntoPipHostActivity */, "enterPictureInPictureMode",
+ transition);
// Continue the pausing process after entering pip.
- if (task.getPausingActivity() == r) {
- task.schedulePauseActivity(r, false /* userLeaving */,
+ if (r.isState(PAUSING)) {
+ r.getTask().schedulePauseActivity(r, false /* userLeaving */,
false /* pauseImmediately */, "auto-pip");
}
}
@@ -3489,12 +3558,36 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mActivityClientController.dismissKeyguard(r.token, new KeyguardDismissCallback() {
@Override
public void onDismissSucceeded() {
- mH.post(enterPipRunnable);
+ if (transition != null && mWindowManager.mSyncEngine.hasActiveSync()) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+ "Creating Pending Pip-Enter: %s", transition);
+ mWindowManager.mSyncEngine.queueSyncSet(
+ () -> getTransitionController().moveToCollecting(transition),
+ enterPipRunnable);
+ } else {
+ // Move to collecting immediately to "claim" the sync-engine for this
+ // transition.
+ if (transition != null) {
+ getTransitionController().moveToCollecting(transition);
+ }
+ mH.post(enterPipRunnable);
+ }
}
}, null /* message */);
} else {
// Enter picture in picture immediately otherwise
- enterPipRunnable.run();
+ if (transition != null && mWindowManager.mSyncEngine.hasActiveSync()) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+ "Creating Pending Pip-Enter: %s", transition);
+ mWindowManager.mSyncEngine.queueSyncSet(
+ () -> getTransitionController().moveToCollecting(transition),
+ enterPipRunnable);
+ } else {
+ if (transition != null) {
+ getTransitionController().moveToCollecting(transition);
+ }
+ enterPipRunnable.run();
+ }
}
return true;
}
@@ -3588,7 +3681,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution) {
+ public TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution,
+ boolean takeSnapshotIfNeeded) {
mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
final long ident = Binder.clearCallingIdentity();
try {
@@ -3602,8 +3696,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
// Don't call this while holding the lock as this operation might hit the disk.
- return mWindowManager.mTaskSnapshotController.getSnapshot(taskId, task.mUserId,
- true /* restoreFromDisk */, isLowResolution);
+ TaskSnapshot taskSnapshot = mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
+ task.mUserId, true /* restoreFromDisk */, isLowResolution);
+ if (taskSnapshot == null && takeSnapshotIfNeeded) {
+ taskSnapshot = takeTaskSnapshot(taskId);
+ }
+ return taskSnapshot;
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3968,6 +4066,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mTaskOrganizerController.dump(pw, " ");
mVisibleActivityProcessTracker.dump(pw, " ");
mActiveUids.dump(pw, " ");
+ if (mDemoteTopAppReasons != 0) {
+ pw.println(" mDemoteTopAppReasons=" + mDemoteTopAppReasons);
+ }
}
if (!printedAnything) {
@@ -5595,12 +5696,17 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
@Override
public int getTopProcessState() {
+ final int topState = mTopProcessState;
+ if (mDemoteTopAppReasons != 0 && topState == ActivityManager.PROCESS_STATE_TOP) {
+ // There may be a more important UI/animation than the top app.
+ return ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ }
if (mRetainPowerModeAndTopProcessState) {
// There is a launching app while device may be sleeping, force the top state so
// the launching process can have top-app scheduling group.
return ActivityManager.PROCESS_STATE_TOP;
}
- return mTopProcessState;
+ return topState;
}
@HotPath(caller = HotPath.PROCESS_CHANGE)
@@ -6568,7 +6674,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public TaskSnapshot getTaskSnapshotBlocking(
int taskId, boolean isLowResolution) {
- return ActivityTaskManagerService.this.getTaskSnapshot(taskId, isLowResolution);
+ return ActivityTaskManagerService.this.getTaskSnapshot(taskId, isLowResolution,
+ false /* takeSnapshotIfNeeded */);
}
@Override
@@ -6626,15 +6733,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
- public void notifyWakingUp() {
- synchronized (mGlobalLock) {
- // Start a transition for waking. This is needed for showWhenLocked activities.
- getTransitionController().requestTransitionIfNeeded(TRANSIT_WAKE, 0 /* flags */,
- null /* trigger */, mRootWindowContainer.getDefaultDisplay());
- }
- }
-
- @Override
public void registerActivityStartInterceptor(
@ActivityInterceptorCallback.OrderedId int id,
ActivityInterceptorCallback callback) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 35f977de8290..84740683f6aa 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -42,6 +42,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static android.os.Process.INVALID_UID;
+import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -73,13 +74,9 @@ import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
import static com.android.server.wm.Task.TAG_CLEANUP;
-import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
-import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.Manifest;
@@ -93,6 +90,7 @@ import android.app.AppOpsManagerInternal;
import android.app.IActivityClientController;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
+import android.app.TaskInfo;
import android.app.WaitResult;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ClientTransaction;
@@ -121,7 +119,6 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
-import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -155,6 +152,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
// TODO: This class has become a dumping ground. Let's
// - Move things relating to the hierarchy to RootWindowContainer
@@ -246,6 +244,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
/** Helper class to abstract out logic for fetching the set of currently running tasks */
private RunningTasks mRunningTasks;
+ /** Helper for {@link Task#fillTaskInfo}. */
+ final TaskInfoHelper mTaskInfoHelper = new TaskInfoHelper();
+
private final ActivityTaskSupervisorHandler mHandler;
final Looper mLooper;
@@ -1316,7 +1317,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
void acquireLaunchWakelock() {
- if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
+ if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != SYSTEM_UID) {
throw new IllegalStateException("Calling must be system uid");
}
mLaunchingActivityWakeLock.acquire();
@@ -1389,8 +1390,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
if (mLaunchingActivityWakeLock.isHeld()) {
mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);
- if (VALIDATE_WAKE_LOCK_CALLER &&
- Binder.getCallingUid() != Process.myUid()) {
+ if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != SYSTEM_UID) {
throw new IllegalStateException("Calling must be system uid");
}
mLaunchingActivityWakeLock.release();
@@ -1434,10 +1434,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
mUserLeaving = true;
}
- task.mTransitionController.requestTransitionIfNeeded(TRANSIT_TO_FRONT,
- 0 /* flags */, task, task /* readyGroupRef */,
- options != null ? options.getRemoteTransition() : null,
- null /* displayChange */);
+ final Transition newTransition = task.mTransitionController.isShellTransitionsEnabled()
+ ? task.mTransitionController.isCollecting() ? null
+ : task.mTransitionController.createTransition(TRANSIT_TO_FRONT) : null;
+ task.mTransitionController.collect(task);
reason = reason + " findTaskToMoveToFront";
boolean reparented = false;
if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
@@ -1480,6 +1480,17 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
mRootWindowContainer.getDefaultTaskDisplayArea(), currentRootTask,
forceNonResizeable);
+ if (r != null) {
+ // Use a starting window to reduce the transition latency for reshowing the task.
+ // Note that with shell transition, this should be executed before requesting
+ // transition to avoid delaying the starting window.
+ r.showStartingWindow(true /* taskSwitch */);
+ }
+ if (newTransition != null) {
+ task.mTransitionController.requestStartTransition(newTransition, task,
+ options != null ? options.getRemoteTransition() : null,
+ null /* displayChange */);
+ }
} finally {
mUserLeaving = false;
}
@@ -1800,7 +1811,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
if (!mGoingToSleepWakeLock.isHeld()) {
mGoingToSleepWakeLock.acquire();
if (mLaunchingActivityWakeLock.isHeld()) {
- if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
+ if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != SYSTEM_UID) {
throw new IllegalStateException("Calling must be system uid");
}
mLaunchingActivityWakeLock.release();
@@ -1921,9 +1932,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
ArrayList<ActivityRecord> readyToStopActivities = null;
for (int i = mStoppingActivities.size() - 1; i >= 0; --i) {
final ActivityRecord s = mStoppingActivities.get(i);
- final boolean animating = s.isAnimating(TRANSITION | PARENTS,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
- || s.inTransition();
+ final boolean animating = s.isInTransition();
ProtoLog.v(WM_DEBUG_STATES, "Stopping %s: nowVisible=%b animating=%b "
+ "finishing=%s", s, s.nowVisible, animating, s.finishing);
if (!animating || mService.mShuttingDown) {
@@ -2461,8 +2470,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
case LAUNCH_TIMEOUT_MSG: {
if (mLaunchingActivityWakeLock.isHeld()) {
Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
- if (VALIDATE_WAKE_LOCK_CALLER
- && Binder.getCallingUid() != Process.myUid()) {
+ if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != SYSTEM_UID) {
throw new IllegalStateException("Calling must be system uid");
}
mLaunchingActivityWakeLock.release();
@@ -2623,6 +2631,41 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
/**
+ * Fills the info that needs to iterate all activities of task, such as the number of
+ * non-finishing activities and collecting launch cookies.
+ */
+ static class TaskInfoHelper implements Consumer<ActivityRecord> {
+ private TaskInfo mInfo;
+ private ActivityRecord mTopRunning;
+
+ ActivityRecord fillAndReturnTop(Task task, TaskInfo info) {
+ info.numActivities = 0;
+ info.baseActivity = null;
+ mInfo = info;
+ task.forAllActivities(this);
+ final ActivityRecord top = mTopRunning;
+ mTopRunning = null;
+ mInfo = null;
+ return top;
+ }
+
+ @Override
+ public void accept(ActivityRecord r) {
+ if (r.finishing) {
+ return;
+ }
+ if (r.mLaunchCookie != null) {
+ mInfo.addLaunchCookie(r.mLaunchCookie);
+ }
+ mInfo.numActivities++;
+ mInfo.baseActivity = r.mActivityComponent;
+ if (mTopRunning == null) {
+ mTopRunning = r;
+ }
+ }
+ }
+
+ /**
* Internal container to store a match qualifier alongside a WaitResult.
*/
private static class WaitInfo {
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index 6befefda8a12..b23f50154257 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -256,7 +256,7 @@ class AnrController {
Slog.i(TAG_WM, "Pre-dump for unresponsive");
final ArrayList<Integer> firstPids = new ArrayList<>(1);
- firstPids.add(ActivityManagerService.MY_PID);
+ firstPids.add(WindowManagerService.MY_PID);
ArrayList<Integer> nativePids = null;
final int[] pids = shouldDumpSf[0]
? Process.getPidsForCommands(new String[] { "/system/bin/surfaceflinger" })
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 5410dd8508f1..53f2c7146656 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -34,6 +34,8 @@ import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH;
import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
@@ -64,6 +66,9 @@ import static com.android.internal.R.styleable.WindowAnimation_activityCloseEnte
import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
import static com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation;
import static com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation;
+import static com.android.internal.R.styleable.WindowAnimation_dreamActivityCloseExitAnimation;
+import static com.android.internal.R.styleable.WindowAnimation_dreamActivityOpenEnterAnimation;
+import static com.android.internal.R.styleable.WindowAnimation_dreamActivityOpenExitAnimation;
import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindSourceAnimation;
import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindTargetAnimation;
import static com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation;
@@ -309,6 +314,10 @@ public class AppTransition implements Dump {
setAppTransitionState(APP_STATE_TIMEOUT);
}
+ @ColorInt int getNextAppTransitionBackgroundColor() {
+ return mNextAppTransitionBackgroundColor;
+ }
+
HardwareBuffer getAppTransitionThumbnailHeader(WindowContainer container) {
AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
container.hashCode());
@@ -518,6 +527,95 @@ public class AppTransition implements Dump {
return TransitionAnimation.loadAnimationSafely(context, resId, TAG);
}
+ static int mapOpenCloseTransitTypes(int transit, boolean enter) {
+ int animAttr = 0;
+ switch (transit) {
+ case TRANSIT_OLD_ACTIVITY_OPEN:
+ case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN:
+ animAttr = enter
+ ? WindowAnimation_activityOpenEnterAnimation
+ : WindowAnimation_activityOpenExitAnimation;
+ break;
+ case TRANSIT_OLD_ACTIVITY_CLOSE:
+ case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE:
+ animAttr = enter
+ ? WindowAnimation_activityCloseEnterAnimation
+ : WindowAnimation_activityCloseExitAnimation;
+ break;
+ case TRANSIT_OLD_TASK_OPEN:
+ animAttr = enter
+ ? WindowAnimation_taskOpenEnterAnimation
+ : WindowAnimation_taskOpenExitAnimation;
+ break;
+ case TRANSIT_OLD_TASK_CLOSE:
+ animAttr = enter
+ ? WindowAnimation_taskCloseEnterAnimation
+ : WindowAnimation_taskCloseExitAnimation;
+ break;
+ case TRANSIT_OLD_TASK_TO_FRONT:
+ animAttr = enter
+ ? WindowAnimation_taskToFrontEnterAnimation
+ : WindowAnimation_taskToFrontExitAnimation;
+ break;
+ case TRANSIT_OLD_TASK_TO_BACK:
+ animAttr = enter
+ ? WindowAnimation_taskToBackEnterAnimation
+ : WindowAnimation_taskToBackExitAnimation;
+ break;
+ case TRANSIT_OLD_WALLPAPER_OPEN:
+ animAttr = enter
+ ? WindowAnimation_wallpaperOpenEnterAnimation
+ : WindowAnimation_wallpaperOpenExitAnimation;
+ break;
+ case TRANSIT_OLD_WALLPAPER_CLOSE:
+ animAttr = enter
+ ? WindowAnimation_wallpaperCloseEnterAnimation
+ : WindowAnimation_wallpaperCloseExitAnimation;
+ break;
+ case TRANSIT_OLD_WALLPAPER_INTRA_OPEN:
+ animAttr = enter
+ ? WindowAnimation_wallpaperIntraOpenEnterAnimation
+ : WindowAnimation_wallpaperIntraOpenExitAnimation;
+ break;
+ case TRANSIT_OLD_WALLPAPER_INTRA_CLOSE:
+ animAttr = enter
+ ? WindowAnimation_wallpaperIntraCloseEnterAnimation
+ : WindowAnimation_wallpaperIntraCloseExitAnimation;
+ break;
+ case TRANSIT_OLD_TASK_OPEN_BEHIND:
+ animAttr = enter
+ ? WindowAnimation_launchTaskBehindSourceAnimation
+ : WindowAnimation_launchTaskBehindTargetAnimation;
+ break;
+ // TODO(b/189386466): Use activity transition as the fallback. Investigate if we
+ // need new TaskFragment transition.
+ case TRANSIT_OLD_TASK_FRAGMENT_OPEN:
+ animAttr = enter
+ ? WindowAnimation_activityOpenEnterAnimation
+ : WindowAnimation_activityOpenExitAnimation;
+ break;
+ // TODO(b/189386466): Use activity transition as the fallback. Investigate if we
+ // need new TaskFragment transition.
+ case TRANSIT_OLD_TASK_FRAGMENT_CLOSE:
+ animAttr = enter
+ ? WindowAnimation_activityCloseEnterAnimation
+ : WindowAnimation_activityCloseExitAnimation;
+ break;
+ case TRANSIT_OLD_DREAM_ACTIVITY_OPEN:
+ animAttr = enter
+ ? WindowAnimation_dreamActivityOpenEnterAnimation
+ : WindowAnimation_dreamActivityOpenExitAnimation;
+ break;
+ case TRANSIT_OLD_DREAM_ACTIVITY_CLOSE:
+ animAttr = enter
+ ? 0
+ : WindowAnimation_dreamActivityCloseExitAnimation;
+ break;
+ }
+
+ return animAttr;
+ }
+
@Nullable
Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
return mTransitionAnimation.loadAnimationAttr(lp, animAttr, transit);
@@ -645,9 +743,15 @@ public class AppTransition implements Dump {
@Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction,
boolean freeform, WindowContainer container) {
- if (mNextAppTransitionOverrideRequested
- && (container.canCustomizeAppTransition() || mOverrideTaskTransition)) {
- mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
+ final boolean canCustomizeAppTransition = container.canCustomizeAppTransition();
+
+ if (mNextAppTransitionOverrideRequested) {
+ if (canCustomizeAppTransition || mOverrideTaskTransition) {
+ mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
+ } else {
+ ProtoLog.e(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: "
+ + " override requested, but it is prohibited by policy.");
+ }
}
Animation a;
@@ -762,87 +866,16 @@ public class AppTransition implements Dump {
"applyAnimation: anim=%s transit=%s isEntrance=%b Callers=%s",
a, appTransitionOldToString(transit), enter, Debug.getCallers(3));
} else {
- int animAttr = 0;
- switch (transit) {
- case TRANSIT_OLD_ACTIVITY_OPEN:
- case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN:
- animAttr = enter
- ? WindowAnimation_activityOpenEnterAnimation
- : WindowAnimation_activityOpenExitAnimation;
- break;
- case TRANSIT_OLD_ACTIVITY_CLOSE:
- case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE:
- animAttr = enter
- ? WindowAnimation_activityCloseEnterAnimation
- : WindowAnimation_activityCloseExitAnimation;
- break;
- case TRANSIT_OLD_TASK_OPEN:
- animAttr = enter
- ? WindowAnimation_taskOpenEnterAnimation
- : WindowAnimation_taskOpenExitAnimation;
- break;
- case TRANSIT_OLD_TASK_CLOSE:
- animAttr = enter
- ? WindowAnimation_taskCloseEnterAnimation
- : WindowAnimation_taskCloseExitAnimation;
- break;
- case TRANSIT_OLD_TASK_TO_FRONT:
- animAttr = enter
- ? WindowAnimation_taskToFrontEnterAnimation
- : WindowAnimation_taskToFrontExitAnimation;
- break;
- case TRANSIT_OLD_TASK_TO_BACK:
- animAttr = enter
- ? WindowAnimation_taskToBackEnterAnimation
- : WindowAnimation_taskToBackExitAnimation;
- break;
- case TRANSIT_OLD_WALLPAPER_OPEN:
- animAttr = enter
- ? WindowAnimation_wallpaperOpenEnterAnimation
- : WindowAnimation_wallpaperOpenExitAnimation;
- break;
- case TRANSIT_OLD_WALLPAPER_CLOSE:
- animAttr = enter
- ? WindowAnimation_wallpaperCloseEnterAnimation
- : WindowAnimation_wallpaperCloseExitAnimation;
- break;
- case TRANSIT_OLD_WALLPAPER_INTRA_OPEN:
- animAttr = enter
- ? WindowAnimation_wallpaperIntraOpenEnterAnimation
- : WindowAnimation_wallpaperIntraOpenExitAnimation;
- break;
- case TRANSIT_OLD_WALLPAPER_INTRA_CLOSE:
- animAttr = enter
- ? WindowAnimation_wallpaperIntraCloseEnterAnimation
- : WindowAnimation_wallpaperIntraCloseExitAnimation;
- break;
- case TRANSIT_OLD_TASK_OPEN_BEHIND:
- animAttr = enter
- ? WindowAnimation_launchTaskBehindSourceAnimation
- : WindowAnimation_launchTaskBehindTargetAnimation;
- break;
- // TODO(b/189386466): Use activity transition as the fallback. Investigate if we
- // need new TaskFragment transition.
- case TRANSIT_OLD_TASK_FRAGMENT_OPEN:
- animAttr = enter
- ? WindowAnimation_activityOpenEnterAnimation
- : WindowAnimation_activityOpenExitAnimation;
- break;
- // TODO(b/189386466): Use activity transition as the fallback. Investigate if we
- // need new TaskFragment transition.
- case TRANSIT_OLD_TASK_FRAGMENT_CLOSE:
- animAttr = enter
- ? WindowAnimation_activityCloseEnterAnimation
- : WindowAnimation_activityCloseExitAnimation;
- break;
- }
- a = animAttr != 0 ? loadAnimationAttr(lp, animAttr, transit) : null;
+ int animAttr = mapOpenCloseTransitTypes(transit, enter);
+ a = animAttr == 0 ? null : (canCustomizeAppTransition
+ ? loadAnimationAttr(lp, animAttr, transit)
+ : mTransitionAnimation.loadDefaultAnimationAttr(animAttr, transit));
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b "
- + "Callers=%s",
+ + " canCustomizeAppTransition=%b Callers=%s",
a, animAttr, appTransitionOldToString(transit), enter,
- Debug.getCallers(3));
+ canCustomizeAppTransition, Debug.getCallers(3));
}
setAppTransitionFinishedCallbackIfNeeded(a);
@@ -988,18 +1021,19 @@ public class AppTransition implements Dump {
}
void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
- overridePendingAppTransitionRemote(remoteAnimationAdapter, false /* sync */);
+ overridePendingAppTransitionRemote(remoteAnimationAdapter, false /* sync */,
+ false /* isActivityEmbedding*/);
}
void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter,
- boolean sync) {
+ boolean sync, boolean isActivityEmbedding) {
ProtoLog.i(WM_DEBUG_APP_TRANSITIONS, "Override pending remote transitionSet=%b adapter=%s",
isTransitionSet(), remoteAnimationAdapter);
if (isTransitionSet() && !mNextAppTransitionIsSync) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
mRemoteAnimationController = new RemoteAnimationController(mService, mDisplayContent,
- remoteAnimationAdapter, mHandler);
+ remoteAnimationAdapter, mHandler, isActivityEmbedding);
mNextAppTransitionIsSync = sync;
}
}
@@ -1157,6 +1191,12 @@ public class AppTransition implements Dump {
case TRANSIT_OLD_TASK_FRAGMENT_CHANGE: {
return "TRANSIT_OLD_TASK_FRAGMENT_CHANGE";
}
+ case TRANSIT_OLD_DREAM_ACTIVITY_OPEN: {
+ return "TRANSIT_OLD_DREAM_ACTIVITY_OPEN";
+ }
+ case TRANSIT_OLD_DREAM_ACTIVITY_CLOSE: {
+ return "TRANSIT_OLD_DREAM_ACTIVITY_CLOSE";
+ }
default: {
return "<UNKNOWN: " + transition + ">";
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index e60ea1260868..fb9d7e602210 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
@@ -32,6 +33,8 @@ import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH;
import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
@@ -339,6 +342,9 @@ public class AppTransitionController {
ArraySet<WindowContainer> changingContainers, @Nullable WindowState wallpaperTarget,
@Nullable WindowState oldWallpaper, boolean skipAppTransitionAnimation) {
+ final ActivityRecord topOpeningApp = getTopApp(openingApps, false /* ignoreHidden */);
+ final ActivityRecord topClosingApp = getTopApp(closingApps, true /* ignoreHidden */);
+
// Determine if closing and opening app token sets are wallpaper targets, in which case
// special animations are needed.
final boolean openingAppHasWallpaper = canBeWallpaperTarget(openingApps)
@@ -346,7 +352,7 @@ public class AppTransitionController {
final boolean closingAppHasWallpaper = canBeWallpaperTarget(closingApps)
&& wallpaperTarget != null;
- // Keyguard transit has highest priority.
+ // Keyguard transit has high priority.
switch (appTransition.getKeyguardTransition()) {
case TRANSIT_KEYGUARD_GOING_AWAY:
return openingAppHasWallpaper ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER
@@ -361,6 +367,15 @@ public class AppTransitionController {
return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
}
+ // Determine whether the top opening and closing activity is a dream activity. If so, this
+ // has higher priority than others except keyguard transit.
+ if (topOpeningApp != null && topOpeningApp.getActivityType() == ACTIVITY_TYPE_DREAM) {
+ return TRANSIT_OLD_DREAM_ACTIVITY_OPEN;
+ } else if (topClosingApp != null
+ && topClosingApp.getActivityType() == ACTIVITY_TYPE_DREAM) {
+ return TRANSIT_OLD_DREAM_ACTIVITY_CLOSE;
+ }
+
// This is not keyguard transition and one of the app has request to skip app transition.
if (skipAppTransitionAnimation) {
return WindowManager.TRANSIT_OLD_UNSET;
@@ -424,11 +439,6 @@ public class AppTransitionController {
}
}
- final ActivityRecord topOpeningApp = getTopApp(openingApps,
- false /* ignoreHidden */);
- final ActivityRecord topClosingApp = getTopApp(closingApps,
- true /* ignoreHidden */);
-
if (closingAppHasWallpaper && openingAppHasWallpaper) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Wallpaper animation!");
switch (firstTransit) {
@@ -682,7 +692,8 @@ public class AppTransitionController {
if (adapter == null) {
return false;
}
- mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(adapter);
+ mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(
+ adapter, false /* sync */, true /*isActivityEmbedding*/);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Override with TaskFragment remote animation for transit=%s",
AppTransition.appTransitionOldToString(transit));
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 61deb59084c5..0af046281cc5 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -18,7 +18,6 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFORM;
@@ -121,7 +120,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
} else {
mTransitionOp = OP_CHANGE;
}
- } else if (transitionType != WindowManager.TRANSIT_NONE) {
+ } else if (displayContent.mTransitionController.isShellTransitionsEnabled()) {
mTransitionOp = OP_APP_SWITCH;
} else {
mTransitionOp = OP_LEGACY;
@@ -144,8 +143,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
// Legacy animation doesn't need to wait for the start transaction.
if (mTransitionOp == OP_LEGACY) {
mIsStartTransactionCommitted = true;
- } else if (displayContent.mTransitionController.useShellTransitionsRotation()
- || displayContent.mTransitionController.isCollecting(displayContent)) {
+ } else if (displayContent.mTransitionController.isCollecting(displayContent)) {
keepAppearanceInPreviousRotation();
}
}
@@ -153,8 +151,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
/** Assigns the operation for the window tokens which can update rotation asynchronously. */
@Override
public void accept(WindowState w) {
- if (w.mActivityRecord != null || !w.mHasSurface || w.mIsWallpaper || w.mIsImWindow
- || w.mAttrs.type == TYPE_NOTIFICATION_SHADE) {
+ if (!w.mHasSurface || !canBeAsync(w.mToken)) {
return;
}
if (mTransitionOp == OP_LEGACY && w.mForceSeamlesslyRotate) {
@@ -186,23 +183,28 @@ class AsyncRotationController extends FadeAnimationController implements Consume
mTargetWindowTokens.put(w.mToken, new Operation(action));
}
+ /** Returns {@code true} if the window token can update rotation independently. */
+ static boolean canBeAsync(WindowToken token) {
+ final int type = token.windowType;
+ return type > WindowManager.LayoutParams.LAST_APPLICATION_WINDOW
+ && type != WindowManager.LayoutParams.TYPE_INPUT_METHOD
+ && type != WindowManager.LayoutParams.TYPE_WALLPAPER
+ && type != WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
+ }
+
/**
* Enables {@link #handleFinishDrawing(WindowState, SurfaceControl.Transaction)} to capture the
* draw transactions of the target windows if needed.
*/
void keepAppearanceInPreviousRotation() {
+ if (mIsSyncDrawRequested) return;
// The transition sync group may be finished earlier because it doesn't wait for these
// target windows. But the windows still need to use sync transaction to keep the appearance
// in previous rotation, so request a no-op sync to keep the state.
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
- if (mHasScreenRotationAnimation
- && mTargetWindowTokens.valueAt(i).mAction == Operation.ACTION_FADE) {
- // The windows are hidden (leash is alpha 0) before finishing drawing so it is
- // unnecessary to request sync.
- continue;
- }
final WindowToken token = mTargetWindowTokens.keyAt(i);
for (int j = token.getChildCount() - 1; j >= 0; j--) {
+ // TODO(b/234585256): The consumer should be handleFinishDrawing().
token.getChildAt(j).applyWithNextDraw(t -> {});
}
}
@@ -214,10 +216,10 @@ class AsyncRotationController extends FadeAnimationController implements Consume
private void finishOp(WindowToken windowToken) {
final Operation op = mTargetWindowTokens.remove(windowToken);
if (op == null) return;
- if (op.mCapturedDrawTransaction != null) {
+ if (op.mDrawTransaction != null) {
// Unblock the window to show its latest content.
- mDisplayContent.getPendingTransaction().merge(op.mCapturedDrawTransaction);
- op.mCapturedDrawTransaction = null;
+ mDisplayContent.getPendingTransaction().merge(op.mDrawTransaction);
+ op.mDrawTransaction = null;
if (DEBUG) Slog.d(TAG, "finishOp merge transaction " + windowToken.getTopChild());
}
if (op.mAction == Operation.ACTION_FADE) {
@@ -351,14 +353,34 @@ class AsyncRotationController extends FadeAnimationController implements Consume
}
/**
- * Whether the insets animation leash should use previous position when running fade out
- * animation in rotated display.
+ * Whether the insets animation leash should use previous position when running fade animation
+ * or seamless transformation in a rotated display.
*/
boolean shouldFreezeInsetsPosition(WindowState w) {
- return mTransitionOp == OP_APP_SWITCH && w.mTransitionController.inTransition()
+ return mTransitionOp != OP_LEGACY && w.mTransitionController.inTransition()
&& isTargetToken(w.mToken);
}
+ /**
+ * Returns the transaction which will be applied after the window redraws in new rotation.
+ * This is used to update the position of insets animation leash synchronously.
+ */
+ SurfaceControl.Transaction getDrawTransaction(WindowToken token) {
+ if (mTransitionOp == OP_LEGACY) {
+ // Legacy transition uses startSeamlessRotation and finishSeamlessRotation of
+ // InsetsSourceProvider.
+ return null;
+ }
+ final Operation op = mTargetWindowTokens.get(token);
+ if (op != null) {
+ if (op.mDrawTransaction == null) {
+ op.mDrawTransaction = new SurfaceControl.Transaction();
+ }
+ return op.mDrawTransaction;
+ }
+ return null;
+ }
+
void setOnShowRunnable(Runnable onShowRunnable) {
mOnShowRunnable = onShowRunnable;
}
@@ -368,6 +390,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
* transition starts. And associate transaction callback to consume pending animations.
*/
void setupStartTransaction(SurfaceControl.Transaction t) {
+ if (mIsStartTransactionCommitted) return;
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
final Operation op = mTargetWindowTokens.valueAt(i);
final SurfaceControl leash = op.mLeash;
@@ -395,7 +418,6 @@ class AsyncRotationController extends FadeAnimationController implements Consume
}
}
- if (mIsStartTransactionCommitted) return;
// If there are windows have redrawn in new rotation but the start transaction has not
// been applied yet, the fade-in animation will be deferred. So once the transaction is
// committed, the fade-in animation can run with screen rotation animation.
@@ -452,23 +474,18 @@ class AsyncRotationController extends FadeAnimationController implements Consume
* by this controller.
*/
boolean handleFinishDrawing(WindowState w, SurfaceControl.Transaction postDrawTransaction) {
- if (mTransitionOp == OP_LEGACY || postDrawTransaction == null
- || !mIsSyncDrawRequested || !w.mTransitionController.inTransition()) {
+ if (mTransitionOp == OP_LEGACY || postDrawTransaction == null || !mIsSyncDrawRequested) {
return false;
}
final Operation op = mTargetWindowTokens.get(w.mToken);
if (op == null) return false;
- final boolean keepUntilTransitionFinish =
- mTransitionOp == OP_APP_SWITCH && op.mAction == Operation.ACTION_FADE;
- final boolean keepUntilStartTransaction =
- !mIsStartTransactionCommitted && op.mAction == Operation.ACTION_SEAMLESS;
- if (!keepUntilTransitionFinish && !keepUntilStartTransaction) return false;
- if (op.mCapturedDrawTransaction == null) {
- op.mCapturedDrawTransaction = postDrawTransaction;
+ if (DEBUG) Slog.d(TAG, "handleFinishDrawing " + w);
+ if (op.mDrawTransaction == null) {
+ op.mDrawTransaction = postDrawTransaction;
} else {
- op.mCapturedDrawTransaction.merge(postDrawTransaction);
+ op.mDrawTransaction.merge(postDrawTransaction);
}
- if (DEBUG) Slog.d(TAG, "Capture draw transaction " + w);
+ mDisplayContent.finishAsyncRotation(w.mToken);
return true;
}
@@ -512,7 +529,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
* the start transaction of transition, so there won't be a flickering such as the window
* has redrawn during fading out.
*/
- SurfaceControl.Transaction mCapturedDrawTransaction;
+ SurfaceControl.Transaction mDrawTransaction;
Operation(@Action int action) {
mAction = action;
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 46ce43335f01..9a94a4f54b61 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -236,12 +236,18 @@ class BLASTSyncEngine {
private void onTimeout() {
if (!mActiveSyncs.contains(mSyncId)) return;
+ boolean allFinished = true;
for (int i = mRootMembers.size() - 1; i >= 0; --i) {
final WindowContainer<?> wc = mRootMembers.valueAt(i);
if (!wc.isSyncFinished()) {
+ allFinished = false;
Slog.i(TAG, "Unfinished container: " + wc);
}
}
+ if (allFinished && !mReady) {
+ Slog.w(TAG, "Sync group " + mSyncId + " timed-out because not ready. If you see "
+ + "this, please file a bug.");
+ }
finishNow();
}
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index d07cc68af890..9295c18fe80e 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -112,6 +112,7 @@ class BackNavigationController {
RemoteAnimationTarget topAppTarget = null;
int prevTaskId;
int prevUserId;
+ boolean prepareAnimation;
BackNavigationInfo.Builder infoBuilder = new BackNavigationInfo.Builder();
synchronized (wmService.mGlobalLock) {
@@ -257,7 +258,8 @@ class BackNavigationController {
BackNavigationInfo.typeToString(backType));
// For now, we only animate when going home.
- boolean prepareAnimation = backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
+ prepareAnimation = backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
+ && requestAnimation
// Only create a new leash if no leash has been created.
// Otherwise return null for animation target to avoid conflict.
&& !removedWindowContainer.hasCommittedReparentToAnimationLeash();
@@ -292,7 +294,7 @@ class BackNavigationController {
}
// Special handling for back to home animation
- if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && requestAnimation
+ if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && prepareAnimation
&& prevTask != null) {
currentTask.mBackGestureStarted = true;
// Make launcher show from behind by marking its top activity as visible and
@@ -347,7 +349,7 @@ class BackNavigationController {
Task finalTask = currentTask;
RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone(
result, finalRemovedWindowContainer, finalBackType, finalTask,
- finalprevActivity, requestAnimation));
+ finalprevActivity, prepareAnimation));
infoBuilder.setOnBackNavigationDone(onBackNavigationDone);
}
@@ -381,14 +383,14 @@ class BackNavigationController {
private void onBackNavigationDone(
Bundle result, WindowContainer<?> windowContainer, int backType,
- Task task, ActivityRecord prevActivity, boolean requestAnimation) {
+ Task task, ActivityRecord prevActivity, boolean prepareAnimation) {
SurfaceControl surfaceControl = windowContainer.getSurfaceControl();
boolean triggerBack = result != null && result.getBoolean(
BackNavigationInfo.KEY_TRIGGER_BACK);
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, "
+ "task=%s, prevActivity=%s", backType, task, prevActivity);
- if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && requestAnimation) {
+ if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && prepareAnimation) {
if (triggerBack) {
if (surfaceControl != null && surfaceControl.isValid()) {
// When going back to home, hide the task surface before it is re-parented to
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index fcdf175cb809..0c6cea82e00c 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -486,14 +486,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
return WindowConfiguration.inMultiWindowMode(windowingMode);
}
- /**
- * Returns true if this container supports split-screen multi-window and can be put in
- * split-screen based on its current state.
- */
- public boolean supportsSplitScreenWindowingMode() {
- return mFullConfiguration.windowConfiguration.supportSplitScreenWindowingMode();
- }
-
public boolean inPinnedWindowingMode() {
return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
}
diff --git a/services/core/java/com/android/server/wm/ContentRecordingController.java b/services/core/java/com/android/server/wm/ContentRecordingController.java
index fca4942d4b79..fff7637acc7e 100644
--- a/services/core/java/com/android/server/wm/ContentRecordingController.java
+++ b/services/core/java/com/android/server/wm/ContentRecordingController.java
@@ -63,6 +63,7 @@ final class ContentRecordingController {
*/
void setContentRecordingSessionLocked(@Nullable ContentRecordingSession incomingSession,
@NonNull WindowManagerService wmService) {
+ // TODO(b/219761722) handle a null session arriving due to task setup failing
if (incomingSession != null && (!ContentRecordingSession.isValid(incomingSession)
|| ContentRecordingSession.isSameDisplay(mSession, incomingSession))) {
// Ignore an invalid session, or a session for the same display as currently recording.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 72b996c4662d..90b77f7336c0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -67,6 +67,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -130,7 +131,6 @@ import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.DisplayContentProto.SLEEP_TOKENS;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -162,6 +162,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.ScreenOrientation;
@@ -558,7 +559,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final FixedRotationTransitionListener mFixedRotationTransitionListener =
new FixedRotationTransitionListener();
- private PhysicalDisplaySwitchTransitionLauncher mDisplaySwitchTransitionLauncher;
+ private final PhysicalDisplaySwitchTransitionLauncher mDisplaySwitchTransitionLauncher;
+ final RemoteDisplayChangeController mRemoteDisplayChangeController;
/** Windows added since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
final ArrayList<WindowState> mWinAddedSinceNullFocus = new ArrayList<>();
@@ -756,8 +758,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
if (w.mAttrs.type == TYPE_INPUT_METHOD_DIALOG && mImeLayeringTarget != null
&& !mImeLayeringTarget.getRequestedVisibility(ITYPE_IME)
- && mImeLayeringTarget.isAnimating(PARENTS | TRANSITION,
- ANIMATION_TYPE_APP_TRANSITION)) {
+ && !mImeLayeringTarget.isVisibleRequested()) {
return false;
}
@@ -1054,6 +1055,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
mDisplaySwitchTransitionLauncher = new PhysicalDisplaySwitchTransitionLauncher(this,
mTransitionController);
+ mRemoteDisplayChangeController = new RemoteDisplayChangeController(mWmService, mDisplayId);
final InputChannel inputChannel = mWmService.mInputManager.monitorInput(
"PointerEventDispatcher" + mDisplayId, mDisplayId);
@@ -1362,7 +1364,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void setInsetProvider(@InternalInsetsType int type, WindowContainer win,
@Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider) {
- setInsetProvider(type, win, frameProvider, null /* imeFrameProvider */);
+ setInsetProvider(type, win, frameProvider, null /* overrideFrameProviders */);
}
/**
@@ -1371,15 +1373,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* @param type The type of inset this window provides.
* @param win The window.
* @param frameProvider Function to compute the frame, or {@code null} if the just the frame of
- * the window should be taken.
- * @param imeFrameProvider Function to compute the frame when dispatching insets to the IME, or
- * {@code null} if the normal frame should be taken.
+ * the window should be taken. Only for non-WindowState providers, nav bar
+ * and status bar.
+ * @param overrideFrameProviders Functions to compute the frame when dispatching insets to the
+ * given window types, or {@code null} if the normal frame should
+ * be taken.
*/
void setInsetProvider(@InternalInsetsType int type, WindowContainer win,
@Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider,
- @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider) {
+ @Nullable SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>>
+ overrideFrameProviders) {
mInsetsStateController.getSourceProvider(type).setWindowContainer(win, frameProvider,
- imeFrameProvider);
+ overrideFrameProviders);
}
InsetsStateController getInsetsStateController() {
@@ -1435,7 +1440,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (!isReady()) {
return;
}
- if (mDisplayRotation.isWaitingForRemoteRotation()) {
+ if (mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange()) {
return;
}
@@ -1535,8 +1540,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
config = new Configuration();
computeScreenConfiguration(config);
} else if (!(mTransitionController.isCollecting(this)
- // If waiting for a remote rotation, don't prematurely update configuration.
- || mDisplayRotation.isWaitingForRemoteRotation())) {
+ // If waiting for a remote display change, don't prematurely update configuration.
+ || mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange())) {
// No obvious action we need to take, but if our current state mismatches the
// activity manager's, update it, disregarding font scale, which should remain set
// to the value of the previous configuration.
@@ -1583,8 +1588,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mAtmService.getTaskChangeNotificationController()
.notifyTaskRequestedOrientationChanged(task.mTaskId, orientation);
}
- // Currently there is no use case from non-activity.
- if (handleTopActivityLaunchingInDifferentOrientation(r, true /* checkOpening */)) {
+ // The orientation source may not be the top if it uses SCREEN_ORIENTATION_BEHIND.
+ final ActivityRecord topCandidate = !r.mVisibleRequested ? topRunningActivity() : r;
+ if (handleTopActivityLaunchingInDifferentOrientation(
+ topCandidate, r, true /* checkOpening */)) {
// Display orientation should be deferred until the top fixed rotation is finished.
return false;
}
@@ -1596,21 +1603,31 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
boolean isSyncFinished() {
// Do not consider children because if they are requested to be synced, they should be
// added to sync group explicitly.
- return !mDisplayRotation.isWaitingForRemoteRotation();
+ return !mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange();
}
/**
* Returns a valid rotation if the activity can use different orientation than the display.
- * Otherwise {@link #ROTATION_UNDEFINED}.
+ * Otherwise {@link android.app.WindowConfiguration#ROTATION_UNDEFINED}.
*/
@Rotation
int rotationForActivityInDifferentOrientation(@NonNull ActivityRecord r) {
if (mTransitionController.useShellTransitionsRotation()) {
return ROTATION_UNDEFINED;
}
- if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM) {
+ if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM
+ || getIgnoreOrientationRequest()) {
return ROTATION_UNDEFINED;
}
+ if (r.mOrientation == ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+ final ActivityRecord nextCandidate = getActivity(
+ a -> a.mOrientation != SCREEN_ORIENTATION_UNSET
+ && a.mOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND,
+ r, false /* includeBoundary */, true /* traverseTopToBottom */);
+ if (nextCandidate != null) {
+ r = nextCandidate;
+ }
+ }
if (r.inMultiWindowMode() || r.getRequestedConfigurationOrientation(true /* forDisplay */)
== getConfiguration().orientation) {
return ROTATION_UNDEFINED;
@@ -1624,18 +1641,25 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return rotation;
}
+ boolean handleTopActivityLaunchingInDifferentOrientation(@NonNull ActivityRecord r,
+ boolean checkOpening) {
+ return handleTopActivityLaunchingInDifferentOrientation(r, r, checkOpening);
+ }
+
/**
* We need to keep display rotation fixed for a while when the activity in different orientation
* is launching until the launch animation is done to avoid showing the previous activity
* inadvertently in a wrong orientation.
*
* @param r The launching activity which may change display orientation.
+ * @param orientationSrc It may be different from {@param r} if the launching activity uses
+ * "behind" orientation.
* @param checkOpening Whether to check if the activity is animating by transition. Set to
* {@code true} if the caller is not sure whether the activity is launching.
* @return {@code true} if the fixed rotation is started.
*/
- boolean handleTopActivityLaunchingInDifferentOrientation(@NonNull ActivityRecord r,
- boolean checkOpening) {
+ private boolean handleTopActivityLaunchingInDifferentOrientation(@NonNull ActivityRecord r,
+ @NonNull ActivityRecord orientationSrc, boolean checkOpening) {
if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM) {
return false;
}
@@ -1665,7 +1689,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return false;
}
}
- if (r.isState(RESUMED) && !r.getRootTask().mInResumeTopActivity) {
+ if (r.isState(RESUMED) && !r.getTask().mInResumeTopActivity) {
// If the activity is executing or has done the lifecycle callback, use normal
// rotation animation so the display info can be updated immediately (see
// updateDisplayAndOrientation). This prevents a compatibility issue such as
@@ -1684,7 +1708,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// animation is not running (it may be swiping to home).
return false;
}
- final int rotation = rotationForActivityInDifferentOrientation(r);
+ final int rotation = rotationForActivityInDifferentOrientation(orientationSrc);
if (rotation == ROTATION_UNDEFINED) {
// The display rotation won't be changed by current top activity. The client side
// adjustments of previous rotated activity should be cleared earlier. Otherwise if
@@ -1762,7 +1786,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* rotation transform to it and indicate that the display may be rotated after it is launched.
*/
void setFixedRotationLaunchingApp(@NonNull ActivityRecord r, @Rotation int rotation) {
- final WindowToken prevRotatedLaunchingApp = mFixedRotationLaunchingApp;
+ final ActivityRecord prevRotatedLaunchingApp = mFixedRotationLaunchingApp;
if (prevRotatedLaunchingApp == r
&& r.getWindowConfiguration().getRotation() == rotation) {
// The given launching app and target rotation are the same as the existing ones.
@@ -1771,8 +1795,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (prevRotatedLaunchingApp != null
&& prevRotatedLaunchingApp.getWindowConfiguration().getRotation() == rotation
// It is animating so we can expect there will have a transition callback.
- && (prevRotatedLaunchingApp.isAnimating(TRANSITION | PARENTS)
- || mTransitionController.inTransition(prevRotatedLaunchingApp))) {
+ && (prevRotatedLaunchingApp.isInTransition())) {
// It may be the case that multiple activities launch consecutively. Because their
// rotation are the same, the transformed state can be shared to avoid duplicating
// the heavy operations. This also benefits that the states of multiple activities
@@ -1813,8 +1836,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
sendNewConfiguration();
return;
}
- if (mDisplayRotation.isWaitingForRemoteRotation()) {
- // There is pending rotation change to apply.
+ if (mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange()) {
+ // There is pending display change to apply.
return;
}
// The orientation of display is not changed.
@@ -1862,7 +1885,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
/** Returns {@code true} if the decided new rotation has not applied to configuration yet. */
- private boolean isRotationChanging() {
+ boolean isRotationChanging() {
return mDisplayRotation.getRotation() != getWindowConfiguration().getRotation();
}
@@ -2015,7 +2038,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
ProtoLog.v(WM_DEBUG_ORIENTATION, "Set mOrientationChanging of %s", w);
w.setOrientationChanging(true);
}
- w.mReportOrientationChanged = true;
}, true /* traverseTopToBottom */);
for (int i = mWmService.mRotationWatchers.size() - 1; i >= 0; i--) {
@@ -2205,9 +2227,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final float density = mDisplayMetrics.density;
outConfig.screenWidthDp = (int) (mDisplayPolicy.getConfigDisplayWidth(dw, dh, rotation,
- uiMode, displayCutout) / density);
+ uiMode, displayCutout) / density + 0.5f);
outConfig.screenHeightDp = (int) (mDisplayPolicy.getConfigDisplayHeight(dw, dh, rotation,
- uiMode, displayCutout) / density);
+ uiMode, displayCutout) / density + 0.5f);
outConfig.compatScreenWidthDp = (int) (outConfig.screenWidthDp / mCompatibleScreenScale);
outConfig.compatScreenHeightDp = (int) (outConfig.screenHeightDp / mCompatibleScreenScale);
@@ -2390,7 +2412,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode);
sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode);
sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode);
- outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
+ outConfig.smallestScreenWidthDp =
+ (int) (displayInfo.smallestNominalAppWidth / density + 0.5f);
outConfig.screenLayout = sl;
}
@@ -2412,8 +2435,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
longSize = shortSize;
shortSize = tmp;
}
- longSize = (int)(longSize/density);
- shortSize = (int)(shortSize/density);
+ longSize = (int) (longSize / density + 0.5f);
+ shortSize = (int) (shortSize / density + 0.5f);
return Configuration.reduceScreenLayout(curLayout, longSize, shortSize);
}
@@ -2758,6 +2781,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
private void updateBaseDisplayMetricsIfNeeded() {
// Get real display metrics without overrides from WM.
mWmService.mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mDisplayInfo);
+ final int currentRotation = getRotation();
final int orientation = mDisplayInfo.rotation;
final boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270);
final int newWidth = rotated ? mDisplayInfo.logicalHeight : mDisplayInfo.logicalWidth;
@@ -2818,7 +2842,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
reconfigureDisplayLocked();
if (physicalDisplayChanged) {
- mDisplaySwitchTransitionLauncher.onDisplayUpdated();
+ mDisplaySwitchTransitionLauncher.onDisplayUpdated(currentRotation, getRotation(),
+ getDisplayAreaInfo());
}
}
}
@@ -2922,9 +2947,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Set some sort of reasonable bounds on the size of the display that we will try
// to emulate.
final int minSize = 200;
- final int maxScale = 2;
- width = Math.min(Math.max(width, minSize), mInitialDisplayWidth * maxScale);
- height = Math.min(Math.max(height, minSize), mInitialDisplayHeight * maxScale);
+ final int maxScale = 3;
+ final int maxSize = Math.max(mInitialDisplayWidth, mInitialDisplayHeight) * maxScale;
+ width = Math.min(Math.max(width, minSize), maxSize);
+ height = Math.min(Math.max(height, minSize), maxSize);
}
Slog.i(TAG_WM, "Using new display size: " + width + "x" + height);
@@ -3844,7 +3870,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mAtmService.onImeWindowSetOnDisplayArea(imePid, mImeWindowsContainer);
}
mInsetsStateController.getSourceProvider(ITYPE_IME).setWindowContainer(win,
- mDisplayPolicy.getImeSourceFrameProvider(), null /* imeFrameProvider */);
+ mDisplayPolicy.getImeSourceFrameProvider(), null);
computeImeTarget(true /* updateImeTarget */);
updateImeControlTarget();
}
@@ -4385,8 +4411,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
boolean imeLayeringTargetMayUseIme =
LayoutParams.mayUseInputMethod(mImeLayeringTarget.mAttrs.flags)
|| mImeLayeringTarget.mAttrs.type == TYPE_APPLICATION_STARTING;
- if (imeLayeringTargetMayUseIme && mImeInputTarget != null
- && mImeLayeringTarget.mActivityRecord != mImeInputTarget.getActivityRecord()) {
+ if (imeLayeringTargetMayUseIme && (mImeInputTarget == null
+ || mImeLayeringTarget.mActivityRecord != mImeInputTarget.getActivityRecord())) {
// Do not change parent if the window hasn't requested IME.
return null;
}
@@ -4858,9 +4884,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return true;
}
- // TODO(b/165794880): Freeform task organizer doesn't support drag-resize yet. Remove
- // the special case when it does.
- if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+ if (task.isOrganized()) {
return true;
}
@@ -5551,11 +5575,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
private static boolean needsGestureExclusionRestrictions(WindowState win,
boolean ignoreRequest) {
final int type = win.mAttrs.type;
+ final int privateFlags = win.mAttrs.privateFlags;
final boolean stickyHideNav =
!win.getRequestedVisibility(ITYPE_NAVIGATION_BAR)
&& win.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
return (!stickyHideNav || ignoreRequest) && type != TYPE_INPUT_METHOD
- && type != TYPE_NOTIFICATION_SHADE && win.getActivityType() != ACTIVITY_TYPE_HOME;
+ && type != TYPE_NOTIFICATION_SHADE && win.getActivityType() != ACTIVITY_TYPE_HOME
+ && (privateFlags & PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION) == 0;
}
/**
@@ -5667,7 +5693,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void getKeepClearAreas(Set<Rect> outRestricted, Set<Rect> outUnrestricted) {
final Matrix tmpMatrix = new Matrix();
final float[] tmpFloat9 = new float[9];
+ final RecentsAnimationController recentsAnimationController =
+ mWmService.getRecentsAnimationController();
forAllWindows(w -> {
+ // Skip the window if it is part of Recents animation
+ final boolean ignoreRecentsAnimationTarget = recentsAnimationController != null
+ && recentsAnimationController.shouldApplyInputConsumer(w.getActivityRecord());
+ if (ignoreRecentsAnimationTarget) {
+ return false; // continue traversal
+ }
+
if (w.isVisible() && !w.inPinnedWindowingMode()) {
w.getKeepClearAreas(outRestricted, outUnrestricted, tmpMatrix, tmpFloat9);
}
@@ -6091,7 +6126,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
rootTask.ensureActivitiesVisible(starting, configChanges, preserveWindows,
notifyClients);
});
- if (mTransitionController.isCollecting()
+ if (mTransitionController.useShellTransitionsRotation()
+ && mTransitionController.isCollecting()
&& mWallpaperController.getWallpaperTarget() != null) {
// Also update wallpapers so that their requestedVisibility immediately reflects
// the changes to activity visibility.
@@ -6268,7 +6304,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/**
* Sets if Display APIs should be sandboxed to the activity window bounds.
*/
- @VisibleForTesting
void setSandboxDisplayApis(boolean sandboxDisplayApis) {
mSandboxDisplayApis = sandboxDisplayApis;
}
@@ -6456,7 +6491,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Different tasks won't be in one activity transition animation.
return;
}
- if (task.isAppTransitioning()) {
+ if (task.getActivity(ActivityRecord::isInTransition) != null) {
return;
// Continue to update orientation because the transition of the top rotated
// launching activity is done.
@@ -6489,12 +6524,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/**
* Notifies the remote insets controller that the top focused window has changed.
*
- * @param packageName The name of the package that is open in the top focused window.
+ * @param component The application component that is open in the top focussed window.
* @param requestedVisibilities The insets visibilities requested by the focussed window.
*/
- void topFocusedWindowChanged(String packageName, InsetsVisibilities requestedVisibilities) {
+ void topFocusedWindowChanged(ComponentName component,
+ InsetsVisibilities requestedVisibilities) {
try {
- mRemoteInsetsController.topFocusedWindowChanged(packageName, requestedVisibilities);
+ mRemoteInsetsController.topFocusedWindowChanged(component, requestedVisibilities);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to deliver package in top focused window change", e);
}
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index fd0631320520..7ca38b8fcc00 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -99,19 +99,28 @@ public class DisplayFrames {
state.setRoundedCorners(roundedCorners);
state.setPrivacyIndicatorBounds(indicatorBounds);
state.getDisplayCutoutSafe(safe);
- if (!cutout.isEmpty()) {
+ if (safe.left > unrestricted.left) {
state.getSource(ITYPE_LEFT_DISPLAY_CUTOUT).setFrame(
unrestricted.left, unrestricted.top, safe.left, unrestricted.bottom);
+ } else {
+ state.removeSource(ITYPE_LEFT_DISPLAY_CUTOUT);
+ }
+ if (safe.top > unrestricted.top) {
state.getSource(ITYPE_TOP_DISPLAY_CUTOUT).setFrame(
unrestricted.left, unrestricted.top, unrestricted.right, safe.top);
+ } else {
+ state.removeSource(ITYPE_TOP_DISPLAY_CUTOUT);
+ }
+ if (safe.right < unrestricted.right) {
state.getSource(ITYPE_RIGHT_DISPLAY_CUTOUT).setFrame(
safe.right, unrestricted.top, unrestricted.right, unrestricted.bottom);
+ } else {
+ state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT);
+ }
+ if (safe.bottom < unrestricted.bottom) {
state.getSource(ITYPE_BOTTOM_DISPLAY_CUTOUT).setFrame(
unrestricted.left, safe.bottom, unrestricted.right, unrestricted.bottom);
} else {
- state.removeSource(ITYPE_LEFT_DISPLAY_CUTOUT);
- state.removeSource(ITYPE_TOP_DISPLAY_CUTOUT);
- state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT);
state.removeSource(ITYPE_BOTTOM_DISPLAY_CUTOUT);
}
return true;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 7e91989a9105..4a7a8bd99419 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -47,7 +47,9 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
@@ -61,6 +63,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_WAKE;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED;
import static android.view.WindowManagerPolicyConstants.ALT_BAR_BOTTOM;
@@ -119,6 +122,7 @@ import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.InsetsFlags;
+import android.view.InsetsFrameProvider;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
@@ -143,6 +147,7 @@ import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.policy.SystemBarUtils;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.util.function.TriConsumer;
import com.android.internal.view.AppearanceRegion;
@@ -778,7 +783,23 @@ public class DisplayPolicy {
}
public void setAwake(boolean awake) {
+ if (awake == mAwake) {
+ return;
+ }
mAwake = awake;
+ synchronized (mService.mGlobalLock) {
+ if (!mDisplayContent.isDefaultDisplay) {
+ return;
+ }
+ if (mAwake && mDisplayContent.mTransitionController.isShellTransitionsEnabled()
+ && !mDisplayContent.mTransitionController.isCollecting()) {
+ // Start a transition for waking. This is needed for showWhenLocked activities.
+ mDisplayContent.mTransitionController.requestTransitionIfNeeded(TRANSIT_WAKE,
+ 0 /* flags */, null /* trigger */, mDisplayContent);
+ }
+ mService.mAtmService.mKeyguardController.updateDeferWakeTransition(
+ mAwake /* waiting */);
+ }
}
public boolean isAwake() {
@@ -954,6 +975,10 @@ public class DisplayPolicy {
}
}
+ if (!win.mSession.mCanSetUnrestrictedGestureExclusion) {
+ attrs.privateFlags &= ~PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION;
+ }
+
// Check if alternate bars positions were updated.
if (mStatusBarAlt == win) {
mStatusBarAltPosition = getAltBarPosition(attrs);
@@ -1062,17 +1087,18 @@ public class DisplayPolicy {
return WindowManagerGlobal.ADD_INVALID_TYPE;
}
- if (attrs.providesInsetsTypes != null) {
+ if (attrs.providedInsets != null) {
// Recents component is allowed to add inset types.
if (!mService.mAtmService.isCallerRecents(callingUid)) {
mContext.enforcePermission(
android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
"DisplayPolicy");
}
- enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providesInsetsTypes);
+ enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providedInsets);
- for (@InternalInsetsType int insetType : attrs.providesInsetsTypes) {
- switch (insetType) {
+ for (InsetsFrameProvider provider : attrs.providedInsets) {
+ @InternalInsetsType int insetsType = provider.type;
+ switch (insetsType) {
case ITYPE_STATUS_BAR:
if ((mStatusBar != null && mStatusBar.isAlive())
|| (mStatusBarAlt != null && mStatusBarAlt.isAlive())) {
@@ -1135,23 +1161,32 @@ public class DisplayPolicy {
break;
case TYPE_NAVIGATION_BAR:
mNavigationBar = win;
- mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
+ final TriConsumer<DisplayFrames, WindowContainer, Rect> navFrameProvider =
(displayFrames, windowContainer, inOutFrame) -> {
if (!mNavButtonForcedVisible) {
- final Insets[] providedInternalInsets = win.getLayoutingAttrs(
- displayFrames.mRotation).providedInternalInsets;
- if (providedInternalInsets != null
- && providedInternalInsets.length > ITYPE_NAVIGATION_BAR
- && providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) {
- inOutFrame.inset(providedInternalInsets[ITYPE_NAVIGATION_BAR]);
+ final LayoutParams lp =
+ win.mAttrs.forRotation(displayFrames.mRotation);
+ if (lp.providedInsets != null) {
+ for (InsetsFrameProvider provider : lp.providedInsets) {
+ if (provider.type != ITYPE_NAVIGATION_BAR) {
+ continue;
+ }
+ InsetsFrameProvider.calculateInsetsFrame(
+ displayFrames.mUnrestricted,
+ win.getBounds(), displayFrames.mDisplayCutoutSafe,
+ inOutFrame, provider.source,
+ provider.insetsSize, lp.privateFlags);
+ }
}
inOutFrame.inset(win.mGivenContentInsets);
}
- },
-
- (displayFrames, windowContainer, inOutFrame) -> {
- // For IME, we don't modify the frame.
- });
+ };
+ final SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>> imeOverride =
+ new SparseArray<>();
+ // For IME, we don't modify the frame.
+ imeOverride.put(TYPE_INPUT_METHOD, null);
+ mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
+ navFrameProvider, imeOverride);
mDisplayContent.setInsetProvider(ITYPE_BOTTOM_MANDATORY_GESTURES, win,
(displayFrames, windowContainer, inOutFrame) -> {
@@ -1187,21 +1222,10 @@ public class DisplayPolicy {
if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
break;
default:
- if (attrs.providesInsetsTypes != null) {
- for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
- final TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider =
- win.getAttrs().providedInternalImeInsets != null
- ? (displayFrames, windowContainer, inOutFrame) -> {
- final Insets[] providedInternalImeInsets =
- win.getLayoutingAttrs(displayFrames.mRotation)
- .providedInternalImeInsets;
- if (providedInternalImeInsets != null
- && providedInternalImeInsets.length > insetsType
- && providedInternalImeInsets[insetsType] != null) {
- inOutFrame.inset(providedInternalImeInsets[insetsType]);
- }
- } : null;
- switch (insetsType) {
+ if (attrs.providedInsets != null) {
+ for (int i = attrs.providedInsets.length - 1; i >= 0; i--) {
+ final InsetsFrameProvider provider = attrs.providedInsets[i];
+ switch (provider.type) {
case ITYPE_STATUS_BAR:
mStatusBarAlt = win;
mStatusBarAltPosition = getAltBarPosition(attrs);
@@ -1219,41 +1243,59 @@ public class DisplayPolicy {
mExtraNavBarAltPosition = getAltBarPosition(attrs);
break;
}
- mDisplayContent.setInsetProvider(insetsType, win,
- win.getAttrs().providedInternalInsets != null ? (displayFrames,
- windowContainer, inOutFrame) -> {
- final Insets[] providedInternalInsets = win.getLayoutingAttrs(
- displayFrames.mRotation).providedInternalInsets;
- if (providedInternalInsets != null
- && providedInternalInsets.length > insetsType
- && providedInternalInsets[insetsType] != null) {
- inOutFrame.inset(providedInternalInsets[insetsType]);
- }
- inOutFrame.inset(win.mGivenContentInsets);
- } : null, imeFrameProvider);
- if (mNavigationBar == null && (insetsType == ITYPE_NAVIGATION_BAR
- || insetsType == ITYPE_EXTRA_NAVIGATION_BAR)) {
- mDisplayContent.setInsetProvider(ITYPE_LEFT_GESTURES, win,
- (displayFrames, windowState, inOutFrame) -> {
- final int leftSafeInset =
- Math.max(displayFrames.mDisplayCutoutSafe.left,0);
- inOutFrame.left = 0;
- inOutFrame.top = 0;
- inOutFrame.bottom = displayFrames.mDisplayHeight;
- inOutFrame.right =
- leftSafeInset + mLeftGestureInset;
- });
- mDisplayContent.setInsetProvider(ITYPE_RIGHT_GESTURES, win,
- (displayFrames, windowState, inOutFrame) -> {
- final int rightSafeInset =
- Math.min(displayFrames.mDisplayCutoutSafe.right,
- displayFrames.mUnrestricted.right);
- inOutFrame.left = rightSafeInset - mRightGestureInset;
- inOutFrame.top = 0;
- inOutFrame.bottom = displayFrames.mDisplayHeight;
- inOutFrame.right = displayFrames.mDisplayWidth;
- });
+ // The index of the provider and corresponding insets types cannot change at
+ // runtime as ensured in WMS. Make use of the index in the provider directly
+ // to access the latest provided size at runtime.
+ final int index = i;
+ final TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider =
+ provider.insetsSize != null
+ ? (displayFrames, windowContainer, inOutFrame) -> {
+ inOutFrame.inset(win.mGivenContentInsets);
+ final LayoutParams lp =
+ win.mAttrs.forRotation(displayFrames.mRotation);
+ final InsetsFrameProvider ifp =
+ win.mAttrs.forRotation(displayFrames.mRotation)
+ .providedInsets[index];
+ InsetsFrameProvider.calculateInsetsFrame(
+ displayFrames.mUnrestricted,
+ windowContainer.getBounds(),
+ displayFrames.mDisplayCutoutSafe,
+ inOutFrame, ifp.source,
+ ifp.insetsSize, lp.privateFlags);
+ } : null;
+ final InsetsFrameProvider.InsetsSizeOverride[] overrides =
+ provider.insetsSizeOverrides;
+ final SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>>
+ overrideProviders;
+ if (overrides != null) {
+ overrideProviders = new SparseArray<>();
+ for (int j = overrides.length - 1; j >= 0; j--) {
+ final int overrideIndex = j;
+ final TriConsumer<DisplayFrames, WindowContainer, Rect>
+ overrideFrameProvider =
+ (displayFrames, windowContainer, inOutFrame) -> {
+ final LayoutParams lp =
+ win.mAttrs.forRotation(
+ displayFrames.mRotation);
+ final InsetsFrameProvider ifp =
+ win.mAttrs.providedInsets[index];
+ InsetsFrameProvider.calculateInsetsFrame(
+ displayFrames.mUnrestricted,
+ windowContainer.getBounds(),
+ displayFrames.mDisplayCutoutSafe,
+ inOutFrame, ifp.source,
+ ifp.insetsSizeOverrides[
+ overrideIndex].insetsSize,
+ lp.privateFlags);
+ };
+ overrideProviders.put(overrides[j].windowType,
+ overrideFrameProvider);
+ }
+ } else {
+ overrideProviders = null;
}
+ mDisplayContent.setInsetProvider(provider.type, win, frameProvider,
+ overrideProviders);
mInsetsSourceWindowsExceptIme.add(win);
}
}
@@ -1299,10 +1341,11 @@ public class DisplayPolicy {
};
}
- private static void enforceSingleInsetsTypeCorrespondingToWindowType(int[] insetsTypes) {
+ private static void enforceSingleInsetsTypeCorrespondingToWindowType(
+ InsetsFrameProvider[] providers) {
int count = 0;
- for (int insetsType : insetsTypes) {
- switch (insetsType) {
+ for (InsetsFrameProvider provider : providers) {
+ switch (provider.type) {
case ITYPE_NAVIGATION_BAR:
case ITYPE_STATUS_BAR:
case ITYPE_CLIMATE_BAR:
@@ -1350,7 +1393,7 @@ public class DisplayPolicy {
private int getStatusBarHeight(DisplayFrames displayFrames) {
int statusBarHeight;
if (mStatusBar != null) {
- statusBarHeight = mStatusBar.getLayoutingAttrs(displayFrames.mRotation).height;
+ statusBarHeight = mStatusBar.mAttrs.forRotation(displayFrames.mRotation).height;
} else {
statusBarHeight = 0;
}
@@ -1504,13 +1547,14 @@ public class DisplayPolicy {
*/
void simulateLayoutDisplay(DisplayFrames displayFrames) {
final InsetsStateController controller = mDisplayContent.getInsetsStateController();
+ sTmpClientFrames.attachedFrame = null;
for (int i = mInsetsSourceWindowsExceptIme.size() - 1; i >= 0; i--) {
final WindowState win = mInsetsSourceWindowsExceptIme.valueAt(i);
- mWindowLayout.computeFrames(win.getLayoutingAttrs(displayFrames.mRotation),
+ mWindowLayout.computeFrames(win.mAttrs.forRotation(displayFrames.mRotation),
displayFrames.mInsetsState, displayFrames.mDisplayCutoutSafe,
displayFrames.mUnrestricted, win.getWindowingMode(), UNSPECIFIED_LENGTH,
- UNSPECIFIED_LENGTH, win.getRequestedVisibilities(),
- null /* attachedWindowFrame */, win.mGlobalScale, sTmpClientFrames);
+ UNSPECIFIED_LENGTH, win.getRequestedVisibilities(), win.mGlobalScale,
+ sTmpClientFrames);
final SparseArray<InsetsSource> sources = win.getProvidedInsetsSources();
final InsetsState state = displayFrames.mInsetsState;
for (int index = sources.size() - 1; index >= 0; index--) {
@@ -1522,13 +1566,14 @@ public class DisplayPolicy {
}
void updateInsetsSourceFramesExceptIme(DisplayFrames displayFrames) {
+ sTmpClientFrames.attachedFrame = null;
for (int i = mInsetsSourceWindowsExceptIme.size() - 1; i >= 0; i--) {
final WindowState win = mInsetsSourceWindowsExceptIme.valueAt(i);
- mWindowLayout.computeFrames(win.getLayoutingAttrs(displayFrames.mRotation),
+ mWindowLayout.computeFrames(win.mAttrs.forRotation(displayFrames.mRotation),
displayFrames.mInsetsState, displayFrames.mDisplayCutoutSafe,
displayFrames.mUnrestricted, win.getWindowingMode(), UNSPECIFIED_LENGTH,
- UNSPECIFIED_LENGTH, win.getRequestedVisibilities(),
- null /* attachedWindowFrame */, win.mGlobalScale, sTmpClientFrames);
+ UNSPECIFIED_LENGTH, win.getRequestedVisibilities(), win.mGlobalScale,
+ sTmpClientFrames);
win.updateSourceFrame(sTmpClientFrames.frame);
}
}
@@ -1557,8 +1602,8 @@ public class DisplayPolicy {
// We invoke this to get the proper DisplayFrames.
displayFrames = win.getDisplayFrames(displayFrames);
- final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation);
- final Rect attachedWindowFrame = attached != null ? attached.getFrame() : null;
+ final WindowManager.LayoutParams attrs = win.mAttrs.forRotation(displayFrames.mRotation);
+ sTmpClientFrames.attachedFrame = attached != null ? attached.getFrame() : null;
// If this window has different LayoutParams for rotations, we cannot trust its requested
// size. Because it might have not sent its requested size for the new rotation.
@@ -1568,8 +1613,7 @@ public class DisplayPolicy {
mWindowLayout.computeFrames(attrs, win.getInsetsState(), displayFrames.mDisplayCutoutSafe,
win.getBounds(), win.getWindowingMode(), requestedWidth, requestedHeight,
- win.getRequestedVisibilities(), attachedWindowFrame, win.mGlobalScale,
- sTmpClientFrames);
+ win.getRequestedVisibilities(), win.mGlobalScale, sTmpClientFrames);
win.setFrames(sTmpClientFrames, win.mRequestedWidth, win.mRequestedHeight);
}
@@ -1961,25 +2005,20 @@ public class DisplayPolicy {
&& lp.paramsForRotation[rotation] != null) {
lp = lp.paramsForRotation[rotation];
}
- final Insets providedInternalInsets;
- if (lp.providedInternalInsets != null
- && lp.providedInternalInsets.length > ITYPE_NAVIGATION_BAR
- && lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) {
- providedInternalInsets = lp.providedInternalInsets[ITYPE_NAVIGATION_BAR];
- } else {
- providedInternalInsets = Insets.NONE;
- }
- if (position == NAV_BAR_LEFT) {
- if (lp.width > providedInternalInsets.right) {
- return lp.width - providedInternalInsets.right;
- } else {
- return 0;
+ Insets providedInsetsSize = null;
+ if (lp.providedInsets != null) {
+ for (InsetsFrameProvider provider : lp.providedInsets) {
+ if (provider.type != ITYPE_NAVIGATION_BAR) {
+ continue;
+ }
+ providedInsetsSize = provider.insetsSize;
}
- } else if (position == NAV_BAR_RIGHT) {
- if (lp.width > providedInternalInsets.left) {
- return lp.width - providedInternalInsets.left;
- } else {
- return 0;
+ }
+ if (providedInsetsSize != null) {
+ if (position == NAV_BAR_LEFT) {
+ return providedInsetsSize.left;
+ } else if (position == NAV_BAR_RIGHT) {
+ return providedInsetsSize.right;
}
}
return lp.width;
@@ -2025,19 +2064,21 @@ public class DisplayPolicy {
if (mNavigationBar == null) {
return 0;
}
- LayoutParams lp = mNavigationBar.getLayoutingAttrs(rotation);
- final Insets providedInternalInsets;
- if (lp.providedInternalInsets != null
- && lp.providedInternalInsets.length > ITYPE_NAVIGATION_BAR
- && lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) {
- providedInternalInsets = lp.providedInternalInsets[ITYPE_NAVIGATION_BAR];
- } else {
- providedInternalInsets = Insets.NONE;
- }
- if (lp.height < providedInternalInsets.top) {
- return 0;
+ LayoutParams lp = mNavigationBar.mAttrs.forRotation(rotation);
+ Insets providedInsetsSize = null;
+ if (lp.providedInsets != null) {
+ for (InsetsFrameProvider provider : lp.providedInsets) {
+ if (provider.type != ITYPE_NAVIGATION_BAR) {
+ continue;
+ }
+ providedInsetsSize = provider.insetsSize;
+ if (providedInsetsSize != null) {
+ return providedInsetsSize.bottom;
+ }
+ break;
+ }
}
- return lp.height - providedInternalInsets.top;
+ return lp.height;
}
/**
@@ -2055,7 +2096,7 @@ public class DisplayPolicy {
if (mNavigationBar == null) {
return 0;
}
- return mNavigationBar.getLayoutingAttrs(rotation).height;
+ return mNavigationBar.mAttrs.forRotation(rotation).height;
}
/**
@@ -2184,7 +2225,7 @@ public class DisplayPolicy {
@NavigationBarPosition
int navigationBarPosition(int displayRotation) {
if (mNavigationBar != null) {
- final int gravity = mNavigationBar.getLayoutingAttrs(displayRotation).gravity;
+ final int gravity = mNavigationBar.mAttrs.forRotation(displayRotation).gravity;
switch (gravity) {
case Gravity.LEFT:
return NAV_BAR_LEFT;
@@ -2371,7 +2412,7 @@ public class DisplayPolicy {
mLastStatusBarAppearanceRegions = statusBarAppearanceRegions;
callStatusBarSafely(statusBar -> statusBar.onSystemBarAttributesChanged(displayId,
appearance, statusBarAppearanceRegions, isNavbarColorManagedByIme, behavior,
- requestedVisibilities, focusedApp));
+ requestedVisibilities, focusedApp, new LetterboxDetails[]{}));
}
private void callStatusBarSafely(Consumer<StatusBarManagerInternal> consumer) {
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 03e1429f1bf1..3d91921e3ab7 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -51,14 +51,12 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.power.Boost;
import android.os.Handler;
-import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
-import android.view.IDisplayWindowRotationCallback;
import android.view.IWindowManager;
import android.view.Surface;
import android.window.TransitionRequestInfo;
@@ -67,7 +65,6 @@ import android.window.WindowContainerTransaction;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy;
@@ -211,31 +208,6 @@ public class DisplayRotation {
private boolean mDemoHdmiRotationLock;
private boolean mDemoRotationLock;
- private static final int REMOTE_ROTATION_TIMEOUT_MS = 800;
-
- private boolean mIsWaitingForRemoteRotation = false;
-
- private final Runnable mDisplayRotationHandlerTimeout =
- new Runnable() {
- @Override
- public void run() {
- continueRotation(mRotation, null /* transaction */);
- }
- };
-
- private final IDisplayWindowRotationCallback mRemoteRotationCallback =
- new IDisplayWindowRotationCallback.Stub() {
- @Override
- public void continueRotateDisplay(int targetRotation,
- WindowContainerTransaction t) {
- synchronized (mService.getWindowManagerLock()) {
- mService.mH.sendMessage(PooledLambda.obtainMessage(
- DisplayRotation::continueRotation, DisplayRotation.this,
- targetRotation, t));
- }
- }
- };
-
DisplayRotation(WindowManagerService service, DisplayContent displayContent) {
this(service, displayContent, displayContent.getDisplayPolicy(),
service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock());
@@ -511,6 +483,7 @@ public class DisplayRotation {
final TransitionRequestInfo.DisplayChange change = wasCollecting ? null
: new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),
oldRotation, mRotation);
+
mDisplayContent.requestChangeTransitionIfNeeded(
ActivityInfo.CONFIG_WINDOW_CONFIGURATION, change);
if (wasCollecting) {
@@ -554,61 +527,39 @@ public class DisplayRotation {
return null;
}
- /**
- * A Remote rotation is when we are waiting for some registered (remote)
- * {@link IDisplayWindowRotationController} to calculate and return some hierarchy operations
- * to perform in sync with the rotation.
- */
- boolean isWaitingForRemoteRotation() {
- return mIsWaitingForRemoteRotation;
- }
-
private void startRemoteRotation(int fromRotation, int toRotation) {
- if (mService.mDisplayRotationController == null) {
- return;
- }
- mIsWaitingForRemoteRotation = true;
- try {
- mService.mDisplayRotationController.onRotateDisplay(mDisplayContent.getDisplayId(),
- fromRotation, toRotation, mRemoteRotationCallback);
- mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
- mService.mH.postDelayed(mDisplayRotationHandlerTimeout, REMOTE_ROTATION_TIMEOUT_MS);
- } catch (RemoteException e) {
- mIsWaitingForRemoteRotation = false;
- return;
- }
+ mDisplayContent.mRemoteDisplayChangeController.performRemoteDisplayChange(
+ fromRotation, toRotation, null /* newDisplayAreaInfo */,
+ (transaction) -> continueRotation(toRotation, transaction)
+ );
}
private void continueRotation(int targetRotation, WindowContainerTransaction t) {
- synchronized (mService.mGlobalLock) {
- if (targetRotation != mRotation || !mIsWaitingForRemoteRotation) {
- // Drop it, this is either coming from an outdated remote rotation; or, we've
- // already moved on.
- return;
- }
- mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
- mIsWaitingForRemoteRotation = false;
+ if (targetRotation != mRotation) {
+ // Drop it, this is either coming from an outdated remote rotation; or, we've
+ // already moved on.
+ return;
+ }
- if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
- if (!mDisplayContent.mTransitionController.isCollecting()) {
- throw new IllegalStateException("Trying to rotate outside a transition");
- }
- mDisplayContent.mTransitionController.collect(mDisplayContent);
- // Go through all tasks and collect them before the rotation
- // TODO(shell-transitions): move collect() to onConfigurationChange once wallpaper
- // handling is synchronized.
- mDisplayContent.mTransitionController.collectForDisplayChange(mDisplayContent,
- null /* use collecting transition */);
+ if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
+ if (!mDisplayContent.mTransitionController.isCollecting()) {
+ throw new IllegalStateException("Trying to rotate outside a transition");
}
- mService.mAtmService.deferWindowLayout();
- try {
- mDisplayContent.sendNewConfiguration();
- if (t != null) {
- mService.mAtmService.mWindowOrganizerController.applyTransaction(t);
- }
- } finally {
- mService.mAtmService.continueWindowLayout();
+ mDisplayContent.mTransitionController.collect(mDisplayContent);
+ // Go through all tasks and collect them before the rotation
+ // TODO(shell-transitions): move collect() to onConfigurationChange once wallpaper
+ // handling is synchronized.
+ mDisplayContent.mTransitionController.collectForDisplayAreaChange(mDisplayContent,
+ null /* use collecting transition */);
+ }
+ mService.mAtmService.deferWindowLayout();
+ try {
+ mDisplayContent.sendNewConfiguration();
+ if (t != null) {
+ mService.mAtmService.mWindowOrganizerController.applyTransaction(t);
}
+ } finally {
+ mService.mAtmService.continueWindowLayout();
}
}
@@ -669,7 +620,8 @@ public class DisplayRotation {
// We only enable seamless rotation if the top window has requested it and is in the
// fullscreen opaque state. Seamless rotation requires freezing various Surface states and
// won't work well with animations, so we disable it in the animation case for now.
- if (w.getAttrs().rotationAnimation != ROTATION_ANIMATION_SEAMLESS || w.isAnimatingLw()) {
+ if (w.getAttrs().rotationAnimation != ROTATION_ANIMATION_SEAMLESS || w.inMultiWindowMode()
+ || w.isAnimatingLw()) {
return false;
}
@@ -1571,8 +1523,8 @@ public class DisplayRotation {
}
@Override
- public boolean isKeyguardLocked() {
- return mService.isKeyguardLocked();
+ public boolean isKeyguardShowingAndNotOccluded() {
+ return mService.isKeyguardShowingAndNotOccluded();
}
@Override
@@ -1684,7 +1636,9 @@ public class DisplayRotation {
final WindowContainer<?> source = dc.getLastOrientationSource();
if (source != null) {
mLastOrientationSource = source.toString();
- mSourceOrientation = source.mOrientation;
+ final WindowState w = source.asWindowState();
+ mSourceOrientation =
+ w != null ? w.mAttrs.screenOrientation : source.mOrientation;
} else {
mLastOrientationSource = null;
mSourceOrientation = SCREEN_ORIENTATION_UNSET;
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 65b1f0233d93..f2d4d5427291 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -31,6 +31,8 @@ import static com.android.server.wm.DragDropController.MSG_TEAR_DOWN_DRAG_AND_DR
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.MY_PID;
+import static com.android.server.wm.WindowManagerService.MY_UID;
import android.animation.Animator;
import android.animation.PropertyValuesHolder;
@@ -45,7 +47,6 @@ import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.InputConfig;
-import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -208,8 +209,6 @@ class DragState {
// Send drag end broadcast if drag start has been sent.
if (mDragInProgress) {
- final int myPid = Process.myPid();
-
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "broadcasting DRAG_ENDED");
}
@@ -237,7 +236,7 @@ class DragState {
}
// if the current window is in the same process,
// the dispatch has already recycled the event
- if (myPid != ws.mSession.mPid) {
+ if (MY_PID != ws.mSession.mPid) {
event.recycle();
}
}
@@ -321,7 +320,6 @@ class DragState {
mData.fixUris(mSourceUserId);
}
}
- final int myPid = Process.myPid();
final IBinder clientToken = touchedWin.mClient.asBinder();
final DragEvent event = obtainDragEvent(DragEvent.ACTION_DROP, x, y,
mData, targetInterceptsGlobalDrag(touchedWin),
@@ -337,7 +335,7 @@ class DragState {
endDragLocked();
return false;
} finally {
- if (myPid != touchedWin.mSession.mPid) {
+ if (MY_PID != touchedWin.mSession.mPid) {
event.recycle();
}
}
@@ -365,8 +363,8 @@ class DragState {
mDragWindowHandle.token = mClientChannel.getToken();
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- mDragWindowHandle.ownerPid = Process.myPid();
- mDragWindowHandle.ownerUid = Process.myUid();
+ mDragWindowHandle.ownerPid = MY_PID;
+ mDragWindowHandle.ownerUid = MY_UID;
mDragWindowHandle.scaleFactor = 1.0f;
// Keep the default behavior of this window to be focusable, which allows the system
@@ -478,7 +476,7 @@ class DragState {
Slog.w(TAG_WM, "Unable to drag-start window " + newWin);
} finally {
// if the callee was local, the dispatch has already recycled the event
- if (Process.myPid() != newWin.mSession.mPid) {
+ if (MY_PID != newWin.mSession.mPid) {
event.recycle();
}
}
diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags
index 6d63331622b9..1e5a219e5e52 100644
--- a/services/core/java/com/android/server/wm/EventLogTags.logtags
+++ b/services/core/java/com/android/server/wm/EventLogTags.logtags
@@ -66,4 +66,4 @@ option java_package com.android.server.wm
31007 wm_boot_animation_done (time|2|3)
# Request surface flinger to show / hide the wallpaper surface.
-33001 wm_wallpaper_surface (Display Id|1|5),(visible|1)
+33001 wm_wallpaper_surface (Display Id|1|5),(Visible|1),(Target|3)
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 0d4cfa3a8128..3e6e06a27fdc 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -234,7 +234,7 @@ final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider
//
private static boolean isImeLayeringTarget(@NonNull InsetsControlTarget target,
@NonNull InsetsControlTarget dcTarget) {
- return !dcTarget.getWindow().isClosing() && target == dcTarget;
+ return !isImeTargetWindowClosing(dcTarget.getWindow()) && target == dcTarget;
}
private static boolean isAboveImeLayeringTarget(@NonNull InsetsControlTarget target,
@@ -256,7 +256,14 @@ final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider
final InsetsControlTarget target = mDisplayContent.getImeTarget(IME_TARGET_CONTROL);
return target == mImeRequester
&& (mImeRequester.getWindow() == null
- || !mImeRequester.getWindow().isClosing());
+ || !isImeTargetWindowClosing(mImeRequester.getWindow()));
+ }
+
+ private static boolean isImeTargetWindowClosing(@NonNull WindowState win) {
+ return win.mAnimatingExit || win.mActivityRecord != null
+ && (win.mActivityRecord.isInTransition()
+ && !win.mActivityRecord.isVisibleRequested()
+ || win.mActivityRecord.willCloseOrEnterPip());
}
private boolean isTargetChangedWithinActivity(InsetsControlTarget target) {
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 59be3e05f2c0..39622c1c5aaf 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -23,7 +23,6 @@ import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.InputConfig;
-import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.view.InputApplicationHandle;
@@ -72,8 +71,8 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
mWindowHandle.token = mClientChannel.getToken();
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- mWindowHandle.ownerPid = Process.myPid();
- mWindowHandle.ownerUid = Process.myUid();
+ mWindowHandle.ownerPid = WindowManagerService.MY_PID;
+ mWindowHandle.ownerUid = WindowManagerService.MY_UID;
mWindowHandle.scaleFactor = 1.0f;
mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.TRUSTED_OVERLAY;
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index b7ddbd070460..33cdd2e98113 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -265,7 +265,7 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
.setContainerLayer()
.setName(name)
.setCallsite("createSurfaceForGestureMonitor")
- .setParent(dc.getOverlayLayer())
+ .setParent(dc.getSurfaceControl())
.build();
}
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 7e06b8823260..2ce333d5438b 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -64,7 +64,10 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.LocalServices;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -288,10 +291,7 @@ final class InputMonitor {
SurfaceControl touchableRegionCrop = null;
final Task task = w.getTask();
if (task != null) {
- // TODO(b/165794636): Remove the special case for freeform window once drag resizing is
- // handled by WM shell.
- if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
- && !task.inFreeformWindowingMode()) {
+ if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
// If the window is in a TaskManaged by a TaskOrganizer then most cropping will
// be applied using the SurfaceControl hierarchy from the Organizer. This means
// we need to make sure that these changes in crop are reflected in the input
@@ -408,8 +408,28 @@ final class InputMonitor {
// Shell transitions doesn't use RecentsAnimationController
|| getWeak(mActiveRecentsActivity) != null && focus.inTransition();
if (shouldApplyRecentsInputConsumer) {
- requestFocus(recentsAnimationInputConsumer.mWindowHandle.token,
- recentsAnimationInputConsumer.mName);
+ if (mInputFocus != recentsAnimationInputConsumer.mWindowHandle.token) {
+ requestFocus(recentsAnimationInputConsumer.mWindowHandle.token,
+ recentsAnimationInputConsumer.mName);
+ // Hiding IME/IME icon when recents input consumer gain focus.
+ if (!mDisplayContent.isImeAttachedToApp()) {
+ // Hiding IME if IME window is not attached to app since it's not proper to
+ // snapshot Task with IME window to animate together in this case.
+ final InputMethodManagerInternal inputMethodManagerInternal =
+ LocalServices.getService(InputMethodManagerInternal.class);
+ if (inputMethodManagerInternal != null) {
+ inputMethodManagerInternal.hideCurrentInputMethod(
+ SoftInputShowHideReason.HIDE_RECENTS_ANIMATION);
+ }
+ } else {
+ // Disable IME icon explicitly when IME attached to the app in case
+ // IME icon might flickering while swiping to the next app task still
+ // in animating before the next app window focused, or IME icon
+ // persists on the bottom when swiping the task to recents.
+ InputMethodManagerInternal.get().updateImeWindowStatus(
+ true /* disableImeIcon */);
+ }
+ }
return;
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 9e0d7b57264e..3e2d7c928936 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -46,6 +46,7 @@ import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.StatusBarManager;
import android.app.WindowConfiguration;
+import android.content.ComponentName;
import android.content.res.Resources;
import android.graphics.Rect;
import android.util.ArrayMap;
@@ -55,6 +56,7 @@ import android.view.InsetsAnimationControlCallbacks;
import android.view.InsetsAnimationControlImpl;
import android.view.InsetsAnimationControlRunner;
import android.view.InsetsController;
+import android.view.InsetsFrameProvider;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -264,7 +266,7 @@ class InsetsPolicy {
state = originalState;
}
state = adjustVisibilityForIme(target, state, state == originalState);
- return adjustInsetsForRoundedCorners(target, state, state == originalState);
+ return adjustInsetsForRoundedCorners(target.mToken, state, state == originalState);
}
InsetsState adjustInsetsForWindow(WindowState target, InsetsState originalState) {
@@ -288,8 +290,9 @@ class InsetsPolicy {
// contains all insets types.
final InsetsState originalState = mDisplayContent.getInsetsPolicy()
.enforceInsetsPolicyForTarget(type, WINDOWING_MODE_FULLSCREEN, alwaysOnTop,
- mStateController.getRawInsetsState());
- return adjustVisibilityForTransientTypes(originalState);
+ attrs.type, mStateController.getRawInsetsState());
+ InsetsState state = adjustVisibilityForTransientTypes(originalState);
+ return adjustInsetsForRoundedCorners(token, state, state == originalState);
}
/**
@@ -322,14 +325,14 @@ class InsetsPolicy {
}
// If not one of the types above, check whether an internal inset mapping is specified.
- if (attrs.providesInsetsTypes != null) {
- for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
- switch (insetsType) {
+ if (attrs.providedInsets != null) {
+ for (InsetsFrameProvider provider : attrs.providedInsets) {
+ switch (provider.type) {
case ITYPE_STATUS_BAR:
case ITYPE_NAVIGATION_BAR:
case ITYPE_CLIMATE_BAR:
case ITYPE_EXTRA_NAVIGATION_BAR:
- return insetsType;
+ return provider.type;
}
}
}
@@ -349,12 +352,13 @@ class InsetsPolicy {
* @param type the inset type provided by the target
* @param windowingMode the windowing mode of the target
* @param isAlwaysOnTop is the target always on top
+ * @param windowType the type of the target
* @param state the input inset state containing all the sources
* @return The state stripped of the necessary information.
*/
InsetsState enforceInsetsPolicyForTarget(@InternalInsetsType int type,
@WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop,
- InsetsState state) {
+ int windowType, InsetsState state) {
boolean stateCopied = false;
if (type != ITYPE_INVALID) {
@@ -375,21 +379,20 @@ class InsetsPolicy {
if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) {
state.removeSource(ITYPE_CAPTION_BAR);
}
-
- // IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
- if (type == ITYPE_IME) {
- ArrayMap<Integer, WindowContainerInsetsSourceProvider> providers = mStateController
- .getSourceProviders();
- for (int i = providers.size() - 1; i >= 0; i--) {
- WindowContainerInsetsSourceProvider otherProvider = providers.valueAt(i);
- if (otherProvider.overridesImeFrame()) {
- InsetsSource override =
- new InsetsSource(
- state.getSource(otherProvider.getSource().getType()));
- override.setFrame(otherProvider.getImeOverrideFrame());
- state.addSource(override);
- }
+ }
+ ArrayMap<Integer, WindowContainerInsetsSourceProvider> providers = mStateController
+ .getSourceProviders();
+ for (int i = providers.size() - 1; i >= 0; i--) {
+ WindowContainerInsetsSourceProvider otherProvider = providers.valueAt(i);
+ if (otherProvider.overridesFrame(windowType)) {
+ if (!stateCopied) {
+ state = new InsetsState(state);
+ stateCopied = true;
}
+ InsetsSource override =
+ new InsetsSource(state.getSource(otherProvider.getSource().getType()));
+ override.setFrame(otherProvider.getOverriddenFrame(windowType));
+ state.addSource(override);
}
}
@@ -464,15 +467,19 @@ class InsetsPolicy {
return originalState;
}
- private InsetsState adjustInsetsForRoundedCorners(WindowState w, InsetsState originalState,
+ private InsetsState adjustInsetsForRoundedCorners(WindowToken token, InsetsState originalState,
boolean copyState) {
- final Task task = w.getTask();
- if (task != null && !task.getWindowConfiguration().tasksAreFloating()) {
- // Use task bounds to calculating rounded corners if the task is not floating.
- final Rect roundedCornerFrame = new Rect(task.getBounds());
- final InsetsState state = copyState ? new InsetsState(originalState) : originalState;
- state.setRoundedCornerFrame(roundedCornerFrame);
- return state;
+ if (token != null) {
+ final ActivityRecord activityRecord = token.asActivityRecord();
+ final Task task = activityRecord != null ? activityRecord.getTask() : null;
+ if (task != null && !task.getWindowConfiguration().tasksAreFloating()) {
+ // Use task bounds to calculating rounded corners if the task is not floating.
+ final Rect roundedCornerFrame = new Rect(task.getBounds());
+ final InsetsState state = copyState ? new InsetsState(originalState)
+ : originalState;
+ state.setRoundedCornerFrame(roundedCornerFrame);
+ return state;
+ }
}
return originalState;
}
@@ -542,8 +549,10 @@ class InsetsPolicy {
return focusedWin;
}
if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
+ ComponentName component = focusedWin.mActivityRecord != null
+ ? focusedWin.mActivityRecord.mActivityComponent : null;
mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
- focusedWin.mAttrs.packageName, focusedWin.getRequestedVisibilities());
+ component, focusedWin.getRequestedVisibilities());
return mDisplayContent.mRemoteInsetsControlTarget;
}
if (mPolicy.areSystemBarsForcedShownLw()) {
@@ -600,8 +609,10 @@ class InsetsPolicy {
return null;
}
if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
+ ComponentName component = focusedWin.mActivityRecord != null
+ ? focusedWin.mActivityRecord.mActivityComponent : null;
mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
- focusedWin.mAttrs.packageName, focusedWin.getRequestedVisibilities());
+ component, focusedWin.getRequestedVisibilities());
return mDisplayContent.mRemoteInsetsControlTarget;
}
if (mPolicy.areSystemBarsForcedShownLw()) {
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 358e93d89f64..86a73c935e52 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -27,7 +27,6 @@ import static com.android.server.wm.InsetsSourceProviderProto.CONTROL_TARGET;
import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL;
import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL_TARGET;
import static com.android.server.wm.InsetsSourceProviderProto.FRAME;
-import static com.android.server.wm.InsetsSourceProviderProto.IME_OVERRIDDEN_FRAME;
import static com.android.server.wm.InsetsSourceProviderProto.IS_LEASH_READY_FOR_DISPATCHING;
import static com.android.server.wm.InsetsSourceProviderProto.PENDING_CONTROL_TARGET;
import static com.android.server.wm.InsetsSourceProviderProto.SEAMLESS_ROTATING;
@@ -41,7 +40,9 @@ import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
+import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import android.view.InsetsFrameProvider;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -77,8 +78,8 @@ abstract class InsetsSourceProvider {
private @Nullable ControlAdapter mAdapter;
private TriConsumer<DisplayFrames, WindowContainer, Rect> mFrameProvider;
- private TriConsumer<DisplayFrames, WindowContainer, Rect> mImeFrameProvider;
- private final Rect mImeOverrideFrame = new Rect();
+ private SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>> mOverrideFrameProviders;
+ private final SparseArray<Rect> mOverrideFrames = new SparseArray<Rect>();
private boolean mIsLeashReadyForDispatching;
private final Rect mSourceFrame = new Rect();
private final Rect mLastSourceFrame = new Rect();
@@ -145,12 +146,15 @@ abstract class InsetsSourceProvider {
* @param windowContainer The window container that links to this source.
* @param frameProvider Based on display frame state and the window, calculates the resulting
* frame that should be reported to clients.
- * @param imeFrameProvider Based on display frame state and the window, calculates the resulting
- * frame that should be reported to IME.
+ * This will only be used when the window container providing the insets is
+ * not a WindowState.
+ * @param overrideFrameProviders Based on display frame state and the window, calculates the
+ * resulting frame that should be reported to given window type.
*/
void setWindowContainer(@Nullable WindowContainer windowContainer,
@Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider,
- @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider) {
+ @Nullable SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>>
+ overrideFrameProviders) {
if (mWindowContainer != null) {
if (mControllable) {
mWindowContainer.setControllableInsetProvider(null);
@@ -166,8 +170,9 @@ abstract class InsetsSourceProvider {
ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "InsetsSource setWin %s for type %s",
windowContainer, InsetsState.typeToString(mSource.getType()));
mWindowContainer = windowContainer;
+ // TODO: remove the frame provider for non-WindowState container.
mFrameProvider = frameProvider;
- mImeFrameProvider = imeFrameProvider;
+ mOverrideFrameProviders = overrideFrameProviders;
if (windowContainer == null) {
setServerVisible(false);
mSource.setVisibleFrame(null);
@@ -227,10 +232,25 @@ abstract class InsetsSourceProvider {
}
updateSourceFrameForServerVisibility();
- if (mImeFrameProvider != null) {
- mImeOverrideFrame.set(frame);
- mImeFrameProvider.accept(mWindowContainer.getDisplayContent().mDisplayFrames,
- mWindowContainer, mImeOverrideFrame);
+ if (mOverrideFrameProviders != null) {
+ for (int i = mOverrideFrameProviders.size() - 1; i >= 0; i--) {
+ final int windowType = mOverrideFrameProviders.keyAt(i);
+ final Rect overrideFrame;
+ if (mOverrideFrames.contains(windowType)) {
+ overrideFrame = mOverrideFrames.get(windowType);
+ overrideFrame.set(frame);
+ } else {
+ overrideFrame = new Rect(frame);
+ }
+ final TriConsumer<DisplayFrames, WindowContainer, Rect> provider =
+ mOverrideFrameProviders.get(windowType);
+ if (provider != null) {
+ mOverrideFrameProviders.get(windowType).accept(
+ mWindowContainer.getDisplayContent().mDisplayFrames, mWindowContainer,
+ overrideFrame);
+ }
+ mOverrideFrames.put(windowType, overrideFrame);
+ }
}
if (win.mGivenVisibleInsets.left != 0 || win.mGivenVisibleInsets.top != 0
@@ -290,7 +310,22 @@ abstract class InsetsSourceProvider {
&& windowState.mWinAnimator.getShown() && mWindowContainer.okToDisplay()) {
windowState.applyWithNextDraw(mSetLeashPositionConsumer);
} else {
- mSetLeashPositionConsumer.accept(mWindowContainer.getSyncTransaction());
+ Transaction t = mWindowContainer.getSyncTransaction();
+ if (windowState != null) {
+ // Make the buffer, token transformation, and leash position to be updated
+ // together when the window is drawn for new rotation. Otherwise the window
+ // may be outside the screen by the inconsistent orientations.
+ final AsyncRotationController rotationController =
+ mDisplayContent.getAsyncRotationController();
+ if (rotationController != null) {
+ final Transaction drawT =
+ rotationController.getDrawTransaction(windowState.mToken);
+ if (drawT != null) {
+ t = drawT;
+ }
+ }
+ }
+ mSetLeashPositionConsumer.accept(t);
}
}
if (mServerVisible && !mLastSourceFrame.equals(mSource.getFrame())) {
@@ -310,17 +345,15 @@ abstract class InsetsSourceProvider {
}
private Point getWindowFrameSurfacePosition() {
- WindowState win = mWindowContainer.asWindowState();
- if (mControl != null) {
- final AsyncRotationController controller =
- win.mDisplayContent.getAsyncRotationController();
+ final WindowState win = mWindowContainer.asWindowState();
+ if (win != null && mControl != null) {
+ final AsyncRotationController controller = mDisplayContent.getAsyncRotationController();
if (controller != null && controller.shouldFreezeInsetsPosition(win)) {
- // Use previous position because the fade-out animation runs in old rotation.
+ // Use previous position because the window still shows with old rotation.
return mControl.getSurfacePosition();
}
}
- final Rect frame = mWindowContainer.asWindowState() != null
- ? mWindowContainer.asWindowState().getFrame() : mWindowContainer.getBounds();
+ final Rect frame = win != null ? win.getFrame() : mWindowContainer.getBounds();
final Point position = new Point();
mWindowContainer.transformFrameToSurfacePosition(frame.left, frame.top, position);
return position;
@@ -500,12 +533,13 @@ abstract class InsetsSourceProvider {
if (mWindowContainer.asWindowState() == null) {
return false;
}
- final int[] provides = ((WindowState) mWindowContainer).mAttrs.providesInsetsTypes;
- if (provides == null) {
+ final InsetsFrameProvider[] providers =
+ ((WindowState) mWindowContainer).mAttrs.providedInsets;
+ if (providers == null) {
return false;
}
- for (int i = 0; i < provides.length; i++) {
- if (provides[i] == ITYPE_IME) {
+ for (int i = 0; i < providers.length; i++) {
+ if (providers[i].type == ITYPE_IME) {
return true;
}
}
@@ -537,32 +571,30 @@ abstract class InsetsSourceProvider {
return mClientVisible;
}
- /**
- * @return Whether this provider uses a different frame to dispatch to the IME.
- */
- boolean overridesImeFrame() {
- return mImeFrameProvider != null;
+ boolean overridesFrame(int windowType) {
+ return mOverrideFrames.contains(windowType);
}
- /**
- * @return Rect to dispatch to the IME as frame. Only valid if {@link #overridesImeFrame()}
- * returns {@code true}.
- */
- Rect getImeOverrideFrame() {
- return mImeOverrideFrame;
+ Rect getOverriddenFrame(int windowType) {
+ return mOverrideFrames.get(windowType);
}
public void dump(PrintWriter pw, String prefix) {
pw.println(prefix + getClass().getSimpleName());
prefix = prefix + " ";
pw.print(prefix + "mSource="); mSource.dump("", pw);
+ pw.print(prefix + "mSourceFrame=");
+ pw.println(mSourceFrame);
+ if (mOverrideFrames.size() > 0) {
+ pw.print(prefix + "mOverrideFrames=");
+ pw.println(mOverrideFrames);
+ }
if (mControl != null) {
pw.print(prefix + "mControl=");
mControl.dump("", pw);
}
pw.print(prefix);
pw.print("mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching);
- pw.print(" mImeOverrideFrame="); pw.print(mImeOverrideFrame.toShortString());
pw.println();
if (mWindowContainer != null) {
pw.print(prefix + "mWindowContainer=");
@@ -606,7 +638,6 @@ abstract class InsetsSourceProvider {
if (mAdapter != null && mAdapter.mCapturedLeash != null) {
mAdapter.mCapturedLeash.dumpDebug(proto, CAPTURED_LEASH);
}
- mImeOverrideFrame.dumpDebug(proto, IME_OVERRIDDEN_FRAME);
proto.write(IS_LEASH_READY_FOR_DISPATCHING, mIsLeashReadyForDispatching);
proto.write(CLIENT_VISIBLE, mClientVisible);
proto.write(SERVER_VISIBLE, mServerVisible);
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 01c94142f46b..d76f6be93aeb 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -41,6 +41,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.KeyguardControllerProto.AOD_SHOWING;
+import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_GOING_AWAY;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_PER_DISPLAY;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING;
@@ -72,6 +73,8 @@ class KeyguardController {
static final String KEYGUARD_SLEEP_TOKEN_TAG = "keyguard";
+ private static final int DEFER_WAKE_TRANSITION_TIMEOUT_MS = 5000;
+
private final ActivityTaskSupervisor mTaskSupervisor;
private WindowManagerService mWindowManager;
@@ -79,7 +82,7 @@ class KeyguardController {
private final ActivityTaskManagerService mService;
private RootWindowContainer mRootWindowContainer;
private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer;
-
+ private boolean mWaitingForWakeTransition;
KeyguardController(ActivityTaskManagerService service,
ActivityTaskSupervisor taskSupervisor) {
@@ -167,10 +170,14 @@ class KeyguardController {
final KeyguardDisplayState state = getDisplayState(displayId);
final boolean aodChanged = aodShowing != state.mAodShowing;
+ final boolean aodRemoved = state.mAodShowing && !aodShowing;
// If keyguard is going away, but SystemUI aborted the transition, need to reset state.
- // Do not reset keyguardChanged status if this is aodChanged.
+ // Do not reset keyguardChanged status when only AOD is removed.
final boolean keyguardChanged = (keyguardShowing != state.mKeyguardShowing)
- || (state.mKeyguardGoingAway && keyguardShowing && !aodChanged);
+ || (state.mKeyguardGoingAway && keyguardShowing && !aodRemoved);
+ if (aodRemoved) {
+ updateDeferWakeTransition(false /* waiting */);
+ }
if (!keyguardChanged && !aodChanged) {
setWakeTransitionReady();
return;
@@ -199,10 +206,6 @@ class KeyguardController {
state.mKeyguardShowing = keyguardShowing;
state.mAodShowing = aodShowing;
- if (aodChanged) {
- // Ensure the new state takes effect.
- mWindowManager.mWindowPlacerLocked.performSurfacePlacement();
- }
if (keyguardChanged) {
// Irrelevant to AOD.
@@ -220,6 +223,10 @@ class KeyguardController {
mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
InputMethodManagerInternal.get().updateImeWindowStatus(false /* disableImeIcon */);
setWakeTransitionReady();
+ if (aodChanged) {
+ // Ensure the new state takes effect.
+ mWindowManager.mWindowPlacerLocked.performSurfacePlacement();
+ }
}
private void setWakeTransitionReady() {
@@ -526,6 +533,33 @@ class KeyguardController {
}
}
+ private final Runnable mResetWaitTransition = () -> {
+ synchronized (mWindowManager.mGlobalLock) {
+ updateDeferWakeTransition(false /* waiting */);
+ }
+ };
+
+ void updateDeferWakeTransition(boolean waiting) {
+ if (waiting == mWaitingForWakeTransition) {
+ return;
+ }
+ if (!mWindowManager.mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ return;
+ }
+ // if aod is showing, defer the wake transition until aod state changed.
+ if (waiting && isAodShowing(DEFAULT_DISPLAY)) {
+ mWaitingForWakeTransition = true;
+ mWindowManager.mAtmService.getTransitionController().deferTransitionReady();
+ mWindowManager.mH.postDelayed(mResetWaitTransition, DEFER_WAKE_TRANSITION_TIMEOUT_MS);
+ } else if (!waiting) {
+ // dismiss the deferring if the aod state change or cancel awake.
+ mWaitingForWakeTransition = false;
+ mWindowManager.mAtmService.getTransitionController().continueTransitionReady();
+ mWindowManager.mH.removeCallbacks(mResetWaitTransition);
+ }
+ }
+
+
/** Represents Keyguard state per individual display. */
private static class KeyguardDisplayState {
private final int mDisplayId;
@@ -594,6 +628,7 @@ class KeyguardController {
} else if (top.canShowWhenLocked()) {
mTopOccludesActivity = top;
}
+ top.mDismissKeyguard = false;
// Only the top activity may control occluded, as we can't occlude the Keyguard
// if the top app doesn't want to occlude it.
@@ -671,6 +706,7 @@ class KeyguardController {
proto.write(KeyguardPerDisplayProto.KEYGUARD_SHOWING, mKeyguardShowing);
proto.write(KeyguardPerDisplayProto.AOD_SHOWING, mAodShowing);
proto.write(KeyguardPerDisplayProto.KEYGUARD_OCCLUDED, mOccluded);
+ proto.write(KeyguardPerDisplayProto.KEYGUARD_GOING_AWAY, mKeyguardGoingAway);
proto.end(token);
}
}
@@ -691,6 +727,7 @@ class KeyguardController {
final long token = proto.start(fieldId);
proto.write(AOD_SHOWING, default_state.mAodShowing);
proto.write(KEYGUARD_SHOWING, default_state.mKeyguardShowing);
+ proto.write(KEYGUARD_GOING_AWAY, default_state.mKeyguardGoingAway);
writeDisplayStatesToProto(proto, KEYGUARD_PER_DISPLAY);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 40df02c176e5..b5eff41d4f62 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -25,7 +25,6 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.InputConfig;
-import android.os.Process;
import android.view.GestureDetector;
import android.view.InputChannel;
import android.view.InputEvent;
@@ -72,7 +71,8 @@ public class Letterbox {
private final LetterboxSurface mFullWindowSurface = new LetterboxSurface("fullWindow");
private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
// Reachability gestures.
- private final IntConsumer mDoubleTapCallback;
+ private final IntConsumer mDoubleTapCallbackX;
+ private final IntConsumer mDoubleTapCallbackY;
/**
* Constructs a Letterbox.
@@ -86,7 +86,8 @@ public class Letterbox {
Supplier<Boolean> hasWallpaperBackgroundSupplier,
Supplier<Integer> blurRadiusSupplier,
Supplier<Float> darkScrimAlphaSupplier,
- IntConsumer doubleTapCallback) {
+ IntConsumer doubleTapCallbackX,
+ IntConsumer doubleTapCallbackY) {
mSurfaceControlFactory = surfaceControlFactory;
mTransactionFactory = transactionFactory;
mAreCornersRounded = areCornersRounded;
@@ -94,7 +95,8 @@ public class Letterbox {
mHasWallpaperBackgroundSupplier = hasWallpaperBackgroundSupplier;
mBlurRadiusSupplier = blurRadiusSupplier;
mDarkScrimAlphaSupplier = darkScrimAlphaSupplier;
- mDoubleTapCallback = doubleTapCallback;
+ mDoubleTapCallbackX = doubleTapCallbackX;
+ mDoubleTapCallbackY = doubleTapCallbackY;
}
/**
@@ -264,7 +266,8 @@ public class Letterbox {
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_UP) {
- mDoubleTapCallback.accept((int) e.getX());
+ mDoubleTapCallbackX.accept((int) e.getX());
+ mDoubleTapCallbackY.accept((int) e.getY());
return true;
}
return false;
@@ -293,8 +296,8 @@ public class Letterbox {
mWindowHandle.token = mToken;
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- mWindowHandle.ownerPid = Process.myPid();
- mWindowHandle.ownerUid = Process.myUid();
+ mWindowHandle.ownerPid = WindowManagerService.MY_PID;
+ mWindowHandle.ownerUid = WindowManagerService.MY_UID;
mWindowHandle.scaleFactor = 1.0f;
mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.SLIPPERY;
}
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index d02ad992c7e8..91b2fb63a543 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -22,7 +22,6 @@ import android.content.Context;
import android.graphics.Color;
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -56,25 +55,48 @@ final class LetterboxConfiguration {
static final int LETTERBOX_BACKGROUND_WALLPAPER = 3;
/**
- * Enum for Letterbox reachability position types.
+ * Enum for Letterbox horizontal reachability position types.
*
* <p>Order from left to right is important since it's used in {@link
* #movePositionForReachabilityToNextRightStop} and {@link
* #movePositionForReachabilityToNextLeftStop}.
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({LETTERBOX_REACHABILITY_POSITION_LEFT, LETTERBOX_REACHABILITY_POSITION_CENTER,
- LETTERBOX_REACHABILITY_POSITION_RIGHT})
- @interface LetterboxReachabilityPosition {};
+ @IntDef({LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT,
+ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER,
+ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT})
+ @interface LetterboxHorizontalReachabilityPosition {};
/** Letterboxed app window is aligned to the left side. */
- static final int LETTERBOX_REACHABILITY_POSITION_LEFT = 0;
+ static final int LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT = 0;
/** Letterboxed app window is positioned in the horizontal center. */
- static final int LETTERBOX_REACHABILITY_POSITION_CENTER = 1;
+ static final int LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER = 1;
/** Letterboxed app window is aligned to the right side. */
- static final int LETTERBOX_REACHABILITY_POSITION_RIGHT = 2;
+ static final int LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT = 2;
+
+ /**
+ * Enum for Letterbox vertical reachability position types.
+ *
+ * <p>Order from top to bottom is important since it's used in {@link
+ * #movePositionForReachabilityToNextBottomStop} and {@link
+ * #movePositionForReachabilityToNextTopStop}.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP,
+ LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER,
+ LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM})
+ @interface LetterboxVerticalReachabilityPosition {};
+
+ /** Letterboxed app window is aligned to the left side. */
+ static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP = 0;
+
+ /** Letterboxed app window is positioned in the vertical center. */
+ static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER = 1;
+
+ /** Letterboxed app window is aligned to the right side. */
+ static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM = 2;
final Context mContext;
@@ -82,6 +104,9 @@ final class LetterboxConfiguration {
// MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored.
private float mFixedOrientationLetterboxAspectRatio;
+ // Default min aspect ratio for unresizable apps that are eligible for the size compat mode.
+ private float mDefaultMinAspectRatioForUnresizableApps;
+
// Corners radius for activities presented in the letterbox mode, values < 0 will be ignored.
private int mLetterboxActivityCornersRadius;
@@ -107,29 +132,57 @@ final class LetterboxConfiguration {
// side of the screen and 1.0 to the right side.
private float mLetterboxHorizontalPositionMultiplier;
- // Default horizontal position the letterboxed app window when reachability is enabled and
- // an app is fullscreen in landscape device orientatio.
- // It is used as a starting point for mLetterboxPositionForReachability.
- @LetterboxReachabilityPosition
- private int mDefaultPositionForReachability;
+ // Vertical position of a center of the letterboxed app window. 0 corresponds to the top
+ // side of the screen and 1.0 to the bottom side.
+ private float mLetterboxVerticalPositionMultiplier;
+
+ // Default horizontal position the letterboxed app window when horizontal reachability is
+ // enabled and an app is fullscreen in landscape device orientation.
+ // It is used as a starting point for mLetterboxPositionForHorizontalReachability.
+ @LetterboxHorizontalReachabilityPosition
+ private int mDefaultPositionForHorizontalReachability;
+
+ // Default vertical position the letterboxed app window when vertical reachability is enabled
+ // and an app is fullscreen in portrait device orientation.
+ // It is used as a starting point for mLetterboxPositionForVerticalReachability.
+ @LetterboxVerticalReachabilityPosition
+ private int mDefaultPositionForVerticalReachability;
+
+ // Whether horizontal reachability repositioning is allowed for letterboxed fullscreen apps in
+ // landscape device orientation.
+ private boolean mIsHorizontalReachabilityEnabled;
+
+ // Whether vertical reachability repositioning is allowed for letterboxed fullscreen apps in
+ // portrait device orientation.
+ private boolean mIsVerticalReachabilityEnabled;
- // Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape
- // device orientation.
- private boolean mIsReachabilityEnabled;
// Horizontal position of a center of the letterboxed app window which is global to prevent
// "jumps" when switching between letterboxed apps. It's updated to reposition the app window
// in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in
// LetterboxUiController#getHorizontalPositionMultiplier which is called from
- // ActivityRecord#updateResolvedBoundsHorizontalPosition.
+ // ActivityRecord#updateResolvedBoundsPosition.
+ // TODO(b/199426138): Global reachability setting causes a jump when resuming an app from
+ // Overview after changing position in another app.
+ @LetterboxHorizontalReachabilityPosition
+ private volatile int mLetterboxPositionForHorizontalReachability;
+
+ // Vertical position of a center of the letterboxed app window which is global to prevent
+ // "jumps" when switching between letterboxed apps. It's updated to reposition the app window
+ // in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in
+ // LetterboxUiController#getVerticalPositionMultiplier which is called from
+ // ActivityRecord#updateResolvedBoundsPosition.
// TODO(b/199426138): Global reachability setting causes a jump when resuming an app from
// Overview after changing position in another app.
- @LetterboxReachabilityPosition
- private volatile int mLetterboxPositionForReachability;
+ @LetterboxVerticalReachabilityPosition
+ private volatile int mLetterboxPositionForVerticalReachability;
// Whether education is allowed for letterboxed fullscreen apps.
private boolean mIsEducationEnabled;
+ // Whether using split screen aspect ratio as a default aspect ratio for unresizable apps.
+ private boolean mIsSplitScreenAspectRatioForUnresizableAppsEnabled;
+
LetterboxConfiguration(Context systemUiContext) {
mContext = systemUiContext;
mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
@@ -143,12 +196,24 @@ final class LetterboxConfiguration {
R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
R.dimen.config_letterboxHorizontalPositionMultiplier);
- mIsReachabilityEnabled = mContext.getResources().getBoolean(
- R.bool.config_letterboxIsReachabilityEnabled);
- mDefaultPositionForReachability = readLetterboxReachabilityPositionFromConfig(mContext);
- mLetterboxPositionForReachability = mDefaultPositionForReachability;
+ mLetterboxVerticalPositionMultiplier = mContext.getResources().getFloat(
+ R.dimen.config_letterboxVerticalPositionMultiplier);
+ mIsHorizontalReachabilityEnabled = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsHorizontalReachabilityEnabled);
+ mIsVerticalReachabilityEnabled = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsVerticalReachabilityEnabled);
+ mDefaultPositionForHorizontalReachability =
+ readLetterboxHorizontalReachabilityPositionFromConfig(mContext);
+ mDefaultPositionForVerticalReachability =
+ readLetterboxVerticalReachabilityPositionFromConfig(mContext);
+ mLetterboxPositionForHorizontalReachability = mDefaultPositionForHorizontalReachability;
+ mLetterboxPositionForVerticalReachability = mDefaultPositionForVerticalReachability;
mIsEducationEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsEducationEnabled);
+ setDefaultMinAspectRatioForUnresizableApps(mContext.getResources().getFloat(
+ R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps));
+ mIsSplitScreenAspectRatioForUnresizableAppsEnabled = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled);
}
/**
@@ -157,7 +222,6 @@ final class LetterboxConfiguration {
* com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
* the framework implementation will be used to determine the aspect ratio.
*/
- @VisibleForTesting
void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
mFixedOrientationLetterboxAspectRatio = aspectRatio;
}
@@ -166,7 +230,6 @@ final class LetterboxConfiguration {
* Resets the aspect ratio of letterbox for fixed orientation to {@link
* com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}.
*/
- @VisibleForTesting
void resetFixedOrientationLetterboxAspectRatio() {
mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
@@ -180,6 +243,47 @@ final class LetterboxConfiguration {
}
/**
+ * Resets the min aspect ratio for unresizable apps that are eligible for size compat mode.
+ */
+ void resetDefaultMinAspectRatioForUnresizableApps() {
+ setDefaultMinAspectRatioForUnresizableApps(mContext.getResources().getFloat(
+ R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps));
+ }
+
+ /**
+ * Gets the min aspect ratio for unresizable apps that are eligible for size compat mode.
+ */
+ float getDefaultMinAspectRatioForUnresizableApps() {
+ return mDefaultMinAspectRatioForUnresizableApps;
+ }
+
+ /**
+ * Overrides the min aspect ratio for unresizable apps that are eligible for size compat mode.
+ */
+ void setDefaultMinAspectRatioForUnresizableApps(float aspectRatio) {
+ mDefaultMinAspectRatioForUnresizableApps = aspectRatio;
+ }
+
+ /**
+ * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0,
+ * both it and a value of {@link
+ * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and
+ * corners of the activity won't be rounded.
+ */
+ void setLetterboxActivityCornersRadius(int cornersRadius) {
+ mLetterboxActivityCornersRadius = cornersRadius;
+ }
+
+ /**
+ * Resets corners raidus for activities presented in the letterbox mode to {@link
+ * com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
+ */
+ void resetLetterboxActivityCornersRadius() {
+ mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_letterboxActivityCornersRadius);
+ }
+
+ /**
* Whether corners of letterboxed activities are rounded.
*/
boolean isLetterboxActivityCornersRounded() {
@@ -210,6 +314,34 @@ final class LetterboxConfiguration {
return Color.valueOf(mContext.getResources().getColor(colorId));
}
+
+ /**
+ * Sets color of letterbox background which is used when {@link
+ * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+ * fallback for other backfround types.
+ */
+ void setLetterboxBackgroundColor(Color color) {
+ mLetterboxBackgroundColorOverride = color;
+ }
+
+ /**
+ * Sets color ID of letterbox background which is used when {@link
+ * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+ * fallback for other backfround types.
+ */
+ void setLetterboxBackgroundColorResourceId(int colorId) {
+ mLetterboxBackgroundColorResourceIdOverride = colorId;
+ }
+
+ /**
+ * Resets color of letterbox background to {@link
+ * com.android.internal.R.color.config_letterboxBackgroundColor}.
+ */
+ void resetLetterboxBackgroundColor() {
+ mLetterboxBackgroundColorOverride = null;
+ mLetterboxBackgroundColorResourceIdOverride = null;
+ }
+
/**
* Gets {@link LetterboxBackgroundType} specified in {@link
* com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
@@ -219,6 +351,19 @@ final class LetterboxConfiguration {
return mLetterboxBackgroundType;
}
+ /** Sets letterbox background type. */
+ void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) {
+ mLetterboxBackgroundType = backgroundType;
+ }
+
+ /**
+ * Resets cletterbox background type to {@link
+ * com.android.internal.R.integer.config_letterboxBackgroundType}.
+ */
+ void resetLetterboxBackgroundType() {
+ mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
+ }
+
/** Returns a string representing the given {@link LetterboxBackgroundType}. */
static String letterboxBackgroundTypeToString(
@LetterboxBackgroundType int backgroundType) {
@@ -248,6 +393,27 @@ final class LetterboxConfiguration {
}
/**
+ * Overrides alpha of a black scrim shown over wallpaper for {@link
+ * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}.
+ *
+ * <p>If given value is < 0 or >= 1, both it and a value of {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored
+ * and 0.0 (transparent) is instead.
+ */
+ void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) {
+ mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha;
+ }
+
+ /**
+ * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}.
+ */
+ void resetLetterboxBackgroundWallpaperDarkScrimAlpha() {
+ mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
+ }
+
+ /**
* Gets alpha of a black scrim shown over wallpaper letterbox background.
*/
float getLetterboxBackgroundWallpaperDarkScrimAlpha() {
@@ -255,6 +421,28 @@ final class LetterboxConfiguration {
}
/**
+ * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in
+ * {@link mLetterboxBackgroundType}.
+ *
+ * <p> If given value <= 0, both it and a value of {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored
+ * and 0 is used instead.
+ */
+ void setLetterboxBackgroundWallpaperBlurRadius(int radius) {
+ mLetterboxBackgroundWallpaperBlurRadius = radius;
+ }
+
+ /**
+ * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
+ * mLetterboxBackgroundType} to {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}.
+ */
+ void resetLetterboxBackgroundWallpaperBlurRadius() {
+ mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
+ }
+
+ /**
* Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
* mLetterboxBackgroundType}.
*/
@@ -275,72 +463,187 @@ final class LetterboxConfiguration {
? 0.5f : mLetterboxHorizontalPositionMultiplier;
}
+ /*
+ * Gets vertical position of a center of the letterboxed app window specified
+ * in {@link com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier}
+ * or via an ADB command. 0 corresponds to the top side of the screen and 1 to the
+ * bottom side.
+ */
+ float getLetterboxVerticalPositionMultiplier() {
+ return (mLetterboxVerticalPositionMultiplier < 0.0f
+ || mLetterboxVerticalPositionMultiplier > 1.0f)
+ // Default to central position if invalid value is provided.
+ ? 0.5f : mLetterboxVerticalPositionMultiplier;
+ }
+
/**
* Overrides horizontal position of a center of the letterboxed app window. If given value < 0
* or > 1, then it and a value of {@link
* com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and
* central position (0.5) is used.
*/
- @VisibleForTesting
void setLetterboxHorizontalPositionMultiplier(float multiplier) {
mLetterboxHorizontalPositionMultiplier = multiplier;
}
/**
+ * Overrides vertical position of a center of the letterboxed app window. If given value < 0
+ * or > 1, then it and a value of {@link
+ * com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier} are ignored and
+ * central position (0.5) is used.
+ */
+ void setLetterboxVerticalPositionMultiplier(float multiplier) {
+ mLetterboxVerticalPositionMultiplier = multiplier;
+ }
+
+ /**
* Resets horizontal position of a center of the letterboxed app window to {@link
* com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}.
*/
- @VisibleForTesting
void resetLetterboxHorizontalPositionMultiplier() {
mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier);
}
+ /**
+ * Resets vertical position of a center of the letterboxed app window to {@link
+ * com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier}.
+ */
+ void resetLetterboxVerticalPositionMultiplier() {
+ mLetterboxVerticalPositionMultiplier = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier);
+ }
+
+ /*
+ * Whether horizontal reachability repositioning is allowed for letterboxed fullscreen apps in
+ * landscape device orientation.
+ */
+ boolean getIsHorizontalReachabilityEnabled() {
+ return mIsHorizontalReachabilityEnabled;
+ }
+
/*
- * Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape
- * device orientation.
+ * Whether vertical reachability repositioning is allowed for letterboxed fullscreen apps in
+ * portrait device orientation.
*/
- boolean getIsReachabilityEnabled() {
- return mIsReachabilityEnabled;
+ boolean getIsVerticalReachabilityEnabled() {
+ return mIsVerticalReachabilityEnabled;
}
/**
- * Overrides whether reachability repositioning is allowed for letterboxed fullscreen apps in
- * landscape device orientation.
+ * Overrides whether horizontal reachability repositioning is allowed for letterboxed fullscreen
+ * apps in landscape device orientation.
+ */
+ void setIsHorizontalReachabilityEnabled(boolean enabled) {
+ mIsHorizontalReachabilityEnabled = enabled;
+ }
+
+ /**
+ * Overrides whether vertical reachability repositioning is allowed for letterboxed fullscreen
+ * apps in portrait device orientation.
+ */
+ void setIsVerticalReachabilityEnabled(boolean enabled) {
+ mIsVerticalReachabilityEnabled = enabled;
+ }
+
+ /**
+ * Resets whether horizontal reachability repositioning is allowed for letterboxed fullscreen
+ * apps in landscape device orientation to
+ * {@link R.bool.config_letterboxIsHorizontalReachabilityEnabled}.
*/
- @VisibleForTesting
- void setIsReachabilityEnabled(boolean enabled) {
- mIsReachabilityEnabled = enabled;
+ void resetIsHorizontalReachabilityEnabled() {
+ mIsHorizontalReachabilityEnabled = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsHorizontalReachabilityEnabled);
}
/**
- * Resets whether reachability repositioning is allowed for letterboxed fullscreen apps in
- * landscape device orientation to {@link R.bool.config_letterboxIsReachabilityEnabled}.
+ * Resets whether vertical reachability repositioning is allowed for letterboxed fullscreen apps
+ * in portrait device orientation to
+ * {@link R.bool.config_letterboxIsVerticalReachabilityEnabled}.
*/
- @VisibleForTesting
- void resetIsReachabilityEnabled() {
- mIsReachabilityEnabled = mContext.getResources().getBoolean(
- R.bool.config_letterboxIsReachabilityEnabled);
+ void resetIsVerticalReachabilityEnabled() {
+ mIsVerticalReachabilityEnabled = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsVerticalReachabilityEnabled);
}
/*
- * Gets default horizontal position of the letterboxed app window when reachability is enabled.
- * Specified in {@link R.integer.config_letterboxDefaultPositionForReachability} or via an ADB
- * command.
+ * Gets default horizontal position of the letterboxed app window when horizontal reachability
+ * is enabled.
+ *
+ * <p> Specified in {@link R.integer.config_letterboxDefaultPositionForHorizontalReachability}
+ * or via an ADB command.
+ */
+ @LetterboxHorizontalReachabilityPosition
+ int getDefaultPositionForHorizontalReachability() {
+ return mDefaultPositionForHorizontalReachability;
+ }
+
+ /*
+ * Gets default vertical position of the letterboxed app window when vertical reachability is
+ * enabled.
+ *
+ * <p> Specified in {@link R.integer.config_letterboxDefaultPositionForVerticalReachability} or
+ * via an ADB command.
*/
- @LetterboxReachabilityPosition
- int getDefaultPositionForReachability() {
- return mDefaultPositionForReachability;
+ @LetterboxVerticalReachabilityPosition
+ int getDefaultPositionForVerticalReachability() {
+ return mDefaultPositionForVerticalReachability;
}
- @LetterboxReachabilityPosition
- private static int readLetterboxReachabilityPositionFromConfig(Context context) {
+ /**
+ * Overrides default horizontal position of the letterboxed app window when horizontal
+ * reachability is enabled.
+ */
+ void setDefaultPositionForHorizontalReachability(
+ @LetterboxHorizontalReachabilityPosition int position) {
+ mDefaultPositionForHorizontalReachability = position;
+ }
+
+ /**
+ * Overrides default vertical position of the letterboxed app window when vertical
+ * reachability is enabled.
+ */
+ void setDefaultPositionForVerticalReachability(
+ @LetterboxVerticalReachabilityPosition int position) {
+ mDefaultPositionForVerticalReachability = position;
+ }
+
+ /**
+ * Resets default horizontal position of the letterboxed app window when horizontal reachability
+ * is enabled to {@link R.integer.config_letterboxDefaultPositionForHorizontalReachability}.
+ */
+ void resetDefaultPositionForHorizontalReachability() {
+ mDefaultPositionForHorizontalReachability =
+ readLetterboxHorizontalReachabilityPositionFromConfig(mContext);
+ }
+
+ /**
+ * Resets default vertical position of the letterboxed app window when vertical reachability
+ * is enabled to {@link R.integer.config_letterboxDefaultPositionForVerticalReachability}.
+ */
+ void resetDefaultPositionForVerticalReachability() {
+ mDefaultPositionForVerticalReachability =
+ readLetterboxVerticalReachabilityPositionFromConfig(mContext);
+ }
+
+ @LetterboxHorizontalReachabilityPosition
+ private static int readLetterboxHorizontalReachabilityPositionFromConfig(Context context) {
+ int position = context.getResources().getInteger(
+ R.integer.config_letterboxDefaultPositionForHorizontalReachability);
+ return position == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT
+ || position == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER
+ || position == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT
+ ? position : LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
+ }
+
+ @LetterboxVerticalReachabilityPosition
+ private static int readLetterboxVerticalReachabilityPositionFromConfig(Context context) {
int position = context.getResources().getInteger(
- R.integer.config_letterboxDefaultPositionForReachability);
- return position == LETTERBOX_REACHABILITY_POSITION_LEFT
- || position == LETTERBOX_REACHABILITY_POSITION_CENTER
- || position == LETTERBOX_REACHABILITY_POSITION_RIGHT
- ? position : LETTERBOX_REACHABILITY_POSITION_CENTER;
+ R.integer.config_letterboxDefaultPositionForVerticalReachability);
+ return position == LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP
+ || position == LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER
+ || position == LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM
+ ? position : LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
}
/*
@@ -350,48 +653,126 @@ final class LetterboxConfiguration {
* <p>The position multiplier is changed after each double tap in the letterbox area.
*/
float getHorizontalMultiplierForReachability() {
- switch (mLetterboxPositionForReachability) {
- case LETTERBOX_REACHABILITY_POSITION_LEFT:
+ switch (mLetterboxPositionForHorizontalReachability) {
+ case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT:
+ return 0.0f;
+ case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER:
+ return 0.5f;
+ case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT:
+ return 1.0f;
+ default:
+ throw new AssertionError(
+ "Unexpected letterbox position type: "
+ + mLetterboxPositionForHorizontalReachability);
+ }
+ }
+ /*
+ * Gets vertical position of a center of the letterboxed app window when reachability
+ * is enabled specified. 0 corresponds to the top side of the screen and 1 to the bottom side.
+ *
+ * <p>The position multiplier is changed after each double tap in the letterbox area.
+ */
+ float getVerticalMultiplierForReachability() {
+ switch (mLetterboxPositionForVerticalReachability) {
+ case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP:
return 0.0f;
- case LETTERBOX_REACHABILITY_POSITION_CENTER:
+ case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER:
return 0.5f;
- case LETTERBOX_REACHABILITY_POSITION_RIGHT:
+ case LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM:
return 1.0f;
default:
throw new AssertionError(
- "Unexpected letterbox position type: " + mLetterboxPositionForReachability);
+ "Unexpected letterbox position type: "
+ + mLetterboxPositionForVerticalReachability);
}
}
- /** Returns a string representing the given {@link LetterboxReachabilityPosition}. */
- static String letterboxReachabilityPositionToString(
- @LetterboxReachabilityPosition int position) {
+ /*
+ * Gets the horizontal position of the letterboxed app window when horizontal reachability is
+ * enabled.
+ */
+ @LetterboxHorizontalReachabilityPosition
+ int getLetterboxPositionForHorizontalReachability() {
+ return mLetterboxPositionForHorizontalReachability;
+ }
+
+ /*
+ * Gets the vertical position of the letterboxed app window when vertical reachability is
+ * enabled.
+ */
+ @LetterboxVerticalReachabilityPosition
+ int getLetterboxPositionForVerticalReachability() {
+ return mLetterboxPositionForVerticalReachability;
+ }
+
+ /** Returns a string representing the given {@link LetterboxHorizontalReachabilityPosition}. */
+ static String letterboxHorizontalReachabilityPositionToString(
+ @LetterboxHorizontalReachabilityPosition int position) {
switch (position) {
- case LETTERBOX_REACHABILITY_POSITION_LEFT:
- return "LETTERBOX_REACHABILITY_POSITION_LEFT";
- case LETTERBOX_REACHABILITY_POSITION_CENTER:
- return "LETTERBOX_REACHABILITY_POSITION_CENTER";
- case LETTERBOX_REACHABILITY_POSITION_RIGHT:
- return "LETTERBOX_REACHABILITY_POSITION_RIGHT";
+ case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT:
+ return "LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT";
+ case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER:
+ return "LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER";
+ case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT:
+ return "LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT";
default:
throw new AssertionError(
"Unexpected letterbox position type: " + position);
}
}
+ /** Returns a string representing the given {@link LetterboxVerticalReachabilityPosition}. */
+ static String letterboxVerticalReachabilityPositionToString(
+ @LetterboxVerticalReachabilityPosition int position) {
+ switch (position) {
+ case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP:
+ return "LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP";
+ case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER:
+ return "LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER";
+ case LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM:
+ return "LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM";
+ default:
+ throw new AssertionError(
+ "Unexpected letterbox position type: " + position);
+ }
+ }
+
+ /**
+ * Changes letterbox position for horizontal reachability to the next available one on the
+ * right side.
+ */
+ void movePositionForHorizontalReachabilityToNextRightStop() {
+ mLetterboxPositionForHorizontalReachability = Math.min(
+ mLetterboxPositionForHorizontalReachability + 1,
+ LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT);
+ }
+
+ /**
+ * Changes letterbox position for horizontal reachability to the next available one on the left
+ * side.
+ */
+ void movePositionForHorizontalReachabilityToNextLeftStop() {
+ mLetterboxPositionForHorizontalReachability =
+ Math.max(mLetterboxPositionForHorizontalReachability - 1, 0);
+ }
+
/**
- * Changes letterbox position for reachability to the next available one on the right side.
+ * Changes letterbox position for vertical reachability to the next available one on the bottom
+ * side.
*/
- void movePositionForReachabilityToNextRightStop() {
- mLetterboxPositionForReachability = Math.min(
- mLetterboxPositionForReachability + 1, LETTERBOX_REACHABILITY_POSITION_RIGHT);
+ void movePositionForVerticalReachabilityToNextBottomStop() {
+ mLetterboxPositionForVerticalReachability = Math.min(
+ mLetterboxPositionForVerticalReachability + 1,
+ LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM);
}
/**
- * Changes letterbox position for reachability to the next available one on the left side.
+ * Changes letterbox position for vertical reachability to the next available one on the top
+ * side.
*/
- void movePositionForReachabilityToNextLeftStop() {
- mLetterboxPositionForReachability = Math.max(mLetterboxPositionForReachability - 1, 0);
+ void movePositionForVerticalReachabilityToNextTopStop() {
+ mLetterboxPositionForVerticalReachability =
+ Math.max(mLetterboxPositionForVerticalReachability - 1, 0);
}
/**
@@ -404,8 +785,41 @@ final class LetterboxConfiguration {
/**
* Overrides whether education is allowed for letterboxed fullscreen apps.
*/
- @VisibleForTesting
void setIsEducationEnabled(boolean enabled) {
mIsEducationEnabled = enabled;
}
+
+ /**
+ * Resets whether education is allowed for letterboxed fullscreen apps to
+ * {@link R.bool.config_letterboxIsEducationEnabled}.
+ */
+ void resetIsEducationEnabled() {
+ mIsEducationEnabled = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsEducationEnabled);
+ }
+
+ /**
+ * Whether using split screen aspect ratio as a default aspect ratio for unresizable apps.
+ */
+ boolean getIsSplitScreenAspectRatioForUnresizableAppsEnabled() {
+ return mIsSplitScreenAspectRatioForUnresizableAppsEnabled;
+ }
+
+ /**
+ * Overrides whether using split screen aspect ratio as a default aspect ratio for unresizable
+ * apps.
+ */
+ void setIsSplitScreenAspectRatioForUnresizableAppsEnabled(boolean enabled) {
+ mIsSplitScreenAspectRatioForUnresizableAppsEnabled = enabled;
+ }
+
+ /**
+ * Resets whether using split screen aspect ratio as a default aspect ratio for unresizable
+ * apps {@link R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled}.
+ */
+ void resetIsSplitScreenAspectRatioForUnresizableAppsEnabled() {
+ mIsSplitScreenAspectRatioForUnresizableAppsEnabled = mContext.getResources().getBoolean(
+ R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled);
+ }
+
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index bb15d76c3bac..d65276793700 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -21,6 +21,20 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__CENTER;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__LEFT;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__RIGHT;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__TOP;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__UNKNOWN_POSITION;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__BOTTOM_TO_CENTER;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_BOTTOM;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_LEFT;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_RIGHT;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_TOP;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__LEFT_TO_CENTER;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__RIGHT_TO_CENTER;
+import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__TOP_TO_CENTER;
import static com.android.server.wm.ActivityRecord.computeAspectRatio;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -28,6 +42,13 @@ import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
+import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
import static com.android.server.wm.LetterboxConfiguration.letterboxBackgroundTypeToString;
import android.annotation.Nullable;
@@ -163,7 +184,8 @@ final class LetterboxUiController {
this::hasWallpaperBackgroudForLetterbox,
this::getLetterboxWallpaperBlurRadius,
this::getLetterboxWallpaperDarkScrimAlpha,
- this::handleDoubleTap);
+ this::handleHorizontalDoubleTap,
+ this::handleVerticalDoubleTap);
mLetterbox.attachInput(w);
}
mActivityRecord.getPosition(mTmpPoint);
@@ -193,18 +215,36 @@ final class LetterboxUiController {
float getHorizontalPositionMultiplier(Configuration parentConfiguration) {
// Don't check resolved configuration because it may not be updated yet during
// configuration change.
- return isReachabilityEnabled(parentConfiguration)
+ return isHorizontalReachabilityEnabled(parentConfiguration)
// Using the last global dynamic position to avoid "jumps" when moving
// between apps or activities.
? mLetterboxConfiguration.getHorizontalMultiplierForReachability()
: mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier();
}
- float getFixedOrientationLetterboxAspectRatio(Configuration parentConfiguration) {
- // Don't check resolved windowing mode because it may not be updated yet during
+ float getVerticalPositionMultiplier(Configuration parentConfiguration) {
+ // Don't check resolved configuration because it may not be updated yet during
// configuration change.
- if (!isReachabilityEnabled(parentConfiguration)) {
- return mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
+ return isVerticalReachabilityEnabled(parentConfiguration)
+ // Using the last global dynamic position to avoid "jumps" when moving
+ // between apps or activities.
+ ? mLetterboxConfiguration.getVerticalMultiplierForReachability()
+ : mLetterboxConfiguration.getLetterboxVerticalPositionMultiplier();
+ }
+
+ float getFixedOrientationLetterboxAspectRatio() {
+ return mActivityRecord.shouldCreateCompatDisplayInsets()
+ ? getDefaultMinAspectRatioForUnresizableApps()
+ : mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
+ }
+
+ private float getDefaultMinAspectRatioForUnresizableApps() {
+ if (!mLetterboxConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled()
+ || mActivityRecord.getDisplayContent() == null) {
+ return mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
+ > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
+ ? mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
+ : mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
}
int dividerWindowWidth =
@@ -214,10 +254,14 @@ final class LetterboxUiController {
int dividerSize = dividerWindowWidth - dividerInsets * 2;
// Getting the same aspect ratio that apps get in split screen.
- Rect bounds = new Rect(parentConfiguration.windowConfiguration.getAppBounds());
- bounds.inset(dividerSize, /* dy */ 0);
- bounds.right = bounds.centerX();
-
+ Rect bounds = new Rect(mActivityRecord.getDisplayContent().getBounds());
+ if (bounds.width() >= bounds.height()) {
+ bounds.inset(/* dx */ dividerSize / 2, /* dy */ 0);
+ bounds.right = bounds.centerX();
+ } else {
+ bounds.inset(/* dx */ 0, /* dy */ dividerSize / 2);
+ bounds.bottom = bounds.centerY();
+ }
return computeAspectRatio(bounds);
}
@@ -225,8 +269,8 @@ final class LetterboxUiController {
return mActivityRecord.mWmService.mContext.getResources();
}
- private void handleDoubleTap(int x) {
- if (!isReachabilityEnabled() || mActivityRecord.isInTransition()) {
+ private void handleHorizontalDoubleTap(int x) {
+ if (!isHorizontalReachabilityEnabled() || mActivityRecord.isInTransition()) {
return;
}
@@ -235,12 +279,61 @@ final class LetterboxUiController {
return;
}
+ int letterboxPositionForHorizontalReachability = mLetterboxConfiguration
+ .getLetterboxPositionForHorizontalReachability();
if (mLetterbox.getInnerFrame().left > x) {
// Moving to the next stop on the left side of the app window: right > center > left.
- mLetterboxConfiguration.movePositionForReachabilityToNextLeftStop();
+ mLetterboxConfiguration.movePositionForHorizontalReachabilityToNextLeftStop();
+ int changeToLog =
+ letterboxPositionForHorizontalReachability
+ == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER
+ ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_LEFT
+ : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__RIGHT_TO_CENTER;
+ logLetterboxPositionChange(changeToLog);
} else if (mLetterbox.getInnerFrame().right < x) {
// Moving to the next stop on the right side of the app window: left > center > right.
- mLetterboxConfiguration.movePositionForReachabilityToNextRightStop();
+ mLetterboxConfiguration.movePositionForHorizontalReachabilityToNextRightStop();
+ int changeToLog =
+ letterboxPositionForHorizontalReachability
+ == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER
+ ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_RIGHT
+ : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__LEFT_TO_CENTER;
+ logLetterboxPositionChange(changeToLog);
+ }
+
+ // TODO(197549949): Add animation for transition.
+ mActivityRecord.recomputeConfiguration();
+ }
+
+ private void handleVerticalDoubleTap(int y) {
+ if (!isVerticalReachabilityEnabled() || mActivityRecord.isInTransition()) {
+ return;
+ }
+
+ if (mLetterbox.getInnerFrame().top <= y && mLetterbox.getInnerFrame().bottom >= y) {
+ // Only react to clicks at the top and bottom of the letterboxed app window.
+ return;
+ }
+ int letterboxPositionForVerticalReachability = mLetterboxConfiguration
+ .getLetterboxPositionForVerticalReachability();
+ if (mLetterbox.getInnerFrame().top > y) {
+ // Moving to the next stop on the top side of the app window: bottom > center > top.
+ mLetterboxConfiguration.movePositionForVerticalReachabilityToNextTopStop();
+ int changeToLog =
+ letterboxPositionForVerticalReachability
+ == LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER
+ ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_TOP
+ : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__BOTTOM_TO_CENTER;
+ logLetterboxPositionChange(changeToLog);
+ } else if (mLetterbox.getInnerFrame().bottom < y) {
+ // Moving to the next stop on the bottom side of the app window: top > center > bottom.
+ mLetterboxConfiguration.movePositionForVerticalReachabilityToNextBottomStop();
+ int changeToLog =
+ letterboxPositionForVerticalReachability
+ == LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER
+ ? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_BOTTOM
+ : LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__TOP_TO_CENTER;
+ logLetterboxPositionChange(changeToLog);
}
// TODO(197549949): Add animation for transition.
@@ -248,25 +341,47 @@ final class LetterboxUiController {
}
/**
- * Whether reachability is enabled for an activity in the curren configuration.
+ * Whether horizontal reachability is enabled for an activity in the current configuration.
*
* <p>Conditions that needs to be met:
* <ul>
* <li>Activity is portrait-only.
* <li>Fullscreen window in landscape device orientation.
- * <li>Reachability is enabled.
+ * <li>Horizontal Reachability is enabled.
+ * </ul>
+ */
+ private boolean isHorizontalReachabilityEnabled(Configuration parentConfiguration) {
+ return mLetterboxConfiguration.getIsHorizontalReachabilityEnabled()
+ && parentConfiguration.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_FULLSCREEN
+ && (parentConfiguration.orientation == ORIENTATION_LANDSCAPE
+ && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_PORTRAIT);
+ }
+
+ private boolean isHorizontalReachabilityEnabled() {
+ return isHorizontalReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
+ }
+
+ /**
+ * Whether vertical reachability is enabled for an activity in the current configuration.
+ *
+ * <p>Conditions that needs to be met:
+ * <ul>
+ * <li>Activity is landscape-only.
+ * <li>Fullscreen window in portrait device orientation.
+ * <li>Vertical Reachability is enabled.
* </ul>
*/
- private boolean isReachabilityEnabled(Configuration parentConfiguration) {
- return mLetterboxConfiguration.getIsReachabilityEnabled()
+ private boolean isVerticalReachabilityEnabled(Configuration parentConfiguration) {
+ return mLetterboxConfiguration.getIsVerticalReachabilityEnabled()
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
- && parentConfiguration.orientation == ORIENTATION_LANDSCAPE
- && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_PORTRAIT;
+ && (parentConfiguration.orientation == ORIENTATION_PORTRAIT
+ && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_LANDSCAPE);
}
- private boolean isReachabilityEnabled() {
- return isReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
+ private boolean isVerticalReachabilityEnabled() {
+ return isVerticalReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
}
@VisibleForTesting
@@ -474,12 +589,19 @@ final class LetterboxUiController {
+ getLetterboxWallpaperBlurRadius());
}
- pw.println(prefix + " isReachabilityEnabled=" + isReachabilityEnabled());
+ pw.println(prefix + " isHorizontalReachabilityEnabled="
+ + isHorizontalReachabilityEnabled());
+ pw.println(prefix + " isVerticalReachabilityEnabled=" + isVerticalReachabilityEnabled());
pw.println(prefix + " letterboxHorizontalPositionMultiplier="
+ getHorizontalPositionMultiplier(mActivityRecord.getParent().getConfiguration()));
+ pw.println(prefix + " letterboxVerticalPositionMultiplier="
+ + getVerticalPositionMultiplier(mActivityRecord.getParent().getConfiguration()));
pw.println(prefix + " fixedOrientationLetterboxAspectRatio="
- + getFixedOrientationLetterboxAspectRatio(
- mActivityRecord.getParent().getConfiguration()));
+ + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
+ pw.println(prefix + " defaultMinAspectRatioForUnresizableApps="
+ + mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps());
+ pw.println(prefix + " isSplitScreenAspectRatioForUnresizableAppsEnabled="
+ + mLetterboxConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
}
/**
@@ -496,7 +618,69 @@ final class LetterboxUiController {
if (mainWin.isLetterboxedForDisplayCutout()) {
return "DISPLAY_CUTOUT";
}
+ if (mActivityRecord.isAspectRatioApplied()) {
+ return "ASPECT_RATIO";
+ }
return "UNKNOWN_REASON";
}
+ private int letterboxHorizontalReachabilityPositionToLetterboxPosition(
+ @LetterboxConfiguration.LetterboxHorizontalReachabilityPosition int position) {
+ switch (position) {
+ case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT:
+ return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__LEFT;
+ case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER:
+ return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__CENTER;
+ case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT:
+ return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__RIGHT;
+ default:
+ throw new AssertionError(
+ "Unexpected letterbox horizontal reachability position type: "
+ + position);
+ }
+ }
+
+ private int letterboxVerticalReachabilityPositionToLetterboxPosition(
+ @LetterboxConfiguration.LetterboxVerticalReachabilityPosition int position) {
+ switch (position) {
+ case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP:
+ return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__TOP;
+ case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER:
+ return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__CENTER;
+ case LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM:
+ return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM;
+ default:
+ throw new AssertionError(
+ "Unexpected letterbox vertical reachability position type: "
+ + position);
+ }
+ }
+
+ int getLetterboxPositionForLogging() {
+ int positionToLog = APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__UNKNOWN_POSITION;
+ if (isHorizontalReachabilityEnabled()) {
+ int letterboxPositionForHorizontalReachability = getLetterboxConfiguration()
+ .getLetterboxPositionForHorizontalReachability();
+ positionToLog = letterboxHorizontalReachabilityPositionToLetterboxPosition(
+ letterboxPositionForHorizontalReachability);
+ } else if (isVerticalReachabilityEnabled()) {
+ int letterboxPositionForVerticalReachability = getLetterboxConfiguration()
+ .getLetterboxPositionForVerticalReachability();
+ positionToLog = letterboxVerticalReachabilityPositionToLetterboxPosition(
+ letterboxPositionForVerticalReachability);
+ }
+ return positionToLog;
+ }
+
+ private LetterboxConfiguration getLetterboxConfiguration() {
+ return mLetterboxConfiguration;
+ }
+
+ /**
+ * Logs letterbox position changes via {@link ActivityMetricsLogger#logLetterboxPositionChange}.
+ */
+ private void logLetterboxPositionChange(int letterboxPositionChange) {
+ mActivityRecord.mTaskSupervisor.getActivityMetricsLogger()
+ .logLetterboxPositionChange(mActivityRecord, letterboxPositionChange);
+ }
}
diff --git a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
index 9c1d5601dad5..64749cf94ddf 100644
--- a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
+++ b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
@@ -22,17 +22,21 @@ import static com.android.internal.R.bool.config_unfoldTransitionEnabled;
import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager;
import android.os.HandlerExecutor;
+import android.window.DisplayAreaInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
public class PhysicalDisplaySwitchTransitionLauncher {
private final DisplayContent mDisplayContent;
+ private final WindowManagerService mService;
private final DeviceStateManager mDeviceStateManager;
- private final Context mContext;
private final TransitionController mTransitionController;
private DeviceStateListener mDeviceStateListener;
@@ -46,13 +50,13 @@ public class PhysicalDisplaySwitchTransitionLauncher {
public PhysicalDisplaySwitchTransitionLauncher(DisplayContent displayContent,
TransitionController transitionController) {
mDisplayContent = displayContent;
- mContext = mDisplayContent.mWmService.mContext;
+ mService = displayContent.mWmService;
mTransitionController = transitionController;
- mDeviceStateManager = mContext.getSystemService(DeviceStateManager.class);
+ mDeviceStateManager = mService.mContext.getSystemService(DeviceStateManager.class);
if (mDeviceStateManager != null) {
- mDeviceStateListener = new DeviceStateListener(mContext);
+ mDeviceStateListener = new DeviceStateListener(mService.mContext);
mDeviceStateManager
.registerCallback(new HandlerExecutor(mDisplayContent.mWmService.mH),
mDeviceStateListener);
@@ -74,7 +78,7 @@ public class PhysicalDisplaySwitchTransitionLauncher {
if (!mDisplayContent.getLastHasContent()) return;
boolean shouldRequestUnfoldTransition = !mIsFolded
- && mContext.getResources().getBoolean(config_unfoldTransitionEnabled)
+ && mService.mContext.getResources().getBoolean(config_unfoldTransitionEnabled)
&& ValueAnimator.areAnimatorsEnabled();
if (!shouldRequestUnfoldTransition) {
@@ -97,16 +101,47 @@ public class PhysicalDisplaySwitchTransitionLauncher {
if (t != null) {
mDisplayContent.mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
- mTransitionController.collectForDisplayChange(mDisplayContent, t);
+ mTransitionController.collectForDisplayAreaChange(mDisplayContent, t);
mTransition = t;
}
}
- public void onDisplayUpdated() {
- if (mTransition != null) {
- mTransition.setAllReady();
- mTransition = null;
+ /**
+ * Called when physical display is getting updated, this could happen e.g. on foldable
+ * devices when the physical underlying display is replaced.
+ *
+ * @param fromRotation rotation before the display change
+ * @param toRotation rotation after the display change
+ * @param newDisplayAreaInfo display area info after the display change
+ */
+ public void onDisplayUpdated(int fromRotation, int toRotation,
+ @NonNull DisplayAreaInfo newDisplayAreaInfo) {
+ if (mTransition == null) return;
+
+ final boolean started = mDisplayContent.mRemoteDisplayChangeController
+ .performRemoteDisplayChange(fromRotation, toRotation, newDisplayAreaInfo,
+ this::continueDisplayUpdate);
+
+ if (!started) {
+ markTransitionAsReady();
+ }
+ }
+
+ private void continueDisplayUpdate(@Nullable WindowContainerTransaction transaction) {
+ if (mTransition == null) return;
+
+ if (transaction != null) {
+ mService.mAtmService.mWindowOrganizerController.applyTransaction(transaction);
}
+
+ markTransitionAsReady();
+ }
+
+ private void markTransitionAsReady() {
+ if (mTransition == null) return;
+
+ mTransition.setAllReady();
+ mTransition = null;
}
class DeviceStateListener extends DeviceStateManager.FoldStateListener {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index eca201dc2bda..ffe3374e6658 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -203,9 +203,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan
final LaunchingState launchingState =
mTaskSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent);
- if (mCaller != null) {
- mCaller.setRunningRecentsAnimation(true);
- }
+ setProcessAnimating(true);
mService.deferWindowLayout();
try {
@@ -409,15 +407,33 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan
if (mWindowManager.mRoot.isLayoutNeeded()) {
mWindowManager.mRoot.performSurfacePlacement();
}
- if (mCaller != null) {
- mCaller.setRunningRecentsAnimation(false);
- }
+ setProcessAnimating(false);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
});
}
}
+ /** Gives the owner of recents animation higher priority. */
+ private void setProcessAnimating(boolean animating) {
+ if (mCaller == null) return;
+ // Apply the top-app scheduling group to who runs the animation.
+ mCaller.setRunningRecentsAnimation(animating);
+ int demoteReasons = mService.mDemoteTopAppReasons;
+ if (animating) {
+ demoteReasons |= ActivityTaskManagerService.DEMOTE_TOP_REASON_ANIMATING_RECENTS;
+ } else {
+ demoteReasons &= ~ActivityTaskManagerService.DEMOTE_TOP_REASON_ANIMATING_RECENTS;
+ }
+ mService.mDemoteTopAppReasons = demoteReasons;
+ // Make the demotion of the real top app take effect. No need to restore top app state for
+ // finishing recents because addToStopping -> scheduleIdle -> activityIdleInternal ->
+ // trimApplications will have a full update.
+ if (animating && mService.mTopApp != null) {
+ mService.mTopApp.scheduleUpdateOomAdj();
+ }
+ }
+
@Override
public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode,
boolean sendUserLeaveHint) {
@@ -504,7 +520,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan
}
private boolean matchesTarget(Task task) {
- return task.mUserId == mUserId
+ return task.getNonFinishingActivityCount() > 0 && task.mUserId == mUserId
&& task.getBaseIntent().getComponent().equals(mTargetIntent.getComponent());
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index fe0ab2b39909..53f1fe6abec5 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -70,10 +70,7 @@ import android.window.PictureInPictureSurfaceTransaction;
import android.window.TaskSnapshot;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.inputmethod.SoftInputShowHideReason;
-import com.android.internal.os.BackgroundThread;
import com.android.internal.protolog.common.ProtoLog;
-import com.android.internal.util.LatencyTracker;
import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
@@ -99,11 +96,6 @@ import java.util.stream.Collectors;
public class RecentsAnimationController implements DeathRecipient {
private static final String TAG = RecentsAnimationController.class.getSimpleName();
private static final long FAILSAFE_DELAY = 1000;
- /**
- * If the recents animation is canceled before the delay since the window drawn, do not log the
- * action because the duration is too small that may be just a mistouch.
- */
- private static final long LATENCY_TRACKER_LOG_DELAY_MS = 300;
// Constant for a yet-to-be-calculated {@link RemoteAnimationTarget#Mode} state
private static final int MODE_UNKNOWN = -1;
@@ -144,7 +136,7 @@ public class RecentsAnimationController implements DeathRecipient {
private boolean mPendingStart = true;
// Set when the animation has been canceled
- private volatile boolean mCanceled;
+ private boolean mCanceled;
// Whether or not the input consumer is enabled. The input consumer must be both registered and
// enabled for it to start intercepting touch events.
@@ -333,26 +325,6 @@ public class RecentsAnimationController implements DeathRecipient {
}
}
InputMethodManagerInternal.get().maybeFinishStylusHandwriting();
- if (!behindSystemBars) {
- // Hiding IME if IME window is not attached to app.
- // Since some windowing mode is not proper to snapshot Task with IME window
- // while the app transitioning to the next task (e.g. split-screen mode)
- if (!mDisplayContent.isImeAttachedToApp()) {
- final InputMethodManagerInternal inputMethodManagerInternal =
- LocalServices.getService(InputMethodManagerInternal.class);
- if (inputMethodManagerInternal != null) {
- inputMethodManagerInternal.hideCurrentInputMethod(
- SoftInputShowHideReason.HIDE_RECENTS_ANIMATION);
- }
- } else {
- // Disable IME icon explicitly when IME attached to the app in case
- // IME icon might flickering while swiping to the next app task still
- // in animating before the next app window focused, or IME icon
- // persists on the bottom when swiping the task to recents.
- InputMethodManagerInternal.get().updateImeWindowStatus(
- true /* disableImeIcon */);
- }
- }
mService.mWindowPlacerLocked.requestTraversal();
}
} finally {
@@ -380,10 +352,6 @@ public class RecentsAnimationController implements DeathRecipient {
}
}
- // TODO(b/166736352): Remove this method without the need to expose to launcher.
- @Override
- public void hideCurrentInputMethod() { }
-
@Override
public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
synchronized (mService.mGlobalLock) {
@@ -785,15 +753,6 @@ public class RecentsAnimationController implements DeathRecipient {
}, false /* traverseTopToBottom */);
}
- void logRecentsAnimationStartTime(int durationMs) {
- BackgroundThread.getHandler().postDelayed(() -> {
- if (!mCanceled) {
- mService.mLatencyTracker.logAction(LatencyTracker.ACTION_START_RECENTS_ANIMATION,
- durationMs);
- }
- }, LATENCY_TRACKER_LOG_DELAY_MS);
- }
-
private boolean removeTaskInternal(int taskId) {
boolean result = false;
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
@@ -1335,16 +1294,16 @@ public class RecentsAnimationController implements DeathRecipient {
mDisplayContent.mPinnedTaskController.setEnterPipTransaction(
mFinishTransaction);
}
- mFinishTransaction = null;
- mFinishOverlay = null;
- pendingTransaction.apply();
-
- // In the case where we are transferring the transform to the task in preparation
+ // In the case where we are transferring the transform to the task in preparation
// for entering PIP, we disable the task being able to affect sysui flags otherwise
// it may cause a flash
- if (mTask.getActivityType() != mTargetActivityType) {
+ if (mTask.getActivityType() != mTargetActivityType
+ && mFinishTransaction.getShouldDisableCanAffectSystemUiFlags()) {
mTask.setCanAffectSystemUiFlags(false);
}
+ mFinishTransaction = null;
+ mFinishOverlay = null;
+ pendingTransaction.apply();
} else if (!mTask.isAttached()) {
// Apply the task's pending transaction in case it is detached and its transaction
// is not reachable.
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 871b4d8062c2..ac1a2b17603a 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -22,6 +22,7 @@ import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Point;
@@ -57,7 +58,7 @@ import java.util.function.Consumer;
*/
class RemoteAnimationController implements DeathRecipient {
private static final String TAG = TAG_WITH_CLASS_NAME
- ? "RemoteAnimationController" : TAG_WM;
+ ? "RemoteAnimationController" : TAG_WM;
private static final long TIMEOUT_MS = 10000;
private final WindowManagerService mService;
@@ -72,35 +73,40 @@ class RemoteAnimationController implements DeathRecipient {
private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable");
private FinishedCallback mFinishedCallback;
+ private final boolean mIsActivityEmbedding;
private boolean mCanceled;
private boolean mLinkedToDeathOfRunner;
@Nullable
private Runnable mOnRemoteAnimationReady;
RemoteAnimationController(WindowManagerService service, DisplayContent displayContent,
- RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
+ RemoteAnimationAdapter remoteAnimationAdapter, Handler handler,
+ boolean isActivityEmbedding) {
mService = service;
mDisplayContent = displayContent;
mRemoteAnimationAdapter = remoteAnimationAdapter;
mHandler = handler;
+ mIsActivityEmbedding = isActivityEmbedding;
}
/**
* Creates an animation record for each individual {@link WindowContainer}.
*
* @param windowContainer The windows to animate.
- * @param position The position app bounds relative to its parent.
- * @param localBounds The bounds of the app relative to its parent.
- * @param endBounds The end bounds after the transition, in screen coordinates.
- * @param startBounds The start bounds before the transition, in screen coordinates.
+ * @param position The position app bounds relative to its parent.
+ * @param localBounds The bounds of the app relative to its parent.
+ * @param endBounds The end bounds after the transition, in screen coordinates.
+ * @param startBounds The start bounds before the transition, in screen coordinates.
+ * @param showBackdrop To show background behind a window during animation.
* @return The record representing animation(s) to run on the app.
*/
RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
- Point position, Rect localBounds, Rect endBounds, Rect startBounds) {
+ Point position, Rect localBounds, Rect endBounds, Rect startBounds,
+ boolean showBackdrop) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s",
windowContainer);
final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position,
- localBounds, endBounds, startBounds);
+ localBounds, endBounds, startBounds, showBackdrop);
mPendingAnimations.add(adapters);
return adapters;
}
@@ -111,6 +117,15 @@ class RemoteAnimationController implements DeathRecipient {
}
/**
+ * We use isFromActivityEmbedding() in the server process to tell if we're running an
+ * Activity Embedding type remote animation, where animations are driven by the client.
+ * This is currently supporting features like showBackdrop where we need to load App XML.
+ */
+ public boolean isFromActivityEmbedding() {
+ return mIsActivityEmbedding;
+ }
+
+ /**
* Called when the transition is ready to be started, and all leashes have been set up.
*/
void goodToGo(@WindowManager.TransitionOldType int transit) {
@@ -418,30 +433,37 @@ class RemoteAnimationController implements DeathRecipient {
RemoteAnimationTarget mTarget;
final WindowContainer mWindowContainer;
final Rect mStartBounds;
+ final boolean mShowBackdrop;
+ @ColorInt int mBackdropColor = 0;
private @RemoteAnimationTarget.Mode int mMode = RemoteAnimationTarget.MODE_CHANGING;
RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
- Rect endBounds, Rect startBounds) {
+ Rect endBounds, Rect startBounds, boolean showBackdrop) {
mWindowContainer = windowContainer;
+ mShowBackdrop = showBackdrop;
if (startBounds != null) {
mStartBounds = new Rect(startBounds);
mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
- mStartBounds);
+ mStartBounds, mShowBackdrop);
if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
final Rect thumbnailLocalBounds = new Rect(startBounds);
thumbnailLocalBounds.offsetTo(0, 0);
// Snapshot is located at (0,0) of the animation leash. It doesn't have size
// change, so the startBounds is its end bounds, and no start bounds for it.
mThumbnailAdapter = new RemoteAnimationAdapterWrapper(this, new Point(0, 0),
- thumbnailLocalBounds, startBounds, new Rect());
+ thumbnailLocalBounds, startBounds, new Rect(), mShowBackdrop);
}
} else {
mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
- new Rect());
+ new Rect(), mShowBackdrop);
mStartBounds = null;
}
}
+ void setBackDropColor(@ColorInt int backdropColor) {
+ mBackdropColor = backdropColor;
+ }
+
RemoteAnimationTarget createRemoteAnimationTarget() {
if (mAdapter == null
|| mAdapter.mCapturedFinishCallback == null
@@ -483,14 +505,27 @@ class RemoteAnimationController implements DeathRecipient {
final Rect mLocalBounds;
final Rect mEndBounds = new Rect();
final Rect mStartBounds = new Rect();
+ final boolean mShowBackdrop;
RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
- Rect localBounds, Rect endBounds, Rect startBounds) {
+ Rect localBounds, Rect endBounds, Rect startBounds, boolean showBackdrop) {
mRecord = record;
mPosition.set(position.x, position.y);
mLocalBounds = localBounds;
mEndBounds.set(endBounds);
mStartBounds.set(startBounds);
+ mShowBackdrop = showBackdrop;
+ }
+
+ @Override
+ @ColorInt
+ public int getBackgroundColor() {
+ return mRecord.mBackdropColor;
+ }
+
+ @Override
+ public boolean getShowBackground() {
+ return mShowBackdrop;
}
@Override
diff --git a/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
new file mode 100644
index 000000000000..43baebc7255a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.IDisplayChangeWindowCallback;
+import android.window.DisplayAreaInfo;
+import android.window.WindowContainerTransaction;
+
+import com.android.internal.protolog.common.ProtoLog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A helper class, a wrapper around {@link android.view.IDisplayChangeWindowController} to perform
+ * a synchronous display change in other parts (e.g. in the Shell) and continue the process
+ * in the system server. It handles timeouts and multiple requests.
+ * We have an instance of this controller for each display.
+ */
+public class RemoteDisplayChangeController {
+
+ private static final String TAG = "RemoteDisplayChangeController";
+
+ private static final int REMOTE_DISPLAY_CHANGE_TIMEOUT_MS = 800;
+
+ private final WindowManagerService mService;
+ private final int mDisplayId;
+
+ private final Runnable mTimeoutRunnable = this::onContinueTimedOut;
+
+ // all remote changes that haven't finished yet.
+ private final List<ContinueRemoteDisplayChangeCallback> mCallbacks = new ArrayList<>();
+
+ public RemoteDisplayChangeController(WindowManagerService service, int displayId) {
+ mService = service;
+ mDisplayId = displayId;
+ }
+
+ /**
+ * A Remote change is when we are waiting for some registered (remote)
+ * {@link IDisplayChangeWindowController} to calculate and return some hierarchy operations
+ * to perform in sync with the display change.
+ */
+ public boolean isWaitingForRemoteDisplayChange() {
+ return !mCallbacks.isEmpty();
+ }
+
+ /**
+ * Starts remote display change
+ * @param fromRotation rotation before the change
+ * @param toRotation rotation after the change
+ * @param newDisplayAreaInfo display area info after change
+ * @param callback that will be called after completing remote display change
+ * @return true if the change successfully started, false otherwise
+ */
+ public boolean performRemoteDisplayChange(
+ int fromRotation, int toRotation,
+ @Nullable DisplayAreaInfo newDisplayAreaInfo,
+ ContinueRemoteDisplayChangeCallback callback) {
+ if (mService.mDisplayChangeController == null) {
+ return false;
+ }
+ mCallbacks.add(callback);
+
+ if (newDisplayAreaInfo != null) {
+ ProtoLog.v(WM_DEBUG_CONFIGURATION,
+ "Starting remote display change: "
+ + "from [rot = %d], "
+ + "to [%dx%d, rot = %d]",
+ fromRotation,
+ newDisplayAreaInfo.configuration.windowConfiguration
+ .getMaxBounds().width(),
+ newDisplayAreaInfo.configuration.windowConfiguration
+ .getMaxBounds().height(),
+ toRotation);
+ }
+
+ final IDisplayChangeWindowCallback remoteCallback = createCallback(callback);
+ try {
+ mService.mH.removeCallbacks(mTimeoutRunnable);
+ mService.mH.postDelayed(mTimeoutRunnable, REMOTE_DISPLAY_CHANGE_TIMEOUT_MS);
+ mService.mDisplayChangeController.onDisplayChange(mDisplayId, fromRotation, toRotation,
+ newDisplayAreaInfo, remoteCallback);
+ return true;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception while dispatching remote display-change", e);
+ mCallbacks.remove(callback);
+ return false;
+ }
+ }
+
+ private void onContinueTimedOut() {
+ Slog.e(TAG, "RemoteDisplayChange timed-out, UI might get messed-up after this.");
+ // timed-out, so run all continue callbacks and clear the list
+ synchronized (mService.mGlobalLock) {
+ for (int i = 0; i < mCallbacks.size(); ++i) {
+ mCallbacks.get(i).onContinueRemoteDisplayChange(null /* transaction */);
+ }
+ mCallbacks.clear();
+ }
+ }
+
+ private void continueDisplayChange(@NonNull ContinueRemoteDisplayChangeCallback callback,
+ @Nullable WindowContainerTransaction transaction) {
+ synchronized (mService.mGlobalLock) {
+ int idx = mCallbacks.indexOf(callback);
+ if (idx < 0) {
+ // already called this callback or a more-recent one (eg. via timeout)
+ return;
+ }
+ for (int i = 0; i < idx; ++i) {
+ // Expect remote callbacks in order. If they don't come in order, then force
+ // ordering by continuing everything up until this one with empty transactions.
+ mCallbacks.get(i).onContinueRemoteDisplayChange(null /* transaction */);
+ }
+ mCallbacks.subList(0, idx + 1).clear();
+ if (mCallbacks.isEmpty()) {
+ mService.mH.removeCallbacks(mTimeoutRunnable);
+ }
+ callback.onContinueRemoteDisplayChange(transaction);
+ }
+ }
+
+ private IDisplayChangeWindowCallback createCallback(
+ @NonNull ContinueRemoteDisplayChangeCallback callback) {
+ return new IDisplayChangeWindowCallback.Stub() {
+ @Override
+ public void continueDisplayChange(WindowContainerTransaction t) {
+ synchronized (mService.mGlobalLock) {
+ if (!mCallbacks.contains(callback)) {
+ // already ran this callback or a more-recent one.
+ return;
+ }
+ mService.mH.post(() -> RemoteDisplayChangeController.this
+ .continueDisplayChange(callback, t));
+ }
+ }
+ };
+ }
+
+ /**
+ * Callback interface to handle continuation of the remote display change
+ */
+ public interface ContinueRemoteDisplayChangeCallback {
+ /**
+ * This method is called when the remote display change has been applied
+ * @param transaction window changes collected by the remote display change
+ */
+ void onContinueRemoteDisplayChange(@Nullable WindowContainerTransaction transaction);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ef18b50e910b..dfce40b874c0 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -37,6 +37,7 @@ import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_WAKE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON;
@@ -1238,11 +1239,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
@Override
- void scheduleAnimation() {
- mWmService.scheduleAnimationLocked();
- }
-
- @Override
protected void removeChild(DisplayContent dc) {
super.removeChild(dc);
if (mTopFocusedDisplayId == dc.getDisplayId()) {
@@ -1977,23 +1973,28 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
void moveActivityToPinnedRootTask(@NonNull ActivityRecord r,
@Nullable ActivityRecord launchIntoPipHostActivity, String reason) {
- mService.deferWindowLayout();
+ moveActivityToPinnedRootTask(r, launchIntoPipHostActivity, reason, null /* transition */);
+ }
+ void moveActivityToPinnedRootTask(@NonNull ActivityRecord r,
+ @Nullable ActivityRecord launchIntoPipHostActivity, String reason,
+ @Nullable Transition transition) {
final TaskDisplayArea taskDisplayArea = r.getDisplayArea();
+ final Task task = r.getTask();
+ final Task rootTask;
- try {
- final Task task = r.getTask();
-
- // Create a transition now to collect the current pinned Task dismiss. Only do the
- // create here as the Task (trigger) to enter PIP is not ready yet.
- final TransitionController transitionController = task.mTransitionController;
- Transition newTransition = null;
- if (transitionController.isCollecting()) {
- transitionController.setReady(task, false /* ready */);
- } else if (transitionController.getTransitionPlayer() != null) {
- newTransition = transitionController.createTransition(TRANSIT_PIP);
- }
+ Transition newTransition = transition;
+ // Create a transition now (if not provided) to collect the current pinned Task dismiss.
+ // Only do the create here as the Task (trigger) to enter PIP is not ready yet.
+ final TransitionController transitionController = task.mTransitionController;
+ if (newTransition == null && !transitionController.isCollecting()
+ && transitionController.getTransitionPlayer() != null) {
+ newTransition = transitionController.createTransition(TRANSIT_PIP);
+ }
+ transitionController.deferTransitionReady();
+ mService.deferWindowLayout();
+ try {
// This will change the root pinned task's windowing mode to its original mode, ensuring
// we only have one root task that is in pinned mode.
final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask();
@@ -2011,7 +2012,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final TaskFragment organizedTf = r.getOrganizedTaskFragment();
final boolean singleActivity = task.getNonFinishingActivityCount() == 1;
- final Task rootTask;
if (singleActivity) {
rootTask = task;
@@ -2044,6 +2044,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
task.mLastRecentsAnimationTransaction,
task.mLastRecentsAnimationOverlay);
task.clearLastRecentsAnimationTransaction(false /* forceRemoveOverlay */);
+ } else {
+ // Reset the original task surface
+ task.resetSurfaceControlTransforms();
}
// The organized TaskFragment is becoming empty because this activity is reparented
@@ -2077,6 +2080,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
oldTopActivity.mRequestForceTransition = true;
}
}
+
+ transitionController.collect(rootTask);
+
// The intermediate windowing mode to be set on the ActivityRecord later.
// This needs to happen before the re-parenting, otherwise we will always set the
// ActivityRecord to be fullscreen.
@@ -2087,13 +2093,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
rootTask.reparent(taskDisplayArea, true /* onTop */);
}
- // The new PIP Task is ready, start the transition before updating the windowing mode.
- if (newTransition != null) {
- transitionController.requestStartTransition(newTransition, rootTask,
- null /* remoteTransition */, null /* displayChange */);
- }
- transitionController.collect(rootTask);
-
// Defer the windowing mode change until after the transition to prevent the activity
// from doing work and changing the activity visuals while animating
// TODO(task-org): Figure-out more structured way to do this long term.
@@ -2119,7 +2118,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// entering content-pip animation.
mWindowManager.mTaskSnapshotController.recordTaskSnapshot(
task, false /* allowSnapshotHome */);
- rootTask.setBounds(r.getOptions().getLaunchBounds());
+ rootTask.setBounds(r.pictureInPictureArgs.getSourceRectHint());
}
rootTask.setDeferTaskAppear(false);
@@ -2136,9 +2135,23 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
} finally {
mService.continueWindowLayout();
+ try {
+ ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+ } finally {
+ transitionController.continueTransitionReady();
+ }
+ }
+
+ if (newTransition != null) {
+ // Request at end since we want task-organizer events from ensureActivitiesVisible
+ // to be recognized.
+ transitionController.requestStartTransition(newTransition, rootTask,
+ null /* remoteTransition */, null /* displayChange */);
+ // A new transition was created just for this operations. Since the operation is
+ // complete, mark it as ready.
+ newTransition.setReady(rootTask, true /* ready */);
}
- ensureActivitiesVisible(null, 0, false /* preserveWindows */);
resumeFocusedTasksTopActivities();
notifyActivityPipModeChanged(r.getTask(), r);
@@ -2321,6 +2334,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
continue;
}
+ // Prepare transition before resume top activity, so it can be collected.
+ if (!displayShouldSleep && display.isDefaultDisplay
+ && !display.getDisplayPolicy().isAwake()
+ && display.mTransitionController.isShellTransitionsEnabled()
+ && !display.mTransitionController.isCollecting()) {
+ display.mTransitionController.requestTransitionIfNeeded(TRANSIT_WAKE,
+ 0 /* flags */, null /* trigger */, display);
+ }
// Set the sleeping state of the root tasks on the display.
display.forAllRootTasks(rootTask -> {
if (displayShouldSleep) {
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index fd05f19f54c5..2879e33fb71a 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.Manifest.permission.CONTROL_KEYGUARD;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.Manifest.permission.STATUS_BAR_SERVICE;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
@@ -173,7 +174,7 @@ public class SafeActivityOptions {
if (adapter == null) {
return;
}
- if (callingPid == Process.myPid()) {
+ if (callingPid == WindowManagerService.MY_PID) {
Slog.wtf(TAG, "Safe activity options constructed after clearing calling id");
return;
}
@@ -247,6 +248,14 @@ public class SafeActivityOptions {
throw new SecurityException(msg);
}
}
+ if (options.getTransientLaunch() && !supervisor.mRecentTasks.isCallerRecents(callingUid)
+ && ActivityTaskManagerService.checkPermission(
+ MANAGE_ACTIVITY_TASKS, callingPid, callingUid) == PERMISSION_DENIED) {
+ final String msg = "Permission Denial: starting transient launch from " + callerApp
+ + ", pid=" + callingPid + ", uid=" + callingUid;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
// Check if the caller is allowed to launch on the specified display area.
final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
final TaskDisplayArea taskDisplayArea = daToken != null
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index f80e732c8212..8cad16509c4c 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -173,11 +173,6 @@ class ScreenRotationAnimation {
if (isSizeChanged) {
mRoundedCornerOverlay = displayContent.findRoundedCornerOverlays();
- } else {
- // Exclude rounded corner overlay from screenshot buffer. Rounded
- // corner overlay windows are un-rotated during rotation animation
- // for a seamless transition.
- builder.setExcludeLayers(displayContent.findRoundedCornerOverlays());
}
SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
@@ -600,7 +595,7 @@ class ScreenRotationAnimation {
}
private SurfaceAnimator startDisplayRotation() {
- return startAnimation(initializeBuilder()
+ SurfaceAnimator animator = startAnimation(initializeBuilder()
.setAnimationLeashParent(mDisplayContent.getSurfaceControl())
.setSurfaceControl(mDisplayContent.getWindowingLayer())
.setParentSurfaceControl(mDisplayContent.getSurfaceControl())
@@ -609,6 +604,13 @@ class ScreenRotationAnimation {
.build(),
createWindowAnimationSpec(mRotateEnterAnimation),
this::onAnimationEnd);
+
+ // Crop the animation leash to avoid extended wallpaper from showing over
+ // mBackColorSurface
+ Rect displayBounds = mDisplayContent.getBounds();
+ mDisplayContent.getPendingTransaction()
+ .setWindowCrop(animator.mLeash, displayBounds.width(), displayBounds.height());
+ return animator;
}
private SurfaceAnimator startScreenshotAlphaAnimation() {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 30b50839cd35..3577545088e0 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.Manifest.permission.HIDE_OVERLAY_WINDOWS;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+import static android.Manifest.permission.SET_UNRESTRICTED_GESTURE_EXCLUSION;
import static android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY;
@@ -109,14 +110,13 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final boolean mCanCreateSystemApplicationOverlay;
final boolean mCanHideNonSystemOverlayWindows;
+ final boolean mCanSetUnrestrictedGestureExclusion;
private AlertWindowNotification mAlertWindowNotification;
private boolean mShowingAlertWindowNotificationAllowed;
private boolean mClientDead = false;
private float mLastReportedAnimatorScale;
private String mPackageName;
private String mRelayoutTag;
- private String mUpdateViewVisibilityTag;
- private String mUpdateWindowLayoutTag;
private final InsetsVisibilities mDummyRequestedVisibilities = new InsetsVisibilities();
private final InsetsSourceControl[] mDummyControls = new InsetsSourceControl[0];
final boolean mSetsUnrestrictedKeepClearAreas;
@@ -141,6 +141,9 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
mSetsUnrestrictedKeepClearAreas =
service.mContext.checkCallingOrSelfPermission(SET_UNRESTRICTED_KEEP_CLEAR_AREAS)
== PERMISSION_GRANTED;
+ mCanSetUnrestrictedGestureExclusion =
+ service.mContext.checkCallingOrSelfPermission(SET_UNRESTRICTED_GESTURE_EXCLUSION)
+ == PERMISSION_GRANTED;
mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
mDragDropController = mService.mDragDropController;
StringBuilder sb = new StringBuilder();
@@ -195,27 +198,28 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls) {
+ InsetsSourceControl[] outActiveControls, Rect outAttachedFrame) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId,
UserHandle.getUserId(mUid), requestedVisibilities, outInputChannel, outInsetsState,
- outActiveControls);
+ outActiveControls, outAttachedFrame);
}
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls) {
+ InsetsSourceControl[] outActiveControls, Rect outAttachedFrame) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
- requestedVisibilities, outInputChannel, outInsetsState, outActiveControls);
+ requestedVisibilities, outInputChannel, outInsetsState, outActiveControls,
+ outAttachedFrame);
}
@Override
public int addToDisplayWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
- int viewVisibility, int displayId, InsetsState outInsetsState) {
+ int viewVisibility, int displayId, InsetsState outInsetsState, Rect outAttachedFrame) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId,
UserHandle.getUserId(mUid), mDummyRequestedVisibilities, null /* outInputChannel */,
- outInsetsState, mDummyControls);
+ outInsetsState, mDummyControls, outAttachedFrame);
}
@Override
@@ -224,29 +228,13 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
- public int updateVisibility(IWindow client, WindowManager.LayoutParams attrs,
- int viewVisibility, MergedConfiguration outMergedConfiguration,
- SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mUpdateViewVisibilityTag);
- int res = mService.updateViewVisibility(this, client, attrs, viewVisibility,
- outMergedConfiguration, outSurfaceControl, outInsetsState, outActiveControls);
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- return res;
- }
-
- @Override
- public void updateLayout(IWindow window, WindowManager.LayoutParams attrs, int flags,
- ClientWindowFrames clientFrames, int requestedWidth, int requestedHeight) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mUpdateWindowLayoutTag);
- mService.updateWindowLayout(this, window, attrs, flags, clientFrames, requestedWidth,
- requestedHeight);
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ public void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly) {
+ mService.setWillReplaceWindows(appToken, childrenOnly);
}
@Override
- public void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly) {
- mService.setWillReplaceWindows(appToken, childrenOnly);
+ public boolean cancelDraw(IWindow window) {
+ return mService.cancelDraw(this, window);
}
@Override
@@ -711,8 +699,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
if (wpc != null) {
mPackageName = wpc.mInfo.packageName;
mRelayoutTag = "relayoutWindow: " + mPackageName;
- mUpdateViewVisibilityTag = "updateVisibility: " + mPackageName;
- mUpdateWindowLayoutTag = "updateLayout: " + mPackageName;
} else {
Slog.e(TAG_WM, "Unknown process pid=" + mPid);
}
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index f83173bd46c0..0bb773ae5e41 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -220,6 +220,11 @@ public class StartingSurfaceController {
// Attempt to add starting window from the top-most activity.
for (int i = mDeferringAddStartActivities.size() - 1; i >= 0; --i) {
final DeferringStartingWindowRecord next = mDeferringAddStartActivities.get(i);
+ if (next.mDeferring.getTask() == null) {
+ Slog.e(TAG, "No task exists: " + next.mDeferring.shortComponentName
+ + " parent: " + next.mDeferring.getParent());
+ continue;
+ }
next.mDeferring.showStartingWindow(next.mPrev, mInitNewTask, mInitTaskSwitch,
mInitProcessRunning, true /* startActivity */, next.mSource, topOptions);
// If one succeeds, it is done.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index bf5246f2339a..3b97e637a20a 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -67,16 +67,14 @@ import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
-import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
-import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
-import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
import static com.android.server.wm.ActivityRecord.State.PAUSED;
import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
@@ -131,7 +129,6 @@ import static com.android.server.wm.WindowContainerChildProto.TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.dipToPixel;
import static java.lang.Integer.MAX_VALUE;
@@ -197,6 +194,7 @@ import android.window.WindowContainerToken;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.pooled.PooledConsumer;
@@ -426,9 +424,6 @@ class Task extends TaskFragment {
/** Helper object used for updating override configuration. */
private Configuration mTmpConfig = new Configuration();
- /** Used by fillTaskInfo */
- final TaskActivitiesReport mReuseActivitiesReport = new TaskActivitiesReport();
-
/* Unique identifier for this task. */
final int mTaskId;
/* User for which this task was created. */
@@ -743,8 +738,9 @@ class Task extends TaskFragment {
"removeTask:" + reason + " deferring removing taskId=" + mTaskId);
return;
}
+ final boolean isLeafTask = isLeafTask();
removeImmediately(reason);
- if (isLeafTask()) {
+ if (isLeafTask) {
mAtmService.getTaskChangeNotificationController().notifyTaskRemoved(mTaskId);
final TaskDisplayArea taskDisplayArea = getDisplayArea();
@@ -1404,15 +1400,6 @@ class Task extends TaskFragment {
}
/**
- * Return the number of running activities, and the number of non-finishing/initializing
- * activities in the provided {@param reportOut} respectively.
- */
- private void getNumRunningActivities(TaskActivitiesReport reportOut) {
- reportOut.reset();
- forAllActivities(reportOut);
- }
-
- /**
* Reorder the history task so that the passed activity is brought to the front.
*/
final void moveActivityToFrontLocked(ActivityRecord newTop) {
@@ -1707,23 +1694,6 @@ class Task extends TaskFragment {
lockTaskAuthToString());
}
- @Override
- public boolean supportsSplitScreenWindowingMode() {
- return supportsSplitScreenWindowingModeInDisplayArea(getDisplayArea());
- }
-
- boolean supportsSplitScreenWindowingModeInDisplayArea(@Nullable TaskDisplayArea tda) {
- final Task topTask = getTopMostTask();
- return super.supportsSplitScreenWindowingMode()
- && (topTask == null || topTask.supportsSplitScreenWindowingModeInner(tda));
- }
-
- private boolean supportsSplitScreenWindowingModeInner(@Nullable TaskDisplayArea tda) {
- return super.supportsSplitScreenWindowingMode()
- && mAtmService.mSupportsSplitScreenMultiWindow
- && supportsMultiWindowInDisplayArea(tda);
- }
-
boolean supportsFreeform() {
return supportsFreeformInDisplayArea(getDisplayArea());
}
@@ -1889,6 +1859,11 @@ class Task extends TaskFragment {
if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_UNDEFINED) {
nextPersistTaskBounds = newParentConfig.windowConfiguration.persistTaskBounds();
}
+ // Only restore to the last non-fullscreen bounds when the requested override bounds
+ // have not been explicitly set already.
+ nextPersistTaskBounds &=
+ (getRequestedOverrideConfiguration().windowConfiguration.getBounds() == null
+ || getRequestedOverrideConfiguration().windowConfiguration.getBounds().isEmpty());
if (!prevPersistTaskBounds && nextPersistTaskBounds
&& mLastNonFullscreenBounds != null && !mLastNonFullscreenBounds.isEmpty()) {
// Bypass onRequestedOverrideConfigurationChanged here to avoid infinite loop.
@@ -2031,10 +2006,6 @@ class Task extends TaskFragment {
Rect outOverrideBounds = getResolvedOverrideConfiguration().windowConfiguration.getBounds();
if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
- if (!isOrganized()) {
- // Use empty bounds to indicate "fill parent".
- outOverrideBounds.setEmpty();
- }
// The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
// the parent or display is smaller than the size, the content may be cropped.
return;
@@ -3309,7 +3280,6 @@ class Task extends TaskFragment {
}
final SurfaceControl.Transaction t = getSyncTransaction();
- updateShadowsRadius(isFocused(), t);
if (mDimmer.updateDims(t, mTmpDimBoundsRect)) {
scheduleAnimation();
@@ -3384,14 +3354,14 @@ class Task extends TaskFragment {
* the give {@link TaskDisplayArea}.
*/
void fillTaskInfo(TaskInfo info, boolean stripExtras, @Nullable TaskDisplayArea tda) {
- getNumRunningActivities(mReuseActivitiesReport);
+ info.launchCookies.clear();
+ info.addLaunchCookie(mLaunchCookie);
+ final ActivityRecord top = mTaskSupervisor.mTaskInfoHelper.fillAndReturnTop(this, info);
+
info.userId = isLeafTask() ? mUserId : mCurrentUser;
info.taskId = mTaskId;
info.displayId = getDisplayId();
- if (tda != null) {
- info.displayAreaFeatureId = tda.mFeatureId;
- }
- info.isRunning = getTopNonFinishingActivity() != null;
+ info.displayAreaFeatureId = tda != null ? tda.mFeatureId : FEATURE_UNDEFINED;
final Intent baseIntent = getBaseIntent();
// Make a copy of base intent because this is like a snapshot info.
// Besides, {@link RecentTasks#getRecentTasksImpl} may modify it.
@@ -3400,18 +3370,13 @@ class Task extends TaskFragment {
? new Intent()
: stripExtras ? baseIntent.cloneFilter() : new Intent(baseIntent);
info.baseIntent.setFlags(baseIntentFlags);
- info.baseActivity = mReuseActivitiesReport.base != null
- ? mReuseActivitiesReport.base.intent.getComponent()
- : null;
- info.topActivity = mReuseActivitiesReport.top != null
- ? mReuseActivitiesReport.top.mActivityComponent
- : null;
+
+ info.isRunning = top != null;
+ info.topActivity = top != null ? top.mActivityComponent : null;
info.origActivity = origActivity;
info.realActivity = realActivity;
- info.numActivities = mReuseActivitiesReport.numActivities;
info.lastActiveTime = lastActiveTime;
info.taskDescription = new ActivityManager.TaskDescription(getTaskDescription());
- info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingModeInDisplayArea(tda);
info.supportsMultiWindow = supportsMultiWindowInDisplayArea(tda);
info.configuration.setTo(getConfiguration());
// Update to the task's current activity type and windowing mode which may differ from the
@@ -3422,49 +3387,38 @@ class Task extends TaskFragment {
//TODO (AM refactor): Just use local once updateEffectiveIntent is run during all child
// order changes.
- final Task top = getTopMostTask();
- info.resizeMode = top != null ? top.mResizeMode : mResizeMode;
- info.topActivityType = top.getActivityType();
+ final Task topTask = top != null ? top.getTask() : this;
+ info.resizeMode = topTask.mResizeMode;
+ info.topActivityType = topTask.getActivityType();
+ info.displayCutoutInsets = topTask.getDisplayCutoutInsets();
info.isResizeable = isResizeable();
info.minWidth = mMinWidth;
info.minHeight = mMinHeight;
info.defaultMinSize = mDisplayContent == null
? DEFAULT_MIN_TASK_SIZE_DP : mDisplayContent.mMinSizeOfResizeableTaskDp;
-
info.positionInParent = getRelativePosition();
+ info.topActivityInfo = top != null ? top.info : null;
info.pictureInPictureParams = getPictureInPictureParams(top);
- info.shouldDockBigOverlays = shouldDockBigOverlays();
- if (info.pictureInPictureParams != null
+ info.launchIntoPipHostTaskId = (info.pictureInPictureParams != null
&& info.pictureInPictureParams.isLaunchIntoPip()
- && top.getTopMostActivity().getLastParentBeforePip() != null) {
- info.launchIntoPipHostTaskId =
- top.getTopMostActivity().getLastParentBeforePip().mTaskId;
- }
- info.displayCutoutInsets = top != null ? top.getDisplayCutoutInsets() : null;
- info.topActivityInfo = mReuseActivitiesReport.top != null
- ? mReuseActivitiesReport.top.info
- : null;
-
- boolean isTopActivityResumed = mReuseActivitiesReport.top != null
- && mReuseActivitiesReport.top.getOrganizedTask() == this
- && mReuseActivitiesReport.top.isState(RESUMED);
+ && top.getLastParentBeforePip() != null)
+ ? top.getLastParentBeforePip().mTaskId : INVALID_TASK_ID;
+ info.shouldDockBigOverlays = top != null && top.shouldDockBigOverlays;
+ info.mTopActivityLocusId = top != null ? top.getLocusId() : null;
+
+ final boolean isTopActivityResumed = top != null
+ && top.getOrganizedTask() == this && top.isState(RESUMED);
// Whether the direct top activity is in size compat mode on foreground.
- info.topActivityInSizeCompat = isTopActivityResumed
- && mReuseActivitiesReport.top.inSizeCompatMode();
+ info.topActivityInSizeCompat = isTopActivityResumed && top.inSizeCompatMode();
// Whether the direct top activity is eligible for letterbox education.
info.topActivityEligibleForLetterboxEducation = isTopActivityResumed
- && mReuseActivitiesReport.top.isEligibleForLetterboxEducation();
+ && top.isEligibleForLetterboxEducation();
// Whether the direct top activity requested showing camera compat control.
info.cameraCompatControlState = isTopActivityResumed
- ? mReuseActivitiesReport.top.getCameraCompatControlState()
+ ? top.getCameraCompatControlState()
: TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
- info.launchCookies.clear();
- info.addLaunchCookie(mLaunchCookie);
- forAllActivities(r -> {
- info.addLaunchCookie(r.mLaunchCookie);
- });
final Task parentTask = getParent() != null ? getParent().asTask() : null;
info.parentTaskId = parentTask != null && parentTask.mCreatedByOrganizer
? parentTask.mTaskId
@@ -3472,19 +3426,17 @@ class Task extends TaskFragment {
info.isFocused = isFocused();
info.isVisible = hasVisibleChildren();
info.isSleeping = shouldSleepActivities();
- ActivityRecord topRecord = getTopNonFinishingActivity();
- info.mTopActivityLocusId = topRecord != null ? topRecord.getLocusId() : null;
}
@Nullable PictureInPictureParams getPictureInPictureParams() {
- return getPictureInPictureParams(getTopMostTask());
+ final Task topTask = getTopMostTask();
+ if (topTask == null) return null;
+ return getPictureInPictureParams(topTask.getTopMostActivity());
}
- private @Nullable PictureInPictureParams getPictureInPictureParams(Task top) {
- if (top == null) return null;
- final ActivityRecord topMostActivity = top.getTopMostActivity();
- return (topMostActivity == null || topMostActivity.pictureInPictureArgs.empty())
- ? null : new PictureInPictureParams(topMostActivity.pictureInPictureArgs);
+ private static @Nullable PictureInPictureParams getPictureInPictureParams(ActivityRecord top) {
+ return (top == null || top.pictureInPictureArgs.empty())
+ ? null : new PictureInPictureParams(top.pictureInPictureArgs);
}
private boolean shouldDockBigOverlays() {
@@ -3673,30 +3625,35 @@ class Task extends TaskFragment {
}
@Override
+ String toFullString() {
+ final StringBuilder sb = new StringBuilder(192);
+ sb.append(this);
+ sb.setLength(sb.length() - 1); // Remove tail '}'.
+ sb.append(" U=");
+ sb.append(mUserId);
+ final Task rootTask = getRootTask();
+ if (rootTask != this) {
+ sb.append(" rootTaskId=");
+ sb.append(rootTask.mTaskId);
+ }
+ sb.append(" visible=");
+ sb.append(shouldBeVisible(null /* starting */));
+ sb.append(" visibleRequested=");
+ sb.append(isVisibleRequested());
+ sb.append(" mode=");
+ sb.append(windowingModeToString(getWindowingMode()));
+ sb.append(" translucent=");
+ sb.append(isTranslucent(null /* starting */));
+ sb.append(" sz=");
+ sb.append(getChildCount());
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
public String toString() {
+ if (stringName != null) return stringName;
StringBuilder sb = new StringBuilder(128);
- if (stringName != null) {
- sb.append(stringName);
- sb.append(" U=");
- sb.append(mUserId);
- final Task rootTask = getRootTask();
- if (rootTask != this) {
- sb.append(" rootTaskId=");
- sb.append(rootTask.mTaskId);
- }
- sb.append(" visible=");
- sb.append(shouldBeVisible(null /* starting */));
- sb.append(" visibleRequested=");
- sb.append(isVisibleRequested());
- sb.append(" mode=");
- sb.append(windowingModeToString(getWindowingMode()));
- sb.append(" translucent=");
- sb.append(isTranslucent(null /* starting */));
- sb.append(" sz=");
- sb.append(getChildCount());
- sb.append('}');
- return sb.toString();
- }
sb.append("Task{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" #");
@@ -3711,47 +3668,9 @@ class Task extends TaskFragment {
} else if (affinityIntent != null && affinityIntent.getComponent() != null) {
sb.append(" aI=");
sb.append(affinityIntent.getComponent().flattenToShortString());
- } else {
- sb.append(" ??");
- }
- stringName = sb.toString();
- return toString();
- }
-
- /** @see #getNumRunningActivities(TaskActivitiesReport) */
- static class TaskActivitiesReport implements Consumer<ActivityRecord> {
- int numRunning;
- int numActivities;
- ActivityRecord top;
- ActivityRecord base;
-
- void reset() {
- numRunning = numActivities = 0;
- top = base = null;
- }
-
- @Override
- public void accept(ActivityRecord r) {
- if (r.finishing) {
- return;
- }
-
- base = r;
-
- // Increment the total number of non-finishing activities
- numActivities++;
-
- if (top == null || (top.isState(INITIALIZING))) {
- top = r;
- // Reset the number of running activities until we hit the first non-initializing
- // activity
- numRunning = 0;
- }
- if (r.attachedToProcess()) {
- // Increment the number of actually running activities
- numRunning++;
- }
}
+ sb.append('}');
+ return stringName = sb.toString();
}
/**
@@ -4335,48 +4254,10 @@ class Task extends TaskFragment {
}
/**
- * @return the desired shadow radius in pixels for the current task.
- */
- private float getShadowRadius(boolean taskIsFocused) {
- int elevation = 0;
-
- // Get elevation for a specific windowing mode.
- if (inFreeformWindowingMode()) {
- elevation = taskIsFocused
- ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
- } else {
- // For all other windowing modes, do not draw a shadow.
- return 0;
- }
-
- // If the task has no visible children, do not draw a shadow.
- if (!hasVisibleChildren()) {
- return 0;
- }
-
- return dipToPixel(elevation, getDisplayContent().getDisplayMetrics());
- }
-
- /**
- * Update the length of the shadow if needed based on windowing mode and task focus state.
- */
- private void updateShadowsRadius(boolean taskIsFocused,
- SurfaceControl.Transaction pendingTransaction) {
- if (!isRootTask()) return;
-
- final float newShadowRadius = getShadowRadius(taskIsFocused);
- if (mShadowRadius != newShadowRadius) {
- mShadowRadius = newShadowRadius;
- pendingTransaction.setShadowRadius(getSurfaceControl(), mShadowRadius);
- }
- }
-
- /**
* Called on the task when it gained or lost focus.
* @param hasFocus
*/
void onAppFocusChanged(boolean hasFocus) {
- updateShadowsRadius(hasFocus, getSyncTransaction());
dispatchTaskInfoChangedIfNeeded(false /* force */);
}
@@ -4555,13 +4436,13 @@ class Task extends TaskFragment {
: WINDOWING_MODE_FULLSCREEN;
}
if (currentMode == WINDOWING_MODE_PINNED) {
+ // In the case that we've disabled affecting the SysUI flags as a part of seamlessly
+ // transferring the transform on the leash to the task, reset this state once we're
+ // moving out of pip
+ setCanAffectSystemUiFlags(true);
mRootWindowContainer.notifyActivityPipModeChanged(this, null);
}
if (likelyResolvedMode == WINDOWING_MODE_PINNED) {
- // In the case that we've disabled affecting the SysUI flags as a part of seamlessly
- // transferring the transform on the leash to the task, reset this state once we've
- // actually entered pip
- setCanAffectSystemUiFlags(true);
if (taskDisplayArea.getRootPinnedTask() != null) {
// Can only have 1 pip at a time, so replace an existing pip
taskDisplayArea.getRootPinnedTask().dismissPip();
@@ -4652,22 +4533,12 @@ class Task extends TaskFragment {
moveToFront(reason, null);
}
- void moveToFront(String reason, Task task) {
- if (mMoveAdjacentTogether && getAdjacentTaskFragment() != null) {
- final Task adjacentTask = getAdjacentTaskFragment().asTask();
- if (adjacentTask != null) {
- adjacentTask.moveToFrontInner(reason + " adjacentTaskToTop", null /* task */);
- }
- }
- moveToFrontInner(reason, task);
- }
-
/**
* @param reason The reason for moving the root task to the front.
* @param task If non-null, the task will be moved to the top of the root task.
*/
@VisibleForTesting
- void moveToFrontInner(String reason, Task task) {
+ void moveToFront(String reason, Task task) {
if (!isAttached()) {
return;
}
@@ -5581,23 +5452,10 @@ class Task extends TaskFragment {
}
}
- /**
- * Worker method for rearranging history task. Implements the function of moving all
- * activities for a specific task (gathering them if disjoint) into a single group at the
- * bottom of the root task.
- *
- * If a watcher is installed, the action is preflighted and the watcher has an opportunity
- * to premeptively cancel the move.
- *
- * @param tr The task to collect and move to the bottom.
- * @return Returns true if the move completed, false if not.
- */
- boolean moveTaskToBack(Task tr) {
- Slog.i(TAG, "moveTaskToBack: " + tr);
-
+ private boolean canMoveTaskToBack(Task task) {
// In LockTask mode, moving a locked task to the back of the root task may expose unlocked
// ones. Therefore we need to check if this operation is allowed.
- if (!mAtmService.getLockTaskController().canMoveTaskToBack(tr)) {
+ if (!mAtmService.getLockTaskController().canMoveTaskToBack(task)) {
return false;
}
@@ -5605,7 +5463,7 @@ class Task extends TaskFragment {
// for *other* available tasks, but if none are available, then try again allowing the
// current task to be selected.
if (isTopRootTaskInDisplayArea() && mAtmService.mController != null) {
- ActivityRecord next = topRunningActivity(null, tr.mTaskId);
+ ActivityRecord next = topRunningActivity(null, task.mTaskId);
if (next == null) {
next = topRunningActivity(null, INVALID_TASK_ID);
}
@@ -5623,15 +5481,70 @@ class Task extends TaskFragment {
}
}
}
+ return true;
+ }
+
+ /**
+ * Worker method for rearranging history task. Implements the function of moving all
+ * activities for a specific task (gathering them if disjoint) into a single group at the
+ * bottom of the root task.
+ *
+ * If a watcher is installed, the action is preflighted and the watcher has an opportunity
+ * to premeptively cancel the move.
+ *
+ * If this is a pinned task, it will be removed instead of rearranged.
+ *
+ * @param tr The task to collect and move to the bottom.
+ * @return Returns true if the move completed, false if not.
+ */
+ boolean moveTaskToBack(Task tr) {
+ Slog.i(TAG, "moveTaskToBack: " + tr);
+
+ if (!canMoveTaskToBack(tr)) return false;
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task="
+ tr.mTaskId);
- // Skip the transition for pinned task.
- if (!inPinnedWindowingMode()) {
- mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_TO_BACK, tr);
+ if (mTransitionController.isShellTransitionsEnabled()) {
+ final Transition transition = new Transition(TRANSIT_TO_BACK, 0 /* flags */,
+ mTransitionController, mWmService.mSyncEngine);
+ // Guarantee that this gets its own transition by queueing on SyncEngine
+ if (mWmService.mSyncEngine.hasActiveSync()) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+ "Creating Pending Move-to-back: %s", transition);
+ mWmService.mSyncEngine.queueSyncSet(
+ () -> mTransitionController.moveToCollecting(transition),
+ () -> {
+ mTransitionController.requestStartTransition(transition, tr,
+ null /* remoteTransition */, null /* displayChange */);
+ // Need to check again since this happens later and the system might
+ // be in a different state.
+ if (!canMoveTaskToBack(tr)) {
+ Slog.e(TAG, "Failed to move task to back after saying we could: "
+ + tr.mTaskId);
+ transition.abort();
+ return;
+ }
+ moveTaskToBackInner(tr);
+ });
+ } else {
+ mTransitionController.moveToCollecting(transition);
+ mTransitionController.requestStartTransition(transition, tr,
+ null /* remoteTransition */, null /* displayChange */);
+ moveTaskToBackInner(tr);
+ }
+ } else {
+ // Skip the transition for pinned task.
+ if (!inPinnedWindowingMode()) {
+ mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK);
+ }
+ moveTaskToBackInner(tr);
}
- moveToBack("moveTaskToBackLocked", tr);
+ return true;
+ }
+
+ private boolean moveTaskToBackInner(@NonNull Task task) {
+ moveToBack("moveTaskToBackInner", task);
if (inPinnedWindowingMode()) {
mTaskSupervisor.removeRootTask(this);
@@ -6028,8 +5941,13 @@ class Task extends TaskFragment {
mLastRecentsAnimationTransaction = null;
mLastRecentsAnimationOverlay = null;
// reset also the crop and transform introduced by mLastRecentsAnimationTransaction
- getPendingTransaction().setMatrix(mSurfaceControl, Matrix.IDENTITY_MATRIX, new float[9])
+ resetSurfaceControlTransforms();
+ }
+
+ void resetSurfaceControlTransforms() {
+ getSyncTransaction().setMatrix(mSurfaceControl, Matrix.IDENTITY_MATRIX, new float[9])
.setWindowCrop(mSurfaceControl, null)
+ .setShadowRadius(mSurfaceControl, 0)
.setCornerRadius(mSurfaceControl, 0);
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index ca91a742786a..8220cae74dc8 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -29,7 +29,6 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
@@ -91,20 +90,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
*/
private int mColorLayerCounter = 0;
- /**
- * Given that the split-screen divider does not have an AppWindowToken, it
- * will have to live inside of a "NonAppWindowContainer". However, in visual Z order
- * it will need to be interleaved with some of our children, appearing on top of
- * both docked root tasks but underneath any assistant root tasks.
- *
- * To solve this problem we have this anchor control, which will always exist so
- * we can always assign it the correct value in our {@link #assignChildLayers}.
- * Likewise since it always exists, we can always
- * assign the divider a layer relative to it. This way we prevent linking lifecycle
- * events between tasks and the divider window.
- */
- private SurfaceControl mSplitScreenDividerAnchor;
-
// Cached reference to some special tasks we tend to get a lot so we don't need to loop
// through the list to find them.
private Task mRootHomeTask;
@@ -422,8 +407,10 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// wasContained} restricts the preferred root task is set only when moving an existing
// root task to top instead of adding a new root task that may be too early (e.g. in the
// middle of launching or reparenting).
- if (moveToTop && child.isFocusableAndVisible()) {
- mPreferredTopFocusableRootTask = child;
+ final boolean isTopFocusableTask = moveToTop && child.isTopActivityFocusable();
+ if (isTopFocusableTask) {
+ mPreferredTopFocusableRootTask =
+ child.shouldBeVisible(null /* starting */) ? child : null;
} else if (mPreferredTopFocusableRootTask == child) {
mPreferredTopFocusableRootTask = null;
}
@@ -736,12 +723,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// Place root home tasks to the bottom.
layer = adjustRootTaskLayer(t, mTmpHomeChildren, layer);
layer = adjustRootTaskLayer(t, mTmpNormalChildren, layer);
- // TODO(b/207185041): Remove this divider workaround after we full remove leagacy split and
- // make app pair split only have single root then we can just attach the
- // divider to the single root task in shell.
- layer = Math.max(layer, SPLIT_DIVIDER_LAYER + 1);
adjustRootTaskLayer(t, mTmpAlwaysOnTopChildren, layer);
- t.setLayer(mSplitScreenDividerAnchor, SPLIT_DIVIDER_LAYER);
}
/**
@@ -769,19 +751,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
continue;
}
- final Task childTask = child.asTask();
- final boolean inAdjacentTask = childTask != null
- && child.inMultiWindowMode()
- && childTask.getRootTask().getAdjacentTaskFragment() != null;
-
- if (inAdjacentTask) {
- hasAdjacentTask = true;
- } else if (hasAdjacentTask && startLayer < SPLIT_DIVIDER_LAYER) {
- // Task on top of adjacent tasks should be higher than split divider layer so
- // set it as start.
- startLayer = SPLIT_DIVIDER_LAYER + 1;
- }
-
child.assignLayer(t, startLayer++);
}
@@ -808,31 +777,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
return activity != null ? activity.createRemoteAnimationTarget(record) : null;
}
- SurfaceControl getSplitScreenDividerAnchor() {
- return mSplitScreenDividerAnchor;
- }
-
- @Override
- void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
- if (getParent() != null) {
- super.onParentChanged(newParent, oldParent, () -> {
- mSplitScreenDividerAnchor = makeChildSurface(null)
- .setName("splitScreenDividerAnchor")
- .setCallsite("TaskDisplayArea.onParentChanged")
- .build();
-
- getSyncTransaction()
- .show(mSplitScreenDividerAnchor);
- });
- } else {
- super.onParentChanged(newParent, oldParent);
- mWmService.mTransactionFactory.get()
- .remove(mSplitScreenDividerAnchor)
- .apply();
- mSplitScreenDividerAnchor = null;
- }
- }
-
void setBackgroundColor(@ColorInt int colorInt) {
setBackgroundColor(colorInt, false /* restore */);
}
@@ -878,12 +822,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
setBackgroundColor(mBackgroundColor, true /* restore */);
}
- if (mSplitScreenDividerAnchor == null) {
- return;
- }
-
- // As TaskDisplayArea is getting a new surface, reparent and reorder the child surfaces.
- t.reparent(mSplitScreenDividerAnchor, mSurfaceControl);
reassignLayer(t);
scheduleAnimation();
}
@@ -1176,7 +1114,10 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// If a task is launching from a created-by-organizer task, it should be launched into the
// same created-by-organizer task as well. Unless, the candidate task is already positioned
// in the another adjacent task.
- if (sourceTask != null) {
+ if (sourceTask != null && (candidateTask == null
+ // A pinned task relaunching should be handled by its task organizer. Skip fallback
+ // launch target of a pinned task from source task.
+ || candidateTask.getWindowingMode() != WINDOWING_MODE_PINNED)) {
Task launchTarget = sourceTask.getCreatedByOrganizerTask();
if (launchTarget != null && launchTarget.getAdjacentTaskFragment() != null) {
if (candidateTask != null) {
@@ -1906,15 +1847,12 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
task.remove(false /* withTransition */, "removeTaskDisplayArea");
} else {
// Reparent task to corresponding launch root or display area.
- final WindowContainer launchRoot =
- task.supportsSplitScreenWindowingModeInDisplayArea(toDisplayArea)
- ? toDisplayArea.getLaunchRootTask(
+ final WindowContainer launchRoot = toDisplayArea.getLaunchRootTask(
task.getWindowingMode(),
task.getActivityType(),
null /* options */,
null /* sourceTask */,
- 0 /* launchFlags */)
- : null;
+ 0 /* launchFlags */);
task.reparent(launchRoot == null ? toDisplayArea : launchRoot, POSITION_TOP);
// Set the windowing mode to undefined by default to let the root task inherited the
@@ -2002,7 +1940,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
continue;
}
final Task rootTask = child.asTask();
- pw.println(doublePrefix + "* " + rootTask);
+ pw.println(doublePrefix + "* " + rootTask.toFullString());
rootTask.dump(pw, triplePrefix, dumpAll);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 1b0c01816f73..f8a9d4665acc 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -214,14 +214,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
private TaskFragment mAdjacentTaskFragment;
/**
- * Whether to move adjacent task fragment together when re-positioning.
- *
- * @see #mAdjacentTaskFragment
- */
- // TODO(b/207185041): Remove this once having a single-top root for split screen.
- boolean mMoveAdjacentTogether;
-
- /**
* Prevents duplicate calls to onTaskAppeared.
*/
boolean mTaskFragmentAppearedSent;
@@ -373,15 +365,14 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return service.mWindowOrganizerController.getTaskFragment(token);
}
- void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment, boolean moveTogether) {
+ void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment) {
if (mAdjacentTaskFragment == taskFragment) {
return;
}
resetAdjacentTaskFragment();
if (taskFragment != null) {
mAdjacentTaskFragment = taskFragment;
- mMoveAdjacentTogether = moveTogether;
- taskFragment.setAdjacentTaskFragment(this, moveTogether);
+ taskFragment.setAdjacentTaskFragment(this);
}
}
@@ -390,11 +381,9 @@ class TaskFragment extends WindowContainer<WindowContainer> {
if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
mAdjacentTaskFragment.mAdjacentTaskFragment = null;
mAdjacentTaskFragment.mDelayLastActivityRemoval = false;
- mAdjacentTaskFragment.mMoveAdjacentTogether = false;
}
mAdjacentTaskFragment = null;
mDelayLastActivityRemoval = false;
- mMoveAdjacentTogether = false;
}
void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
@@ -1601,7 +1590,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
if (prev.attachedToProcess()) {
if (shouldAutoPip) {
boolean didAutoPip = mAtmService.enterPictureInPictureMode(
- prev, prev.pictureInPictureArgs);
+ prev, prev.pictureInPictureArgs, false /* fromClient */);
ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode "
+ "directly: %s, didAutoPip: %b", prev, didAutoPip);
} else {
@@ -1639,8 +1628,11 @@ class TaskFragment extends WindowContainer<WindowContainer> {
} else {
prev.schedulePauseTimeout();
- // Unset readiness since we now need to wait until this pause is complete.
- mTransitionController.setReady(this, false /* ready */);
+ // All activities will be stopped when sleeping, don't need to wait for pause.
+ if (!uiSleeping) {
+ // Unset readiness since we now need to wait until this pause is complete.
+ mTransitionController.setReady(this, false /* ready */);
+ }
return true;
}
@@ -1934,19 +1926,19 @@ class TaskFragment extends WindowContainer<WindowContainer> {
if (!mAtmService.mSupportsMultiWindow) {
return false;
}
- final Task task = getTask();
- if (task == null) {
+ if (tda == null) {
return false;
}
- if (tda == null) {
+ final Task task = getTask();
+ if (task == null) {
return false;
}
- if (!getTask().isResizeable() && !tda.supportsNonResizableMultiWindow()) {
+ if (!task.isResizeable() && !tda.supportsNonResizableMultiWindow()) {
// Not support non-resizable in multi window.
return false;
}
- final ActivityRecord rootActivity = getTask().getRootActivity();
+ final ActivityRecord rootActivity = task.getRootActivity();
return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight,
rootActivity != null ? rootActivity.info : null);
}
@@ -2108,13 +2100,14 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density);
+ final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density + 0.5f);
inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy)
? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp)
: overrideScreenWidthDp;
}
if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density);
+ final int overrideScreenHeightDp =
+ (int) (mTmpStableBounds.height() / density + 0.5f);
inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy)
? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp)
: overrideScreenHeightDp;
@@ -2133,8 +2126,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
if (WindowConfiguration.isFloating(windowingMode) && !inPipTransition) {
// For floating tasks, calculate the smallest width from the bounds of the
// task, because they should not be affected by insets.
- inOutConfig.smallestScreenWidthDp = (int) (
- Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
+ inOutConfig.smallestScreenWidthDp = (int) (0.5f
+ + Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
} else if (isEmbedded()) {
// For embedded TFs, the smallest width should be updated. Otherwise, inherit
// from the parent task would result in applications loaded wrong resource.
@@ -2153,8 +2146,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
// For calculating screen layout, we need to use the non-decor inset screen area for the
// calculation for compatibility reasons, i.e. screen area without system bars that
// could never go away in Honeycomb.
- int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
- int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
+ int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density + 0.5f);
+ int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density + 0.5f);
// Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is
// undefined so it can't be used.
if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
@@ -2550,7 +2543,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
if (!hasChild()) {
return false;
}
- return isExitAnimationRunningSelfOrChild() || inTransition();
+ return isExitAnimationRunningSelfOrChild();
}
@Override
@@ -2635,6 +2628,32 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds();
}
+ String toFullString() {
+ final StringBuilder sb = new StringBuilder(128);
+ sb.append(this);
+ sb.setLength(sb.length() - 1); // Remove tail '}'.
+ if (mTaskFragmentOrganizerUid != INVALID_UID) {
+ sb.append(" organizerUid=");
+ sb.append(mTaskFragmentOrganizerUid);
+ }
+ if (mTaskFragmentOrganizerProcessName != null) {
+ sb.append(" organizerProc=");
+ sb.append(mTaskFragmentOrganizerProcessName);
+ }
+ if (mAdjacentTaskFragment != null) {
+ sb.append(" adjacent=");
+ sb.append(mAdjacentTaskFragment);
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ return "TaskFragment{" + Integer.toHexString(System.identityHashCode(this))
+ + " mode=" + WindowConfiguration.windowingModeToString(getWindowingMode()) + "}";
+ }
+
boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll,
boolean dumpClient, String dumpPackage, final boolean needSep, Runnable header) {
boolean printed = false;
@@ -2673,7 +2692,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
- pw.print(prefix); pw.print("* "); pw.println(this);
+ pw.print(prefix); pw.print("* "); pw.println(toFullString());
final Rect bounds = getRequestedOverrideBounds();
if (!bounds.isEmpty()) {
pw.println(prefix + " mBounds=" + bounds);
@@ -2694,10 +2713,11 @@ class TaskFragment extends WindowContainer<WindowContainer> {
final String doublePrefix = prefix + " ";
for (int i = mChildren.size() - 1; i >= 0; i--) {
final WindowContainer<?> child = mChildren.get(i);
- pw.println(prefix + "* " + child);
+ final TaskFragment tf = child.asTaskFragment();
+ pw.println(prefix + "* " + (tf != null ? tf.toFullString() : child));
// Only dump non-activity because full activity info is already printed by
// RootWindowContainer#dumpActivities.
- if (child.asActivityRecord() == null) {
+ if (tf != null) {
child.dump(pw, doublePrefix, dumpAll);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 2546177ec367..392d4c2f772b 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -471,6 +471,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
.setException(exception)
.build();
mPendingTaskFragmentEvents.add(pendingEvent);
+ // Make sure the error event will be dispatched if there are no other changes.
+ mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal();
}
void onActivityReparentToTask(ActivityRecord activity) {
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 6e84681f0ab5..801665862d29 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -36,6 +36,7 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -55,7 +56,6 @@ import com.android.internal.util.ArrayUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -94,7 +94,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
* lifecycle order since we may be updating the visibility of task surface controls in a pending
* transaction before they are presented to the task org.
*/
- private class TaskOrganizerCallbacks {
+ private static class TaskOrganizerCallbacks {
final ITaskOrganizer mTaskOrganizer;
final Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
@@ -123,7 +123,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
-
void onTaskVanished(Task task) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task vanished taskId=%d", task.mTaskId);
final RunningTaskInfo taskInfo = task.getTaskInfo();
@@ -173,11 +172,160 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
+ /**
+ * Maintains a list of all the pending events for a given {@link android.window.TaskOrganizer}
+ */
+ static final class TaskOrganizerPendingEventsQueue {
+ private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
+ private final TaskOrganizerState mOrganizerState;
+ private RunningTaskInfo mTmpTaskInfo;
+ // Pending task events due to layout deferred.
+ private final ArrayList<PendingTaskEvent> mPendingTaskEvents = new ArrayList<>();
+
+ TaskOrganizerPendingEventsQueue(TaskOrganizerState taskOrganizerState) {
+ mOrganizerState = taskOrganizerState;
+ }
+
+ @VisibleForTesting
+ public ArrayList<PendingTaskEvent> getPendingEventList() {
+ return mPendingTaskEvents;
+ }
+
+ int numPendingTaskEvents() {
+ return mPendingTaskEvents.size();
+ }
+
+ void clearPendingTaskEvents() {
+ mPendingTaskEvents.clear();
+ }
+
+ void addPendingTaskEvent(PendingTaskEvent event) {
+ mPendingTaskEvents.add(event);
+ }
+
+ void removePendingTaskEvent(PendingTaskEvent event) {
+ mPendingTaskEvents.remove(event);
+ }
+
+ /**
+ * Removes all the pending task events for the given {@code task}.
+ *
+ * @param task
+ * @return true if a {@link PendingTaskEvent#EVENT_APPEARED} is still pending for the given
+ * {code task}.
+ */
+ boolean removePendingTaskEvents(Task task) {
+ boolean foundPendingAppearedEvents = false;
+ for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) {
+ PendingTaskEvent entry = mPendingTaskEvents.get(i);
+ if (task.mTaskId == entry.mTask.mTaskId) {
+ // This task is vanished so remove all pending event of it.
+ mPendingTaskEvents.remove(i);
+
+ if (entry.mEventType == PendingTaskEvent.EVENT_APPEARED) {
+ foundPendingAppearedEvents = true;
+ }
+ }
+ }
+ return foundPendingAppearedEvents;
+ }
+
+ @Nullable
+ private PendingTaskEvent getPendingTaskEvent(Task task, int type) {
+ for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) {
+ PendingTaskEvent entry = mPendingTaskEvents.get(i);
+ if (task.mTaskId == entry.mTask.mTaskId && type == entry.mEventType) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ @Nullable
+ PendingTaskEvent getPendingLifecycleTaskEvent(Task task) {
+ for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) {
+ PendingTaskEvent entry = mPendingTaskEvents.get(i);
+ if (task.mTaskId == entry.mTask.mTaskId && entry.isLifecycleEvent()) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ void dispatchPendingEvents() {
+ if (mPendingTaskEvents.isEmpty()) {
+ return;
+ }
+ for (int i = 0, n = mPendingTaskEvents.size(); i < n; i++) {
+ dispatchPendingEvent(mPendingTaskEvents.get(i));
+ }
+ mPendingTaskEvents.clear();
+ }
+
+ private void dispatchPendingEvent(PendingTaskEvent event) {
+ final Task task = event.mTask;
+ switch (event.mEventType) {
+ case PendingTaskEvent.EVENT_APPEARED:
+ if (task.taskAppearedReady()) {
+ mOrganizerState.mOrganizer.onTaskAppeared(task);
+ }
+ break;
+ case PendingTaskEvent.EVENT_VANISHED:
+ mOrganizerState.mOrganizer.onTaskVanished(task);
+ mLastSentTaskInfos.remove(task);
+ break;
+ case PendingTaskEvent.EVENT_INFO_CHANGED:
+ dispatchTaskInfoChanged(event.mTask, event.mForce);
+ break;
+ case PendingTaskEvent.EVENT_ROOT_BACK_PRESSED:
+ mOrganizerState.mOrganizer.onBackPressedOnTaskRoot(task);
+ break;
+ }
+ }
+
+ private void dispatchTaskInfoChanged(Task task, boolean force) {
+ RunningTaskInfo lastInfo = mLastSentTaskInfos.get(task);
+ if (mTmpTaskInfo == null) {
+ mTmpTaskInfo = new RunningTaskInfo();
+ }
+ mTmpTaskInfo.configuration.unset();
+ task.fillTaskInfo(mTmpTaskInfo);
+
+ boolean changed = !mTmpTaskInfo
+ .equalsForTaskOrganizer(lastInfo)
+ || !configurationsAreEqualForOrganizer(
+ mTmpTaskInfo.configuration,
+ lastInfo.configuration);
+ if (!(changed || force)) {
+ // mTmpTaskInfo will be reused next time.
+ return;
+ }
+ final RunningTaskInfo newInfo = mTmpTaskInfo;
+ mLastSentTaskInfos.put(task,
+ mTmpTaskInfo);
+ // Since we've stored this, clean up the reference so a new one will be created next
+ // time.
+ // Transferring it this way means we only have to construct new RunningTaskInfos when
+ // they change.
+ mTmpTaskInfo = null;
+
+ if (task.isOrganized()) {
+ // Because we defer sending taskAppeared() until the app has drawn, we may receive a
+ // configuration change before the state actually has the task registered. As such
+ // we should ignore these change events to the organizer until taskAppeared(). If
+ // the task was created by the organizer, then we always send the info change.
+ mOrganizerState.mOrganizer.onTaskInfoChanged(task, newInfo);
+ }
+ }
+ }
+
@VisibleForTesting
class TaskOrganizerState {
private final TaskOrganizerCallbacks mOrganizer;
private final DeathRecipient mDeathRecipient;
private final ArrayList<Task> mOrganizedTasks = new ArrayList<>();
+ private final TaskOrganizerPendingEventsQueue mPendingEventsQueue;
private final int mUid;
TaskOrganizerState(ITaskOrganizer organizer, int uid) {
@@ -187,6 +335,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
: mService.mWindowManager.mAnimator::addAfterPrepareSurfacesRunnable;
mOrganizer = new TaskOrganizerCallbacks(organizer, deferTaskOrgCallbacksConsumer);
mDeathRecipient = new DeathRecipient(organizer);
+ mPendingEventsQueue = new TaskOrganizerPendingEventsQueue(this);
try {
organizer.asBinder().linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
@@ -200,6 +349,11 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return mDeathRecipient;
}
+ @VisibleForTesting
+ TaskOrganizerPendingEventsQueue getPendingEventsQueue() {
+ return mPendingEventsQueue;
+ }
+
/**
* Register this task with this state, but doesn't trigger the task appeared callback to
* the organizer.
@@ -263,8 +417,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
// updateTaskOrganizerState should remove the task from the list, but still
// check it again to avoid while-loop isn't terminate.
if (removeTask(t, t.mRemoveWithTaskOrganizer)) {
- TaskOrganizerController.this.onTaskVanishedInternal(
- mOrganizer.mTaskOrganizer, t);
+ TaskOrganizerController.this.onTaskVanishedInternal(this, t);
}
}
if (mService.getTransitionController().isShellTransitionsEnabled()) {
@@ -278,8 +431,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
}
- // Remove organizer state after removing tasks so we get a chance to send
- // onTaskVanished.
+ // Pending events queue for this organizer need to be cleared because this organizer
+ // has either died or unregistered itself.
+ mPendingEventsQueue.clearPendingTaskEvents();
mTaskOrganizerStates.remove(mOrganizer.getBinder());
}
@@ -320,14 +474,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
// List of task organizers by priority
private final LinkedList<ITaskOrganizer> mTaskOrganizers = new LinkedList<>();
- private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();
- private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
- // Pending task events due to layout deferred.
- private final ArrayList<PendingTaskEvent> mPendingTaskEvents = new ArrayList<>();
+ private final ArrayMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new ArrayMap<>();
// Set of organized tasks (by taskId) that dispatch back pressed to their organizers
private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet();
- private RunningTaskInfo mTmpTaskInfo;
private Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
TaskOrganizerController(ActivityTaskManagerService atm) {
@@ -354,11 +504,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mDeferTaskOrgCallbacksConsumer = consumer;
}
- @VisibleForTesting
- ArrayList<PendingTaskEvent> getPendingEventList() {
- return mPendingTaskEvents;
- }
-
/**
* Register a TaskOrganizer to manage tasks as they enter the a supported windowing mode.
*/
@@ -382,11 +527,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mService.mRootWindowContainer.forAllTasks((task) -> {
boolean returnTask = !task.mCreatedByOrganizer;
task.updateTaskOrganizerState(returnTask /* skipTaskAppeared */);
- if (returnTask) {
- SurfaceControl outSurfaceControl = state.addTaskWithoutCallback(task,
+ // It is possible for the task to not yet have a surface control, so ensure that
+ // the update succeeded in setting the organizer for the task before returning
+ if (task.isOrganized() && returnTask) {
+ SurfaceControl taskLeash = state.addTaskWithoutCallback(task,
"TaskOrganizerController.registerTaskOrganizer");
- taskInfos.add(
- new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl));
+ taskInfos.add(new TaskAppearedInfo(task.getTaskInfo(), taskLeash));
}
});
};
@@ -592,10 +738,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
void onTaskAppeared(ITaskOrganizer organizer, Task task) {
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
if (state != null && state.addTask(task)) {
- PendingTaskEvent pending = getPendingTaskEvent(task, PendingTaskEvent.EVENT_APPEARED);
+ final TaskOrganizerPendingEventsQueue pendingEvents =
+ state.mPendingEventsQueue;
+ PendingTaskEvent pending = pendingEvents.getPendingTaskEvent(task,
+ PendingTaskEvent.EVENT_APPEARED);
if (pending == null) {
- pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_APPEARED);
- mPendingTaskEvents.add(pending);
+ pendingEvents.addPendingTaskEvent(new PendingTaskEvent(task,
+ PendingTaskEvent.EVENT_APPEARED));
}
}
}
@@ -603,26 +752,25 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
void onTaskVanished(ITaskOrganizer organizer, Task task) {
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
if (state != null && state.removeTask(task, task.mRemoveWithTaskOrganizer)) {
- onTaskVanishedInternal(organizer, task);
+ onTaskVanishedInternal(state, task);
}
}
- private void onTaskVanishedInternal(ITaskOrganizer organizer, Task task) {
- for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) {
- PendingTaskEvent entry = mPendingTaskEvents.get(i);
- if (task.mTaskId == entry.mTask.mTaskId && entry.mTaskOrg == organizer) {
- // This task is vanished so remove all pending event of it.
- mPendingTaskEvents.remove(i);
- if (entry.mEventType == PendingTaskEvent.EVENT_APPEARED) {
- // If task appeared callback still pend, ignore this callback too.
- return;
- }
- }
+ private void onTaskVanishedInternal(TaskOrganizerState organizerState, Task task) {
+ if (organizerState == null) {
+ Slog.i(TAG, "cannot send onTaskVanished because organizer state is not "
+ + "present for this organizer");
+ return;
}
-
- PendingTaskEvent pending =
- new PendingTaskEvent(task, organizer, PendingTaskEvent.EVENT_VANISHED);
- mPendingTaskEvents.add(pending);
+ TaskOrganizerPendingEventsQueue pendingEventsQueue =
+ organizerState.mPendingEventsQueue;
+ boolean hadPendingAppearedEvents =
+ pendingEventsQueue.removePendingTaskEvents(task);
+ if (hadPendingAppearedEvents) {
+ return;
+ }
+ pendingEventsQueue.addPendingTaskEvent(new PendingTaskEvent(task,
+ organizerState.mOrganizer.mTaskOrganizer, PendingTaskEvent.EVENT_VANISHED));
}
@Override
@@ -690,48 +838,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
}
void dispatchPendingEvents() {
- if (mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()
- || mPendingTaskEvents.isEmpty()) {
+ if (mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
return;
}
-
- for (int i = 0, n = mPendingTaskEvents.size(); i < n; i++) {
- PendingTaskEvent event = mPendingTaskEvents.get(i);
- final Task task = event.mTask;
- final TaskOrganizerState state;
- switch (event.mEventType) {
- case PendingTaskEvent.EVENT_APPEARED:
- state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder());
- if (state != null && task.taskAppearedReady()) {
- state.mOrganizer.onTaskAppeared(task);
- }
- break;
- case PendingTaskEvent.EVENT_VANISHED:
- // TaskOrganizerState cannot be used here because it might have already been
- // removed.
- // The state is removed when an organizer dies or is unregistered. In order to
- // send the pending vanished task events, the mTaskOrg from event is used.
- // These events should not ideally be sent and will be removed as part of
- // b/224812558.
- try {
- event.mTaskOrg.onTaskVanished(task.getTaskInfo());
- } catch (RemoteException ex) {
- Slog.e(TAG, "Exception sending onTaskVanished callback", ex);
- }
- mLastSentTaskInfos.remove(task);
- break;
- case PendingTaskEvent.EVENT_INFO_CHANGED:
- dispatchTaskInfoChanged(event.mTask, event.mForce);
- break;
- case PendingTaskEvent.EVENT_ROOT_BACK_PRESSED:
- state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder());
- if (state != null) {
- state.mOrganizer.onBackPressedOnTaskRoot(task);
- }
- break;
- }
+ for (int taskOrgIdx = 0; taskOrgIdx < mTaskOrganizerStates.size(); taskOrgIdx++) {
+ TaskOrganizerState taskOrganizerState = mTaskOrganizerStates.valueAt(taskOrgIdx);
+ taskOrganizerState.mPendingEventsQueue.dispatchPendingEvents();
}
- mPendingTaskEvents.clear();
}
void reportImeDrawnOnTask(Task task) {
@@ -750,20 +863,30 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
// Skip if task still not appeared.
return;
}
- if (force && mPendingTaskEvents.isEmpty()) {
+ final TaskOrganizerState taskOrganizerState =
+ mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder());
+ final TaskOrganizerPendingEventsQueue pendingEventsQueue =
+ taskOrganizerState.mPendingEventsQueue;
+ if (pendingEventsQueue == null) {
+ Slog.i(TAG, "cannot send onTaskInfoChanged because pending events queue is not "
+ + "present for this organizer");
+ return;
+ }
+ if (force && pendingEventsQueue.numPendingTaskEvents() == 0) {
// There are task-info changed events do not result in
// - RootWindowContainer#performSurfacePlacementNoTrace OR
// - WindowAnimator#animate
// For instance, when an app requesting aspect ratio change when in PiP mode.
// To solve this, we directly dispatch the pending event if there are no events queued (
// otherwise, all pending events should be dispatched on next drawn).
- dispatchTaskInfoChanged(task, true /* force */);
+ pendingEventsQueue.dispatchTaskInfoChanged(task, true /* force */);
return;
}
// Defer task info reporting while layout is deferred. This is because layout defer
// blocks tend to do lots of re-ordering which can mess up animations in receivers.
- PendingTaskEvent pending = getPendingLifecycleTaskEvent(task);
+ PendingTaskEvent pending = pendingEventsQueue
+ .getPendingLifecycleTaskEvent(task);
if (pending == null) {
pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_INFO_CHANGED);
} else {
@@ -774,45 +897,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return;
}
// Remove and add for re-ordering.
- mPendingTaskEvents.remove(pending);
+ pendingEventsQueue.removePendingTaskEvent(pending);
}
pending.mForce |= force;
- mPendingTaskEvents.add(pending);
- }
-
- private void dispatchTaskInfoChanged(Task task, boolean force) {
- RunningTaskInfo lastInfo = mLastSentTaskInfos.get(task);
- if (mTmpTaskInfo == null) {
- mTmpTaskInfo = new RunningTaskInfo();
- }
- mTmpTaskInfo.configuration.unset();
- task.fillTaskInfo(mTmpTaskInfo);
-
- boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo)
- || !configurationsAreEqualForOrganizer(
- mTmpTaskInfo.configuration, lastInfo.configuration);
- if (!(changed || force)) {
- // mTmpTaskInfo will be reused next time.
- return;
- }
- final RunningTaskInfo newInfo = mTmpTaskInfo;
- mLastSentTaskInfos.put(task, mTmpTaskInfo);
- // Since we've stored this, clean up the reference so a new one will be created next time.
- // Transferring it this way means we only have to construct new RunningTaskInfos when they
- // change.
- mTmpTaskInfo = null;
-
- if (task.isOrganized()) {
- // Because we defer sending taskAppeared() until the app has drawn, we may receive a
- // configuration change before the state actually has the task registered. As such we
- // should ignore these change events to the organizer until taskAppeared(). If the task
- // was created by the organizer, then we always send the info change.
- final TaskOrganizerState state = mTaskOrganizerStates.get(
- task.mTaskOrganizer.asBinder());
- if (state != null) {
- state.mOrganizer.onTaskInfoChanged(task, newInfo);
- }
- }
+ pendingEventsQueue.addPendingTaskEvent(pending);
}
@Override
@@ -1018,50 +1106,36 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|| !mInterceptBackPressedOnRootTasks.contains(task.mTaskId)) {
return false;
}
+ final TaskOrganizerPendingEventsQueue pendingEventsQueue =
+ mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder())
+ .mPendingEventsQueue;
+ if (pendingEventsQueue == null) {
+ Slog.w(TAG, "cannot get handle BackPressedOnTaskRoot because organizerState is "
+ + "not present");
+ return false;
+ }
PendingTaskEvent pendingVanished =
- getPendingTaskEvent(task, PendingTaskEvent.EVENT_VANISHED);
+ pendingEventsQueue.getPendingTaskEvent(task,
+ PendingTaskEvent.EVENT_VANISHED);
if (pendingVanished != null) {
// This task will vanish before this callback so just ignore.
return false;
}
- PendingTaskEvent pending = getPendingTaskEvent(
+ PendingTaskEvent pending = pendingEventsQueue.getPendingTaskEvent(
task, PendingTaskEvent.EVENT_ROOT_BACK_PRESSED);
if (pending == null) {
pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_ROOT_BACK_PRESSED);
} else {
// Pending already exist, remove and add for re-ordering.
- mPendingTaskEvents.remove(pending);
+ pendingEventsQueue.removePendingTaskEvent(pending);
}
- mPendingTaskEvents.add(pending);
+ pendingEventsQueue.addPendingTaskEvent(pending);
mService.mWindowManager.mWindowPlacerLocked.requestTraversal();
return true;
}
- @Nullable
- private PendingTaskEvent getPendingTaskEvent(Task task, int type) {
- for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) {
- PendingTaskEvent entry = mPendingTaskEvents.get(i);
- if (task.mTaskId == entry.mTask.mTaskId && type == entry.mEventType) {
- return entry;
- }
- }
- return null;
- }
-
- @VisibleForTesting
- @Nullable
- PendingTaskEvent getPendingLifecycleTaskEvent(Task task) {
- for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) {
- PendingTaskEvent entry = mPendingTaskEvents.get(i);
- if (task.mTaskId == entry.mTask.mTaskId && entry.isLifecycleEvent()) {
- return entry;
- }
- }
- return null;
- }
-
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.print(prefix); pw.println("TaskOrganizerController:");
@@ -1084,4 +1158,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
TaskOrganizerState getTaskOrganizerState(IBinder taskOrganizer) {
return mTaskOrganizerStates.get(taskOrganizer);
}
+
+ @VisibleForTesting
+ TaskOrganizerPendingEventsQueue getTaskOrganizerPendingEvents(IBinder taskOrganizer) {
+ return mTaskOrganizerStates.get(taskOrganizer).mPendingEventsQueue;
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 47c397d12720..03ca4fd49a96 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -41,7 +41,6 @@ import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.InputConfig;
-import android.os.Process;
import android.os.RemoteException;
import android.os.Trace;
import android.util.DisplayMetrics;
@@ -222,8 +221,8 @@ class TaskPositioner implements IBinder.DeathRecipient {
mDragWindowHandle.token = mClientChannel.getToken();
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
- mDragWindowHandle.ownerPid = Process.myPid();
- mDragWindowHandle.ownerUid = Process.myUid();
+ mDragWindowHandle.ownerPid = WindowManagerService.MY_PID;
+ mDragWindowHandle.ownerUid = WindowManagerService.MY_UID;
mDragWindowHandle.scaleFactor = 1.0f;
// When dragging the window around, we do not want to steal focus for the window.
mDragWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 814656db9fa0..534616fb7207 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -47,6 +47,7 @@ import android.graphics.RenderNode;
import android.hardware.HardwareBuffer;
import android.os.Environment;
import android.os.Handler;
+import android.os.Trace;
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
@@ -391,8 +392,10 @@ class TaskSnapshotController {
SurfaceControl.ScreenshotHardwareBuffer createTaskSnapshot(@NonNull Task task,
TaskSnapshot.Builder builder) {
Point taskSize = new Point();
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "createTaskSnapshot");
final SurfaceControl.ScreenshotHardwareBuffer taskSnapshot = createTaskSnapshot(task,
mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize, builder);
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
builder.setTaskSize(taskSize);
return taskSnapshot;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f6f9020555d8..91f69a55d400 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -27,6 +27,7 @@ import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
@@ -45,6 +46,8 @@ import static android.view.WindowManager.TransitionType;
import static android.view.WindowManager.transitTypeToString;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
+import static android.window.TransitionInfo.FLAG_IS_EMBEDDED;
+import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD;
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;
@@ -52,6 +55,8 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
@@ -80,11 +85,9 @@ import android.window.TransitionInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
-import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.LocalServices;
import com.android.server.inputmethod.InputMethodManagerInternal;
import java.lang.annotation.Retention;
@@ -127,12 +130,18 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
*/
private static final int STATE_ABORT = 3;
+ /**
+ * This transition has finished playing successfully.
+ */
+ private static final int STATE_FINISHED = 4;
+
@IntDef(prefix = { "STATE_" }, value = {
STATE_PENDING,
STATE_COLLECTING,
STATE_STARTED,
STATE_PLAYING,
- STATE_ABORT
+ STATE_ABORT,
+ STATE_FINISHED
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionState {}
@@ -189,6 +198,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
private boolean mNavBarAttachedToApp = false;
private int mRecentsDisplayId = INVALID_DISPLAY;
+ /** The delay for light bar appearance animation. */
+ long mStatusBarTransitionDelay;
+
/** @see #setCanPipOnFinish */
private boolean mCanPipOnFinish = true;
@@ -198,6 +210,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mFlags = flags;
mController = controller;
mSyncEngine = syncEngine;
+
+ controller.mTransitionTracer.logState(this);
}
void addFlag(int flag) {
@@ -210,10 +224,28 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mTransientLaunches = new ArrayMap<>();
}
mTransientLaunches.put(activity, restoreBelow);
+ setTransientLaunchToChanges(activity);
+
+ if (restoreBelow != null) {
+ final ChangeInfo info = mChanges.get(restoreBelow);
+ if (info != null) {
+ info.mFlags |= ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH;
+ }
+ }
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Transition %d: Set %s as "
+ "transient-launch", mSyncId, activity);
}
+ boolean isTransientHide(@NonNull Task task) {
+ if (mTransientLaunches == null) return false;
+ for (int i = 0; i < mTransientLaunches.size(); ++i) {
+ if (mTransientLaunches.valueAt(i) == task) {
+ return true;
+ }
+ }
+ return false;
+ }
+
boolean isTransientLaunch(@NonNull ActivityRecord activity) {
return mTransientLaunches != null && mTransientLaunches.containsKey(activity);
}
@@ -238,6 +270,25 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
info.mFlags = info.mFlags | ChangeInfo.FLAG_SEAMLESS_ROTATION;
}
+ /**
+ * Only set flag to the parent tasks and activity itself.
+ */
+ private void setTransientLaunchToChanges(@NonNull WindowContainer wc) {
+ for (WindowContainer curr = wc; curr != null && mChanges.containsKey(curr);
+ curr = curr.getParent()) {
+ if (curr.asTask() == null && curr.asActivityRecord() == null) {
+ return;
+ }
+ final ChangeInfo info = mChanges.get(curr);
+ info.mFlags = info.mFlags | ChangeInfo.FLAG_TRANSIENT_LAUNCH;
+ }
+ }
+
+ @TransitionState
+ int getState() {
+ return mState;
+ }
+
@VisibleForTesting
int getSyncId() {
return mSyncId;
@@ -248,6 +299,20 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
return mFlags;
}
+ @VisibleForTesting
+ SurfaceControl.Transaction getStartTransaction() {
+ return mStartTransaction;
+ }
+
+ @VisibleForTesting
+ SurfaceControl.Transaction getFinishTransaction() {
+ return mFinishTransaction;
+ }
+
+ private boolean isCollecting() {
+ return mState == STATE_COLLECTING || mState == STATE_STARTED;
+ }
+
/** Starts collecting phase. Once this starts, all relevant surface operations are sync. */
void startCollecting(long timeoutMs) {
if (mState != STATE_PENDING) {
@@ -255,6 +320,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
mState = STATE_COLLECTING;
mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG);
+
+ mController.mTransitionTracer.logState(this);
}
/**
@@ -272,6 +339,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Starting Transition %d",
mSyncId);
applyReady();
+
+ mController.mTransitionTracer.logState(this);
}
/**
@@ -281,7 +350,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (mState < STATE_COLLECTING) {
throw new IllegalStateException("Transition hasn't started collecting.");
}
- if (mSyncId < 0) return;
+ if (!isCollecting()) {
+ // Too late, transition already started playing, so don't collect.
+ return;
+ }
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Collecting in transition %d: %s",
mSyncId, wc);
// "snapshot" all parents (as potential promotion targets). Do this before checking
@@ -296,7 +368,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
}
if (mParticipants.contains(wc)) return;
- mSyncEngine.addToSyncSet(mSyncId, wc);
+ // Wallpaper is like in a static drawn state unless display may have changes, so exclude
+ // the case to reduce transition latency waiting for the unchanged wallpaper to redraw.
+ final boolean needSyncDraw = !isWallpaper(wc) || mParticipants.contains(wc.mDisplayContent);
+ if (needSyncDraw) {
+ mSyncEngine.addToSyncSet(mSyncId, wc);
+ }
ChangeInfo info = mChanges.get(wc);
if (info == null) {
info = new ChangeInfo(wc);
@@ -326,7 +403,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
* or waiting until after the animation to close).
*/
void collectExistenceChange(@NonNull WindowContainer wc) {
- if (mSyncId < 0) return;
+ if (mState >= STATE_PLAYING) {
+ // Too late to collect. Don't check too-early here since `collect` will check that.
+ return;
+ }
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Existence Changed in transition %d:"
+ " %s", mSyncId, wc);
collect(wc);
@@ -360,7 +440,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
*/
void setOverrideAnimation(TransitionInfo.AnimationOptions options,
@Nullable IRemoteCallback startCallback, @Nullable IRemoteCallback finishCallback) {
- if (mSyncId < 0) return;
+ if (!isCollecting()) return;
mOverrideOptions = options;
sendRemoteCallback(mClientAnimationStartCallback);
mClientAnimationStartCallback = startCallback;
@@ -378,7 +458,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
* The transition will wait for all groups to be ready.
*/
void setReady(WindowContainer wc, boolean ready) {
- if (mSyncId < 0) return;
+ if (!isCollecting() || mSyncId < 0) return;
mReadyTracker.setReadyFrom(wc, ready);
applyReady();
}
@@ -396,7 +476,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
* @see ReadyTracker#setAllReady.
*/
void setAllReady() {
- if (mSyncId < 0) return;
+ if (!isCollecting() || mSyncId < 0) return;
mReadyTracker.setAllReady();
applyReady();
}
@@ -419,7 +499,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
for (int i = mTargets.size() - 1; i >= 0; --i) {
final WindowContainer target = mTargets.get(i);
if (target.getParent() != null) {
- final SurfaceControl targetLeash = getLeashSurface(target);
+ final SurfaceControl targetLeash = getLeashSurface(target, null /* t */);
final SurfaceControl origParent = getOrigParentSurface(target);
// Ensure surfaceControls are re-parented back into the hierarchy.
t.reparent(targetLeash, origParent);
@@ -428,14 +508,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
t.setPosition(targetLeash, tmpPos.x, tmpPos.y);
final Rect clipRect;
// No need to clip the display in case seeing the clipped content when during the
- // display rotation.
- if (target.asDisplayContent() != null) {
+ // display rotation. No need to clip activities because they rely on clipping on
+ // task layers.
+ if (target.asDisplayContent() != null || target.asActivityRecord() != null) {
clipRect = null;
- } else if (target.asActivityRecord() != null) {
- // Always use parent bounds of activity because letterbox area (e.g. fixed
- // aspect ratio or size compat mode) should be included.
- clipRect = target.getParent().getRequestedOverrideBounds();
- clipRect.offset(-tmpPos.x, -tmpPos.y);
} else {
clipRect = target.getRequestedOverrideBounds();
clipRect.offset(-tmpPos.x, -tmpPos.y);
@@ -456,9 +532,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
// Need to update layers on involved displays since they were all paused while
// the animation played. This puts the layers back into the correct order.
- for (int i = displays.size() - 1; i >= 0; --i) {
- if (displays.valueAt(i) == null) continue;
- displays.valueAt(i).assignChildLayers(t);
+ mController.mBuildingFinishLayers = true;
+ try {
+ for (int i = displays.size() - 1; i >= 0; --i) {
+ if (displays.valueAt(i) == null) continue;
+ displays.valueAt(i).assignChildLayers(t);
+ }
+ } finally {
+ mController.mBuildingFinishLayers = false;
}
if (rootLeash.isValid()) {
t.reparent(rootLeash, null);
@@ -474,6 +555,58 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mCanPipOnFinish = canPipOnFinish;
}
+ private boolean didCommitTransientLaunch() {
+ if (mTransientLaunches == null) return false;
+ for (int j = 0; j < mTransientLaunches.size(); ++j) {
+ if (mTransientLaunches.keyAt(j).isVisibleRequested()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if pip-entry is possible after finishing and enter-pip if it is.
+ *
+ * @return true if we are *guaranteed* to enter-pip. This means we return false if there's
+ * a chance we won't thus legacy-entry (via pause+userLeaving) will return false.
+ */
+ private boolean checkEnterPipOnFinish(@NonNull ActivityRecord ar) {
+ if (!mCanPipOnFinish || !ar.isVisible() || ar.getTask() == null) return false;
+
+ if (ar.pictureInPictureArgs != null && ar.pictureInPictureArgs.isAutoEnterEnabled()) {
+ if (didCommitTransientLaunch()) {
+ // force enable pip-on-task-switch now that we've committed to actually launching
+ // to the transient activity.
+ ar.supportsEnterPipOnTaskSwitch = true;
+ }
+ return mController.mAtm.enterPictureInPictureMode(ar, ar.pictureInPictureArgs,
+ false /* fromClient */);
+ }
+
+ // Legacy pip-entry (not via isAutoEnterEnabled).
+ boolean canPip = ar.getDeferHidingClient();
+ if (!canPip && didCommitTransientLaunch()) {
+ // force enable pip-on-task-switch now that we've committed to actually launching to the
+ // transient activity, and then recalculate whether we can attempt pip.
+ ar.supportsEnterPipOnTaskSwitch = true;
+ canPip = ar.checkEnterPictureInPictureState(
+ "finishTransition", true /* beforeStopping */)
+ && ar.isState(RESUMED);
+ }
+ if (!canPip) return false;
+ try {
+ // Legacy PIP-enter requires pause event with user-leaving.
+ mController.mAtm.mTaskSupervisor.mUserLeaving = true;
+ ar.getTaskFragment().startPausing(false /* uiSleeping */,
+ null /* resuming */, "finishTransition");
+ } finally {
+ mController.mAtm.mTaskSupervisor.mUserLeaving = false;
+ }
+ // Return false anyway because there's no guarantee that the app will enter pip.
+ return false;
+ }
+
/**
* The transition has finished animating and is ready to finalize WM state. This should not
* be called directly; use {@link TransitionController#finishTransition} instead.
@@ -489,7 +622,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
// Commit all going-invisible containers
- boolean activitiesWentInvisible = false;
for (int i = 0; i < mParticipants.size(); ++i) {
final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
if (ar != null) {
@@ -501,32 +633,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// then doing commitVisibility here would actually be out-of-order and leave the
// activity in a bad state.
if (!visibleAtTransitionEnd && !ar.isVisibleRequested()) {
- boolean commitVisibility = true;
- if (mCanPipOnFinish && ar.isVisible() && ar.getTask() != null) {
- if (ar.pictureInPictureArgs != null
- && ar.pictureInPictureArgs.isAutoEnterEnabled()) {
- if (mTransientLaunches != null) {
- for (int j = 0; j < mTransientLaunches.size(); ++j) {
- if (mTransientLaunches.keyAt(j).isVisibleRequested()) {
- // force enable pip-on-task-switch now that we've committed
- // to actually launching to the transient activity.
- ar.supportsEnterPipOnTaskSwitch = true;
- break;
- }
- }
- }
- mController.mAtm.enterPictureInPictureMode(ar, ar.pictureInPictureArgs);
- // Avoid commit visibility to false here, or else we will get a sudden
- // "flash" / surface going invisible for a split second.
- commitVisibility = false;
- } else if (ar.getDeferHidingClient()) {
- // Legacy PIP-enter requires pause event with user-leaving.
- mController.mAtm.mTaskSupervisor.mUserLeaving = true;
- ar.getTaskFragment().startPausing(false /* uiSleeping */,
- null /* resuming */, "finishTransition");
- mController.mAtm.mTaskSupervisor.mUserLeaving = false;
- }
- }
+ final boolean commitVisibility = !checkEnterPipOnFinish(ar);
+ // Avoid commit visibility if entering pip or else we will get a sudden
+ // "flash" / surface going invisible for a split second.
if (commitVisibility) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" Commit activity becoming invisible: %s", ar);
@@ -540,7 +649,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
ar.commitVisibility(false /* visible */, false /* performLayout */,
true /* fromTransition */);
- activitiesWentInvisible = true;
}
}
if (mChanges.get(ar).mVisible != visibleAtTransitionEnd) {
@@ -567,12 +675,23 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mController.dispatchLegacyAppTransitionFinished(ar);
}
}
- if (activitiesWentInvisible) {
- // Always schedule stop processing when transition finishes because activities don't
- // stop while they are in a transition thus their stop could still be pending.
- mController.mAtm.mTaskSupervisor
- .scheduleProcessStoppingAndFinishingActivitiesIfNeeded();
+
+ // Update the input-sink (touch-blocking) state now that the animation is finished.
+ SurfaceControl.Transaction inputSinkTransaction = null;
+ for (int i = 0; i < mParticipants.size(); ++i) {
+ final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
+ if (ar == null || !ar.isVisible() || ar.getParent() == null) continue;
+ if (inputSinkTransaction == null) {
+ inputSinkTransaction = new SurfaceControl.Transaction();
+ }
+ ar.mActivityRecordInputSink.applyChangesToSurfaceIfChanged(inputSinkTransaction);
}
+ if (inputSinkTransaction != null) inputSinkTransaction.apply();
+
+ // Always schedule stop processing when transition finishes because activities don't
+ // stop while they are in a transition thus their stop could still be pending.
+ mController.mAtm.mTaskSupervisor
+ .scheduleProcessStoppingAndFinishingActivitiesIfNeeded();
sendRemoteCallback(mClientAnimationFinishCallback);
@@ -614,6 +733,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
dc.removeImeSurfaceImmediately();
dc.handleCompleteDeferredRemoval();
}
+
+ mState = STATE_FINISHED;
+ mController.mTransitionTracer.logState(this);
}
void abort() {
@@ -667,6 +789,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
mState = STATE_PLAYING;
+ mStartTransaction = transaction;
+ mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
mController.moveToPlaying(this);
if (dc.isKeyguardLocked()) {
@@ -675,7 +799,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// Resolve the animating targets from the participants
mTargets = calculateTargets(mParticipants, mChanges);
- final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges);
+ final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges,
+ transaction);
if (mOverrideOptions != null) {
info.setAnimationOptions(mOverrideOptions);
}
@@ -685,8 +810,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
handleNonAppWindowsInTransition(dc, mType, mFlags);
- reportStartReasonsToLogger();
-
// The callback is only populated for custom activity-level client animations
sendRemoteCallback(mClientAnimationStartCallback);
@@ -728,6 +851,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
for (int i = mParticipants.size() - 1; i >= 0; --i) {
final WindowContainer wc = mParticipants.valueAt(i);
if (wc.asWindowToken() == null || !wc.isVisibleRequested()) continue;
+ // don't include transient launches, though, since those are only temporarily visible.
+ if (mTransientLaunches != null && wc.asActivityRecord() != null
+ && mTransientLaunches.containsKey(wc.asActivityRecord())) continue;
mVisibleAtTransitionEndTokens.add(wc.asWindowToken());
}
@@ -750,11 +876,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (controller != null && mTargets.contains(dc)) {
controller.setupStartTransaction(transaction);
}
- mStartTransaction = transaction;
- mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
buildFinishTransaction(mFinishTransaction, info.getRootLeash());
if (mController.getTransitionPlayer() != null) {
- mController.dispatchLegacyAppTransitionStarting(info);
+ mController.dispatchLegacyAppTransitionStarting(info, mStatusBarTransitionDelay);
try {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Calling onTransitionReady: %s", info);
@@ -773,8 +897,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// No player registered, so just finish/apply immediately
cleanUpOnFailure();
}
- mSyncId = -1;
mOverrideOptions = null;
+
+ reportStartReasonsToLogger();
}
/**
@@ -832,26 +957,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
}
- // Hiding IME/IME icon when starting quick-step with resents animation.
- if (!mTargetDisplays.get(mRecentsDisplayId).isImeAttachedToApp()) {
- // Hiding IME if IME window is not attached to app.
- // Since some windowing mode is not proper to snapshot Task with IME window
- // while the app transitioning to the next task (e.g. split-screen mode)
- final InputMethodManagerInternal inputMethodManagerInternal =
- LocalServices.getService(InputMethodManagerInternal.class);
- if (inputMethodManagerInternal != null) {
- inputMethodManagerInternal.hideCurrentInputMethod(
- SoftInputShowHideReason.HIDE_RECENTS_ANIMATION);
- }
- } else {
- // Disable IME icon explicitly when IME attached to the app in case
- // IME icon might flickering while swiping to the next app task still
- // in animating before the next app window focused, or IME icon
- // persists on the bottom when swiping the task to recents.
- InputMethodManagerInternal.get().updateImeWindowStatus(
- true /* disableImeIcon */);
- }
-
// The rest of this function handles nav-bar reparenting
if (!dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
@@ -987,11 +1092,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
for (int i = mParticipants.size() - 1; i >= 0; --i) {
ActivityRecord r = mParticipants.valueAt(i).asActivityRecord();
if (r == null || !r.mVisibleRequested) continue;
+ int transitionReason = APP_TRANSITION_WINDOWS_DRAWN;
// At this point, r is "ready", but if it's not "ALL ready" then it is probably only
// ready due to starting-window.
- reasons.put(r, (r.mStartingData instanceof SplashScreenStartingData
- && !r.mLastAllReadyAtSync)
- ? APP_TRANSITION_SPLASH_SCREEN : APP_TRANSITION_WINDOWS_DRAWN);
+ if (r.mStartingData instanceof SplashScreenStartingData && !r.mLastAllReadyAtSync) {
+ transitionReason = APP_TRANSITION_SPLASH_SCREEN;
+ } else if (r.isActivityTypeHomeOrRecents() && isTransientLaunch(r)) {
+ transitionReason = APP_TRANSITION_RECENTS_ANIM;
+ }
+ reasons.put(r, transitionReason);
}
mController.mAtm.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
reasons);
@@ -1020,6 +1129,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
return wc.asWallpaperToken() != null;
}
+ private static boolean isInputMethod(WindowContainer wc) {
+ return wc.getWindowType() == TYPE_INPUT_METHOD;
+ }
+
private static boolean occludesKeyguard(WindowContainer wc) {
final ActivityRecord ar = wc.asActivityRecord();
if (ar != null) {
@@ -1226,8 +1339,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
}
- /** Gets the leash surface for a window container */
- private static SurfaceControl getLeashSurface(WindowContainer wc) {
+ /**
+ * Gets the leash surface for a window container.
+ * @param t a transaction to create leashes on when necessary (fixed rotation at token-level).
+ * If t is null, then this will not create any leashes, just use one if it is there --
+ * this is relevant for building the finishTransaction since it needs to match the
+ * start state and not erroneously create a leash of its own.
+ */
+ private static SurfaceControl getLeashSurface(WindowContainer wc,
+ @Nullable SurfaceControl.Transaction t) {
final DisplayContent asDC = wc.asDisplayContent();
if (asDC != null) {
// DisplayContent is the "root", so we use the windowing layer instead to avoid
@@ -1239,7 +1359,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (asToken != null) {
// WindowTokens can have a fixed-rotation applied to them. In the current
// implementation this fact is hidden from the player, so we must create a leash.
- final SurfaceControl leash = asToken.getOrCreateFixedRotationLeash();
+ final SurfaceControl leash = t != null ? asToken.getOrCreateFixedRotationLeash(t)
+ : asToken.getFixedRotationLeash();
if (leash != null) return leash;
}
}
@@ -1268,12 +1389,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
* Construct a TransitionInfo object from a set of targets and changes. Also populates the
* root surface.
* @param sortedTargets The targets sorted by z-order from top (index 0) to bottom.
+ * @param startT The start transaction - used to set-up new leashes.
*/
@VisibleForTesting
@NonNull
static TransitionInfo calculateTransitionInfo(@TransitionType int type, int flags,
ArrayList<WindowContainer> sortedTargets,
- ArrayMap<WindowContainer, ChangeInfo> changes) {
+ ArrayMap<WindowContainer, ChangeInfo> changes,
+ @Nullable SurfaceControl.Transaction startT) {
final TransitionInfo out = new TransitionInfo(type, flags);
WindowContainer<?> topApp = null;
@@ -1311,10 +1434,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName(
"Transition Root: " + leashReference.getName()).build();
- SurfaceControl.Transaction t = ancestor.mWmService.mTransactionFactory.get();
- t.setLayer(rootLeash, leashReference.getLastLayer());
- t.apply();
- t.close();
+ startT.setLayer(rootLeash, leashReference.getLastLayer());
out.setRootLeash(rootLeash, ancestor.getBounds().left, ancestor.getBounds().top);
// Convert all the resolved ChangeInfos into TransactionInfo.Change objects in order.
@@ -1324,7 +1444,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
final ChangeInfo info = changes.get(target);
final TransitionInfo.Change change = new TransitionInfo.Change(
target.mRemoteToken != null ? target.mRemoteToken.toWindowContainerToken()
- : null, getLeashSurface(target));
+ : null, getLeashSurface(target, startT));
// TODO(shell-transitions): Use leash for non-organized windows.
if (info.mParent != null) {
change.setParent(info.mParent.mRemoteToken.toWindowContainerToken());
@@ -1481,7 +1601,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
boolean getLegacyIsReady() {
- return (mState == STATE_STARTED || mState == STATE_COLLECTING) && mSyncId >= 0;
+ return isCollecting() && mSyncId >= 0;
}
static Transition fromBinder(IBinder binder) {
@@ -1497,10 +1617,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
* seamless rotation. This is currently only used by DisplayContent during fixed-rotation.
*/
private static final int FLAG_SEAMLESS_ROTATION = 1;
+ private static final int FLAG_TRANSIENT_LAUNCH = 2;
+ private static final int FLAG_ABOVE_TRANSIENT_LAUNCH = 4;
@IntDef(prefix = { "FLAG_" }, value = {
FLAG_NONE,
- FLAG_SEAMLESS_ROTATION
+ FLAG_SEAMLESS_ROTATION,
+ FLAG_TRANSIENT_LAUNCH,
+ FLAG_ABOVE_TRANSIENT_LAUNCH
})
@Retention(RetentionPolicy.SOURCE)
@interface Flag {}
@@ -1537,6 +1661,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
boolean hasChanged(@NonNull WindowContainer newState) {
+ // the task including transient launch must promote to root task
+ if ((mFlags & ChangeInfo.FLAG_TRANSIENT_LAUNCH) != 0
+ || (mFlags & ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH) != 0) {
+ return true;
+ }
// If it's invisible and hasn't changed visibility, always return false since even if
// something changed, it wouldn't be a visible change.
final boolean currVisible = newState.isVisibleRequested();
@@ -1552,6 +1681,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
@TransitionInfo.TransitionMode
int getTransitMode(@NonNull WindowContainer wc) {
+ if ((mFlags & ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH) != 0) {
+ return TRANSIT_CLOSE;
+ }
final boolean nowVisible = wc.isVisibleRequested();
if (nowVisible == mVisible) {
return TRANSIT_CHANGE;
@@ -1602,14 +1734,33 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (isWallpaper(wc)) {
flags |= FLAG_IS_WALLPAPER;
}
+ if (isInputMethod(wc)) {
+ flags |= FLAG_IS_INPUT_METHOD;
+ }
if (occludesKeyguard(wc)) {
flags |= FLAG_OCCLUDES_KEYGUARD;
}
+ if (wc.isEmbedded()) {
+ flags |= FLAG_IS_EMBEDDED;
+ }
return flags;
}
}
/**
+ * This transition will be considered not-ready until a corresponding call to
+ * {@link #continueTransitionReady}
+ */
+ void deferTransitionReady() {
+ ++mReadyTracker.mDeferReadyDepth;
+ }
+
+ /** This undoes one call to {@link #deferTransitionReady}. */
+ void continueTransitionReady() {
+ --mReadyTracker.mDeferReadyDepth;
+ }
+
+ /**
* The transition sync mechanism has 2 parts:
* 1. Whether all WM operations for a particular transition are "ready" (eg. did the app
* launch or stop or get a new configuration?).
@@ -1639,6 +1790,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
private boolean mReadyOverride = false;
/**
+ * When non-zero, this transition is forced not-ready (even over setAllReady()). Use this
+ * (via deferTransitionReady/continueTransitionReady) for situations where we want to do
+ * bulk operations which could trigger surface-placement but the existing ready-state
+ * isn't known.
+ */
+ private int mDeferReadyDepth = 0;
+
+ /**
* Adds a ready-group. Any setReady calls in this subtree will be tracked together. For
* now these are only DisplayContents.
*/
@@ -1678,8 +1837,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
/** @return true if all tracked subtrees are ready. */
boolean allReady() {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " allReady query: used=%b "
- + "override=%b states=[%s]", mUsed, mReadyOverride, groupsToString());
+ + "override=%b defer=%d states=[%s]", mUsed, mReadyOverride, mDeferReadyDepth,
+ groupsToString());
+ // If the readiness has never been touched, mUsed will be false. We never want to
+ // consider a transition ready if nothing has been reported on it.
if (!mUsed) return false;
+ // If we are deferring readiness, we never report ready. This is usually temporary.
+ if (mDeferReadyDepth > 0) return false;
+ // Next check all the ready groups to see if they are ready. We can short-cut this if
+ // ready-override is set (which is treated as "everything is marked ready").
if (mReadyOverride) return true;
for (int i = mReadyGroups.size() - 1; i >= 0; --i) {
final WindowContainer wc = mReadyGroups.keyAt(i);
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index c1c390eea932..a02be25bc8d2 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -75,6 +75,7 @@ class TransitionController {
private ITransitionPlayer mTransitionPlayer;
final TransitionMetricsReporter mTransitionMetricsReporter = new TransitionMetricsReporter();
+ final TransitionTracer mTransitionTracer;
private IApplicationThread mTransitionPlayerThread;
final ActivityTaskManagerService mAtm;
@@ -99,11 +100,21 @@ class TransitionController {
// TODO(b/188595497): remove when not needed.
final StatusBarManagerInternal mStatusBar;
+ /**
+ * `true` when building surface layer order for the finish transaction. We want to prevent
+ * wm from touching z-order of surfaces during transitions, but we still need to be able to
+ * calculate the layers for the finishTransaction. So, when assigning layers into the finish
+ * transaction, set this to true so that the {@link canAssignLayers} will allow it.
+ */
+ boolean mBuildingFinishLayers = false;
+
TransitionController(ActivityTaskManagerService atm,
- TaskSnapshotController taskSnapshotController) {
+ TaskSnapshotController taskSnapshotController,
+ TransitionTracer transitionTracer) {
mAtm = atm;
mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
mTaskSnapshotController = taskSnapshotController;
+ mTransitionTracer = transitionTracer;
mTransitionPlayerDeath = () -> {
synchronized (mAtm.mGlobalLock) {
// Clean-up/finish any playing transitions.
@@ -207,6 +218,18 @@ class TransitionController {
}
/**
+ * @return {@code true} if transition is actively collecting changes and `wc` is one of them
+ * or a descendant of one of them. {@code false} once playing.
+ */
+ boolean inCollectingTransition(@NonNull WindowContainer wc) {
+ if (!isCollecting()) return false;
+ for (WindowContainer p = wc; p != null; p = p.getParent()) {
+ if (mCollectingTransition.mParticipants.contains(p)) return true;
+ }
+ return false;
+ }
+
+ /**
* @return {@code true} if transition is actively playing. This is not necessarily {@code true}
* during collection.
*/
@@ -214,6 +237,18 @@ class TransitionController {
return !mPlayingTransitions.isEmpty();
}
+ /**
+ * @return {@code true} if one of the playing transitions contains `wc`.
+ */
+ boolean inPlayingTransition(@NonNull WindowContainer wc) {
+ for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+ for (WindowContainer p = wc; p != null; p = p.getParent()) {
+ if (mPlayingTransitions.get(i).mParticipants.contains(p)) return true;
+ }
+ }
+ return false;
+ }
+
/** @return {@code true} if a transition is running */
boolean inTransition() {
// TODO(shell-transitions): eventually properly support multiple
@@ -222,19 +257,7 @@ class TransitionController {
/** @return {@code true} if a transition is running in a participant subtree of wc */
boolean inTransition(@NonNull WindowContainer wc) {
- if (isCollecting()) {
- for (WindowContainer p = wc; p != null; p = p.getParent()) {
- if (isCollecting(p)) return true;
- }
- }
- for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
- for (WindowContainer p = wc; p != null; p = p.getParent()) {
- if (mPlayingTransitions.get(i).mParticipants.contains(p)) {
- return true;
- }
- }
- }
- return false;
+ return inCollectingTransition(wc) || inPlayingTransition(wc);
}
boolean inRecentsTransition(@NonNull WindowContainer wc) {
@@ -270,6 +293,16 @@ class TransitionController {
return false;
}
+ boolean isTransientHide(@NonNull Task task) {
+ if (mCollectingTransition != null && mCollectingTransition.isTransientHide(task)) {
+ return true;
+ }
+ for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+ if (mPlayingTransitions.get(i).isTransientHide(task)) return true;
+ }
+ return false;
+ }
+
/**
* @return {@code true} if {@param ar} is part of a transient-launch activity in an active
* transition.
@@ -284,6 +317,15 @@ class TransitionController {
return false;
}
+ /**
+ * Whether WM can assign layers to window surfaces at this time. This is usually false while
+ * playing, but can be "opened-up" for certain transition operations like calculating layers
+ * for finishTransaction.
+ */
+ boolean canAssignLayers() {
+ return mBuildingFinishLayers || !isPlaying();
+ }
+
@WindowConfiguration.WindowingMode
int getWindowingModeAtStart(@NonNull WindowContainer wc) {
if (mCollectingTransition == null) return wc.getWindowingMode();
@@ -418,23 +460,32 @@ class TransitionController {
* Collects the window containers which need to be synced with the changing display (e.g.
* rotating) to the given transition or the current collecting transition.
*/
- void collectForDisplayChange(@NonNull DisplayContent dc, @Nullable Transition incoming) {
+ void collectForDisplayAreaChange(@NonNull DisplayArea<?> wc, @Nullable Transition incoming) {
if (incoming == null) incoming = mCollectingTransition;
if (incoming == null) return;
final Transition transition = incoming;
// Collect all visible tasks.
- dc.forAllLeafTasks(task -> {
+ wc.forAllLeafTasks(task -> {
if (task.isVisible()) {
transition.collect(task);
}
}, true /* traverseTopToBottom */);
// Collect all visible non-app windows which need to be drawn before the animation starts.
- dc.forAllWindows(w -> {
- if (w.mActivityRecord == null && w.isVisible() && !isCollecting(w.mToken)
- && dc.shouldSyncRotationChange(w)) {
- transition.collect(w.mToken);
- }
- }, true /* traverseTopToBottom */);
+ final DisplayContent dc = wc.asDisplayContent();
+ if (dc != null) {
+ wc.forAllWindows(w -> {
+ if (w.mActivityRecord == null && w.isVisible() && !isCollecting(w.mToken)
+ && dc.shouldSyncRotationChange(w)) {
+ transition.collect(w.mToken);
+ }
+ }, true /* traverseTopToBottom */);
+ }
+ }
+
+ /** @see Transition#mStatusBarTransitionDelay */
+ void setStatusBarTransitionDelay(long delay) {
+ if (mCollectingTransition == null) return;
+ mCollectingTransition.mStatusBarTransitionDelay = delay;
}
/** @see Transition#setOverrideAnimation */
@@ -455,6 +506,24 @@ class TransitionController {
setReady(wc, true);
}
+ /** @see Transition#deferTransitionReady */
+ void deferTransitionReady() {
+ if (!isShellTransitionsEnabled()) return;
+ if (mCollectingTransition == null) {
+ throw new IllegalStateException("No collecting transition to defer readiness for.");
+ }
+ mCollectingTransition.deferTransitionReady();
+ }
+
+ /** @see Transition#continueTransitionReady */
+ void continueTransitionReady() {
+ if (!isShellTransitionsEnabled()) return;
+ if (mCollectingTransition == null) {
+ throw new IllegalStateException("No collecting transition to defer readiness for.");
+ }
+ mCollectingTransition.continueTransitionReady();
+ }
+
/** @see Transition#finishTransition */
void finishTransition(@NonNull IBinder token) {
// It is usually a no-op but make sure that the metric consumer is removed.
@@ -484,6 +553,7 @@ class TransitionController {
setAnimationRunning(true /* running */);
}
mPlayingTransitions.add(transition);
+ mTransitionTracer.logState(transition);
}
private void setAnimationRunning(boolean running) {
@@ -502,6 +572,7 @@ class TransitionController {
}
transition.abort();
mCollectingTransition = null;
+ mTransitionTracer.logState(transition);
}
/**
@@ -526,6 +597,12 @@ class TransitionController {
}
}
+ /** @see Transition#setCanPipOnFinish */
+ void setCanPipOnFinish(boolean canPipOnFinish) {
+ if (mCollectingTransition == null) return;
+ mCollectingTransition.setCanPipOnFinish(canPipOnFinish);
+ }
+
void legacyDetachNavigationBarFromApp(@NonNull IBinder token) {
final Transition transition = Transition.fromBinder(token);
if (transition == null || !mPlayingTransitions.contains(transition)) {
@@ -549,13 +626,14 @@ class TransitionController {
}
}
- void dispatchLegacyAppTransitionStarting(TransitionInfo info) {
+ void dispatchLegacyAppTransitionStarting(TransitionInfo info, long statusBarTransitionDelay) {
final boolean keyguardGoingAway = info.isKeyguardGoingAway();
for (int i = 0; i < mLegacyListeners.size(); ++i) {
// TODO(shell-transitions): handle (un)occlude transition.
mLegacyListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway,
false /* keyguardOcclude */, 0 /* durationHint */,
- SystemClock.uptimeMillis(), AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
+ SystemClock.uptimeMillis() + statusBarTransitionDelay,
+ AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
}
}
diff --git a/services/core/java/com/android/server/wm/TransitionTracer.java b/services/core/java/com/android/server/wm/TransitionTracer.java
new file mode 100644
index 000000000000..c1927d864320
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TransitionTracer.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.os.Build.IS_USER;
+
+import static com.android.server.wm.shell.ChangeInfo.CHANGE_FLAGS;
+import static com.android.server.wm.shell.ChangeInfo.HAS_CHANGED;
+import static com.android.server.wm.shell.ChangeInfo.TRANSIT_MODE;
+import static com.android.server.wm.shell.ChangeInfo.WINDOW_IDENTIFIER;
+import static com.android.server.wm.shell.Transition.CHANGE;
+import static com.android.server.wm.shell.Transition.FINISH_TRANSACTION_ID;
+import static com.android.server.wm.shell.Transition.FLAGS;
+import static com.android.server.wm.shell.Transition.ID;
+import static com.android.server.wm.shell.Transition.START_TRANSACTION_ID;
+import static com.android.server.wm.shell.Transition.STATE;
+import static com.android.server.wm.shell.Transition.TIMESTAMP;
+import static com.android.server.wm.shell.Transition.TRANSITION_TYPE;
+import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER;
+import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER_H;
+import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER_L;
+import static com.android.server.wm.shell.TransitionTraceProto.TRANSITION;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.TraceBuffer;
+import com.android.server.wm.Transition.ChangeInfo;
+import com.android.server.wm.shell.TransitionTraceProto;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * Helper class to collect and dump transition traces.
+ */
+public class TransitionTracer {
+
+ private static final String LOG_TAG = "TransitionTracer";
+
+ /**
+ * Maximum buffer size, currently defined as 5 MB
+ */
+ private static final int BUFFER_CAPACITY = 5120 * 1024; // 5 MB
+ static final String WINSCOPE_EXT = ".winscope";
+ private static final String TRACE_FILE = "/data/misc/wmtrace/transition_trace" + WINSCOPE_EXT;
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+ private final TransitionTraceBuffer mTraceBuffer = new TransitionTraceBuffer();
+
+ private final Object mEnabledLock = new Object();
+ private volatile boolean mEnabled = false;
+
+ private long mTraceStartTimestamp;
+
+ private class TransitionTraceBuffer {
+ private final TraceBuffer mBuffer = new TraceBuffer(BUFFER_CAPACITY);
+
+ private void pushTransitionState(Transition transition) {
+ final ProtoOutputStream outputStream = new ProtoOutputStream();
+ final long transitionEntryToken = outputStream.start(TRANSITION);
+
+ outputStream.write(ID, transition.getSyncId());
+ outputStream.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
+ outputStream.write(TRANSITION_TYPE, transition.mType);
+ outputStream.write(STATE, transition.getState());
+ outputStream.write(FLAGS, transition.getFlags());
+ if (transition.getStartTransaction() != null) {
+ outputStream.write(START_TRANSACTION_ID, transition.getStartTransaction().getId());
+ }
+ if (transition.getFinishTransaction() != null) {
+ outputStream.write(FINISH_TRANSACTION_ID,
+ transition.getFinishTransaction().getId());
+ }
+
+ for (int i = 0; i < transition.mChanges.size(); ++i) {
+ final WindowContainer window = transition.mChanges.keyAt(i);
+ final ChangeInfo changeInfo = transition.mChanges.valueAt(i);
+ writeChange(outputStream, window, changeInfo);
+ }
+
+ outputStream.end(transitionEntryToken);
+
+ mBuffer.add(outputStream);
+ }
+
+ private void writeChange(ProtoOutputStream outputStream, WindowContainer window,
+ ChangeInfo changeInfo) {
+ Trace.beginSection("TransitionProto#addChange");
+ final long changeEntryToken = outputStream.start(CHANGE);
+
+ final int transitMode = changeInfo.getTransitMode(window);
+ final boolean hasChanged = changeInfo.hasChanged(window);
+ final int changeFlags = changeInfo.getChangeFlags(window);
+
+ outputStream.write(TRANSIT_MODE, transitMode);
+ outputStream.write(HAS_CHANGED, hasChanged);
+ outputStream.write(CHANGE_FLAGS, changeFlags);
+ window.writeIdentifierToProto(outputStream, WINDOW_IDENTIFIER);
+
+ outputStream.end(changeEntryToken);
+ Trace.endSection();
+ }
+
+ public void writeToFile(File file, ProtoOutputStream proto) throws IOException {
+ mBuffer.writeTraceToFile(file, proto);
+ }
+
+ public void reset() {
+ mBuffer.resetBuffer();
+ }
+ }
+
+ /**
+ * Records the current state of a transition in the transition trace (if it is running).
+ * @param transition the transition that we want to record the state of.
+ */
+ public void logState(com.android.server.wm.Transition transition) {
+ if (!mEnabled) {
+ return;
+ }
+
+ Log.d(LOG_TAG, "Logging state of transition " + transition);
+ mTraceBuffer.pushTransitionState(transition);
+ }
+
+ /**
+ * Starts collecting transitions for the trace.
+ * If called while a trace is already running, this will reset the trace.
+ */
+ public void startTrace(@Nullable PrintWriter pw) {
+ if (IS_USER) {
+ LogAndPrintln.e(pw, "Tracing is not supported on user builds.");
+ return;
+ }
+ Trace.beginSection("TransitionTracer#startTrace");
+ LogAndPrintln.i(pw, "Starting shell transition trace.");
+ synchronized (mEnabledLock) {
+ mTraceStartTimestamp = SystemClock.elapsedRealtime();
+ mEnabled = true;
+ mTraceBuffer.reset();
+ }
+ Trace.endSection();
+ }
+
+ /**
+ * Stops collecting the transition trace and dump to trace to file.
+ *
+ * Dumps the trace to @link{TRACE_FILE}.
+ */
+ public void stopTrace(@Nullable PrintWriter pw) {
+ stopTrace(pw, new File(TRACE_FILE));
+ }
+
+ /**
+ * Stops collecting the transition trace and dump to trace to file.
+ * @param outputFile The file to dump the transition trace to.
+ */
+ public void stopTrace(@Nullable PrintWriter pw, File outputFile) {
+ if (IS_USER) {
+ LogAndPrintln.e(pw, "Tracing is not supported on user builds.");
+ return;
+ }
+ Trace.beginSection("TransitionTracer#stopTrace");
+ LogAndPrintln.i(pw, "Stopping shell transition trace.");
+ synchronized (mEnabledLock) {
+ if (!mEnabled) {
+ LogAndPrintln.e(pw,
+ "Error: Tracing can't be stopped because it hasn't been started.");
+ return;
+ }
+
+ mEnabled = false;
+ writeTraceToFileLocked(pw, outputFile);
+ }
+ Trace.endSection();
+ }
+
+ boolean isEnabled() {
+ return mEnabled;
+ }
+
+ private void writeTraceToFileLocked(@Nullable PrintWriter pw, File file) {
+ Trace.beginSection("TransitionTracer#writeTraceToFileLocked");
+ try {
+ ProtoOutputStream proto = new ProtoOutputStream();
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ proto.write(TransitionTraceProto.TIMESTAMP, mTraceStartTimestamp);
+ int pid = android.os.Process.myPid();
+ LogAndPrintln.i(pw, "Writing file to " + file.getAbsolutePath()
+ + " from process " + pid);
+ mTraceBuffer.writeToFile(file, proto);
+ } catch (IOException e) {
+ LogAndPrintln.e(pw, "Unable to write buffer to file", e);
+ }
+ Trace.endSection();
+ }
+
+ private static class LogAndPrintln {
+ private static void i(@Nullable PrintWriter pw, String msg) {
+ Log.i(LOG_TAG, msg);
+ if (pw != null) {
+ pw.println(msg);
+ pw.flush();
+ }
+ }
+
+ private static void e(@Nullable PrintWriter pw, String msg) {
+ Log.e(LOG_TAG, msg);
+ if (pw != null) {
+ pw.println("ERROR: " + msg);
+ pw.flush();
+ }
+ }
+
+ private static void e(@Nullable PrintWriter pw, String msg, @NonNull Exception e) {
+ Log.e(LOG_TAG, msg, e);
+ if (pw != null) {
+ pw.println("ERROR: " + msg + " ::\n " + e);
+ pw.flush();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
index 5e963cc5ae8e..41c1e793dd90 100644
--- a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
+++ b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java
@@ -69,6 +69,10 @@ class UnknownAppVisibilityController {
return mUnknownApps.isEmpty();
}
+ boolean isVisibilityUnknown(ActivityRecord r) {
+ return mUnknownApps.containsKey(r);
+ }
+
void clear() {
mUnknownApps.clear();
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index a32a6087a04d..6245005606d7 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -21,6 +21,7 @@ import static android.app.WallpaperManager.COMMAND_UNFREEZE;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
@@ -181,15 +182,11 @@ class WallpaperController {
mFindResults.setUseTopWallpaperAsTarget(true);
}
- final RecentsAnimationController recentsAnimationController =
- mService.getRecentsAnimationController();
final boolean animationWallpaper = animatingContainer != null
&& animatingContainer.getAnimation() != null
&& animatingContainer.getAnimation().getShowWallpaper();
final boolean hasWallpaper = w.hasWallpaper() || animationWallpaper;
- final boolean isRecentsTransitionTarget = (recentsAnimationController != null
- && recentsAnimationController.isWallpaperVisible(w));
- if (isRecentsTransitionTarget) {
+ if (isRecentsTransitionTarget(w)) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w);
mFindResults.setWallpaperTarget(w);
return true;
@@ -213,6 +210,22 @@ class WallpaperController {
return false;
};
+ private boolean isRecentsTransitionTarget(WindowState w) {
+ if (w.mTransitionController.isShellTransitionsEnabled()) {
+ // Because the recents activity is invisible in background while keyguard is occluded
+ // (the activity window is on screen while keyguard is locked) with recents animation,
+ // the task animating by recents needs to be wallpaper target to make wallpaper visible.
+ // While for unlocked case, because recents activity will be moved to top, it can be
+ // the wallpaper target naturally.
+ return w.mActivityRecord != null && w.mAttrs.type == TYPE_BASE_APPLICATION
+ && mDisplayContent.isKeyguardLocked()
+ && w.mTransitionController.isTransientHide(w.getTask());
+ }
+ // The window is either the recents activity or is in the task animating by the recents.
+ final RecentsAnimationController controller = mService.getRecentsAnimationController();
+ return controller != null && controller.isWallpaperVisible(w);
+ }
+
/**
* @see #computeLastWallpaperZoomOut()
*/
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d7257180f01b..d9b25adb8c4c 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -48,6 +48,7 @@ import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -428,6 +429,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
if (insetsTypes == null || insetsTypes.length == 0) {
throw new IllegalArgumentException("Insets type not specified.");
}
+ if (mDisplayContent == null) {
+ // This is possible this container is detached when WM shell is responding to a previous
+ // request. WM shell will be updated when this container is attached again and the
+ // insets need to be updated.
+ Slog.w(TAG, "Can't add local rect insets source provider when detached. " + this);
+ return;
+ }
if (mLocalInsetsSourceProviders == null) {
mLocalInsetsSourceProviders = new SparseArray<>();
}
@@ -1010,6 +1018,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
if (dc != null && dc != this) {
dc.getPendingTransaction().merge(mPendingTransaction);
}
+ if (dc != this && mLocalInsetsSourceProviders != null) {
+ mLocalInsetsSourceProviders.clear();
+ }
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer child = mChildren.get(i);
child.onDisplayChanged(dc);
@@ -1159,6 +1170,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/**
+ * Returns {@code true} if self or the parent container of the window is in transition, e.g.
+ * the app or recents transition. This method is only used when legacy and shell transition
+ * have the same condition to check the animation state.
+ */
+ boolean inTransitionSelfOrParent() {
+ if (!mTransitionController.isShellTransitionsEnabled()) {
+ return isAnimating(PARENTS | TRANSITION,
+ ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
+ }
+ return inTransition();
+ }
+
+ /**
* @return Whether our own container running an animation at the moment.
*/
final boolean isAnimating() {
@@ -1180,7 +1204,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
if (!mTransitionController.isShellTransitionsEnabled()) {
return isAnimating(TRANSITION | CHILDREN, WindowState.EXIT_ANIMATING_TYPES);
}
- if (mTransitionController.isCollecting(this)) {
+ // Only check leaf containers because inTransition() includes parent.
+ if (mChildren.isEmpty() && inTransition()) {
return true;
}
@@ -2458,7 +2483,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
void assignLayer(Transaction t, int layer) {
// Don't assign layers while a transition animation is playing
// TODO(b/173528115): establish robust best-practices around z-order fighting.
- if (mTransitionController.isPlaying()) return;
+ if (!mTransitionController.canAssignLayers()) return;
final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
if (mSurfaceControl != null && changed) {
setLayer(t, layer);
@@ -2679,9 +2704,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* will be applied.
*/
void scheduleAnimation() {
- if (mParent != null) {
- mParent.scheduleAnimation();
- }
+ mWmService.scheduleAnimationLocked();
}
/**
@@ -2801,6 +2824,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* snapshot from {@link #getFreezeSnapshotTarget()}.
*/
void initializeChangeTransition(Rect startBounds, @Nullable SurfaceControl freezeTarget) {
+ if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
+ // TODO(b/207070762): request shell transition for activityEmbedding change.
+ return;
+ }
mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
mDisplayContent.mChangingContainers.add(this);
// Calculate the relative position in parent container.
@@ -2925,18 +2952,34 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
getAnimationPosition(mTmpPoint);
mTmpRect.offsetTo(0, 0);
- final RemoteAnimationController controller =
- getDisplayContent().mAppTransition.getRemoteAnimationController();
+ final AppTransition appTransition = getDisplayContent().mAppTransition;
+ final RemoteAnimationController controller = appTransition.getRemoteAnimationController();
final boolean isChanging = AppTransition.isChangeTransitOld(transit) && enter
&& isChangingAppTransition();
// Delaying animation start isn't compatible with remote animations at all.
if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
+ // Here we load App XML in order to read com.android.R.styleable#Animation_showBackdrop.
+ boolean showBackdrop = false;
+ // Optionally set backdrop color if App explicitly provides it through
+ // {@link Activity#overridePendingTransition(int, int, int)}.
+ @ColorInt int backdropColor = 0;
+ if (controller.isFromActivityEmbedding()) {
+ final int animAttr = AppTransition.mapOpenCloseTransitTypes(transit, enter);
+ final Animation a = animAttr != 0
+ ? appTransition.loadAnimationAttr(lp, animAttr, transit) : null;
+ showBackdrop = a != null && a.getShowBackdrop();
+ backdropColor = appTransition.getNextAppTransitionBackgroundColor();
+ }
final Rect localBounds = new Rect(mTmpRect);
localBounds.offsetTo(mTmpPoint.x, mTmpPoint.y);
final RemoteAnimationController.RemoteAnimationRecord adapters =
- controller.createRemoteAnimationRecord(this, mTmpPoint, localBounds,
- screenBounds, (isChanging ? mSurfaceFreezer.mFreezeBounds : null));
+ controller.createRemoteAnimationRecord(
+ this, mTmpPoint, localBounds, screenBounds,
+ (isChanging ? mSurfaceFreezer.mFreezeBounds : null), showBackdrop);
+ if (backdropColor != 0) {
+ adapters.setBackDropColor(backdropColor);
+ }
if (!isChanging) {
adapters.setMode(enter
? RemoteAnimationTarget.MODE_OPENING
@@ -3183,6 +3226,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
void resetSurfacePositionForAnimationLeash(Transaction t) {
t.setPosition(mSurfaceControl, 0, 0);
+ if (mSyncState != SYNC_STATE_NONE && t != mSyncTransaction) {
+ // Avoid restoring to old position if the sync transaction is applied later.
+ mSyncTransaction.setPosition(mSurfaceControl, 0, 0);
+ }
mLastSurfacePosition.set(0, 0);
}
@@ -3664,10 +3711,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mChildren.get(i).finishSync(outMergedTransaction, cancel);
}
if (cancel && mSyncGroup != null) mSyncGroup.onCancelSync(this);
- clearSyncState();
- }
-
- void clearSyncState() {
mSyncState = SYNC_STATE_NONE;
mSyncGroup = null;
}
@@ -3727,6 +3770,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* hierarchy change implies a configuration change.
*/
private void onSyncReparent(WindowContainer oldParent, WindowContainer newParent) {
+ // Check if this is changing displays. If so, mark the old display as "ready" for
+ // transitions. This is to work around the problem where setting readiness against this
+ // container will only set the new display as ready and leave the old display as unready.
+ if (mSyncState != SYNC_STATE_NONE && oldParent != null
+ && oldParent.getDisplayContent() != null && (newParent == null
+ || oldParent.getDisplayContent() != newParent.getDisplayContent())) {
+ mTransitionController.setReady(oldParent.getDisplayContent());
+ }
+
if (newParent == null || newParent.mSyncState == SYNC_STATE_NONE) {
if (mSyncState == SYNC_STATE_NONE) {
return;
diff --git a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
index 9b6f4d947694..b000a9841d06 100644
--- a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
+++ b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
@@ -33,7 +33,6 @@ import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.HardwareBuffer;
-import android.os.Process;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
@@ -91,7 +90,7 @@ class WindowContainerThumbnail implements Animatable {
.setBLASTLayer()
.setFormat(PixelFormat.TRANSLUCENT)
.setMetadata(METADATA_WINDOW_TYPE, mWindowContainer.getWindowingMode())
- .setMetadata(METADATA_OWNER_UID, Process.myUid())
+ .setMetadata(METADATA_OWNER_UID, WindowManagerService.MY_UID)
.setCallsite("WindowContainerThumbnail")
.build();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 52af39ee64b3..9d6e250e8a47 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -48,6 +48,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR;
import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
@@ -89,6 +90,7 @@ import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManagerGlobal.ADD_OKAY;
+import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
@@ -238,10 +240,10 @@ import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.ICrossWindowBlurEnabledListener;
+import android.view.IDisplayChangeWindowController;
import android.view.IDisplayFoldListener;
import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowListener;
-import android.view.IDisplayWindowRotationController;
import android.view.IInputFilter;
import android.view.IOnKeyguardExitResult;
import android.view.IPinnedTaskListener;
@@ -287,6 +289,7 @@ import android.view.displayhash.VerifiedDisplayHash;
import android.window.ClientWindowFrames;
import android.window.ITaskFpsCallback;
import android.window.TaskSnapshot;
+import android.window.WindowContainerToken;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -397,9 +400,8 @@ public class WindowManagerService extends IWindowManager.Stub
private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
- // Used to indicate that if there is already a transition set, it should be preserved when
- // trying to apply a new one.
- private static final boolean ALWAYS_KEEP_CURRENT = true;
+ static final int MY_PID = myPid();
+ static final int MY_UID = myUid();
static final int LOGTAG_INPUT_FOCUS = 62001;
@@ -460,6 +462,7 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowManagerConstants mConstants;
final WindowTracing mWindowTracing;
+ final TransitionTracer mTransitionTracer;
private final DisplayAreaPolicy.Provider mDisplayAreaPolicyProvider;
@@ -682,9 +685,9 @@ public class WindowManagerService extends IWindowManager.Stub
final WallpaperVisibilityListeners mWallpaperVisibilityListeners =
new WallpaperVisibilityListeners();
- IDisplayWindowRotationController mDisplayRotationController = null;
- private final DeathRecipient mDisplayRotationControllerDeath =
- () -> mDisplayRotationController = null;
+ IDisplayChangeWindowController mDisplayChangeController = null;
+ private final DeathRecipient mDisplayChangeControllerDeath =
+ () -> mDisplayChangeController = null;
final DisplayWindowListenerController mDisplayNotificationController;
final TaskSystemBarsListenerController mTaskSystemBarsListenerController;
@@ -1017,7 +1020,7 @@ public class WindowManagerService extends IWindowManager.Stub
private int mExitAnimId, mEnterAnimId;
/** The display that the rotation animation is applying to. */
- private int mFrozenDisplayId;
+ private int mFrozenDisplayId = INVALID_DISPLAY;
/** Skip repeated ActivityRecords initialization. Note that AppWindowsToken's version of this
* is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */
@@ -1067,7 +1070,6 @@ public class WindowManagerService extends IWindowManager.Stub
Function<SurfaceSession, SurfaceControl.Builder> mSurfaceControlFactory;
Supplier<SurfaceControl.Transaction> mTransactionFactory;
- final Supplier<Surface> mSurfaceFactory;
private final SurfaceControl.Transaction mTransaction;
@@ -1150,7 +1152,7 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm) {
return main(context, im, showBootMsgs, onlyCore, policy, atm,
- new DisplayWindowSettingsProvider(), SurfaceControl.Transaction::new, Surface::new,
+ new DisplayWindowSettingsProvider(), SurfaceControl.Transaction::new,
SurfaceControl.Builder::new);
}
@@ -1163,12 +1165,11 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm, DisplayWindowSettingsProvider
displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory,
- Supplier<Surface> surfaceFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
final WindowManagerService[] wms = new WindowManagerService[1];
DisplayThread.getHandler().runWithScissors(() ->
wms[0] = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
- atm, displayWindowSettingsProvider, transactionFactory, surfaceFactory,
+ atm, displayWindowSettingsProvider, transactionFactory,
surfaceControlFactory), 0);
return wms[0];
}
@@ -1178,7 +1179,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void run() {
WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
- mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
+ mPolicy.init(mContext, WindowManagerService.this);
}
}, 0);
}
@@ -1193,7 +1194,6 @@ public class WindowManagerService extends IWindowManager.Stub
boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm, DisplayWindowSettingsProvider
displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory,
- Supplier<Surface> surfaceFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
installLock(this, INDEX_WINDOW);
mGlobalLock = atm.getGlobalLock();
@@ -1208,8 +1208,7 @@ public class WindowManagerService extends IWindowManager.Stub
com.android.internal.R.bool.config_hasPermanentDpad);
mInTouchMode = context.getResources().getBoolean(
com.android.internal.R.bool.config_defaultInTouchMode);
- inputManager.setInTouchMode(
- mInTouchMode, myPid(), myUid(), /* hasPermission = */ true);
+ inputManager.setInTouchMode(mInTouchMode, MY_PID, MY_UID, true /* hasPermission */);
mDrawLockTimeoutMillis = context.getResources().getInteger(
com.android.internal.R.integer.config_drawLockTimeoutMillis);
mAllowAnimationsInLowPowerMode = context.getResources().getBoolean(
@@ -1233,7 +1232,6 @@ public class WindowManagerService extends IWindowManager.Stub
mSurfaceControlFactory = surfaceControlFactory;
mTransactionFactory = transactionFactory;
- mSurfaceFactory = surfaceFactory;
mTransaction = mTransactionFactory.get();
mPolicy = policy;
@@ -1251,6 +1249,7 @@ public class WindowManagerService extends IWindowManager.Stub
mWindowTracing = WindowTracing.createDefaultAndStartLooper(this,
Choreographer.getInstance());
+ mTransitionTracer = new TransitionTracer();
LocalServices.addService(WindowManagerPolicy.class, mPolicy);
@@ -1449,7 +1448,7 @@ public class WindowManagerService extends IWindowManager.Stub
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls) {
+ InsetsSourceControl[] outActiveControls, Rect outAttachedFrame) {
Arrays.fill(outActiveControls, null);
int[] appOp = new int[1];
final boolean isRoundedCornerOverlay = (attrs.privateFlags
@@ -1864,6 +1863,13 @@ public class WindowManagerService extends IWindowManager.Stub
outInsetsState.set(win.getCompatInsetsState(), win.isClientLocal());
getInsetsSourceControls(win, outActiveControls);
+
+ if (win.mLayoutAttached) {
+ outAttachedFrame.set(win.getParentWindow().getCompatFrame());
+ } else {
+ // Make this invalid which indicates a null attached frame.
+ outAttachedFrame.set(0, 0, -1, -1);
+ }
}
Binder.restoreCallingIdentity(origId);
@@ -2083,7 +2089,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (win.mAttrs.type == TYPE_WALLPAPER) {
dc.mWallpaperController.clearLastWallpaperTimeoutTime();
dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- } else if (win.hasWallpaper()) {
+ } else if (dc.mWallpaperController.isWallpaperTarget(win)) {
dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
@@ -2216,6 +2222,20 @@ public class WindowManagerService extends IWindowManager.Stub
== PackageManager.PERMISSION_GRANTED;
}
+ /**
+ * Returns whether this window can proceed with drawing or needs to retry later.
+ */
+ public boolean cancelDraw(Session session, IWindow client) {
+ synchronized (mGlobalLock) {
+ final WindowState win = windowForClientLocked(session, client, false);
+ if (win == null) {
+ return false;
+ }
+
+ return win.cancelAndRedraw();
+ }
+ }
+
public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
@@ -2232,6 +2252,11 @@ public class WindowManagerService extends IWindowManager.Stub
if (win == null) {
return 0;
}
+
+ if (win.cancelAndRedraw() && win.mPrepareSyncSeqId <= win.mLastSeqIdSentToRelayout) {
+ result |= RELAYOUT_RES_CANCEL_AND_REDRAW;
+ }
+
final DisplayContent displayContent = win.getDisplayContent();
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
@@ -2258,9 +2283,21 @@ public class WindowManagerService extends IWindowManager.Stub
throw new IllegalArgumentException(
"Window type can not be changed after the window is added.");
}
- if (!Arrays.equals(win.mAttrs.providesInsetsTypes, attrs.providesInsetsTypes)) {
- throw new IllegalArgumentException(
- "Insets types can not be changed after the window is added.");
+ if (!(win.mAttrs.providedInsets == null && attrs.providedInsets == null)) {
+ if (win.mAttrs.providedInsets == null || attrs.providedInsets == null
+ || (win.mAttrs.providedInsets.length != attrs.providedInsets.length)) {
+ throw new IllegalArgumentException(
+ "Insets types can not be changed after the window is added.");
+ } else {
+ final int insetsTypes = attrs.providedInsets.length;
+ for (int i = 0; i < insetsTypes; i++) {
+ if (win.mAttrs.providedInsets[i].type != attrs.providedInsets[i].type) {
+ throw new IllegalArgumentException(
+ "Insets types can not be changed after the window is "
+ + "added.");
+ }
+ }
+ }
}
flagChanges = win.mAttrs.flags ^ attrs.flags;
@@ -2634,29 +2671,6 @@ public class WindowManagerService extends IWindowManager.Stub
return result;
}
- int updateViewVisibility(Session session, IWindow client, LayoutParams attrs,
- int viewVisibility, MergedConfiguration outMergedConfiguration,
- SurfaceControl outSurfaceControl, InsetsState outInsetsState,
- InsetsSourceControl[] outActiveControls) {
- // TODO(b/161810301): Finish the implementation.
- return 0;
- }
-
- void updateWindowLayout(Session session, IWindow client, LayoutParams attrs, int flags,
- ClientWindowFrames clientWindowFrames, int requestedWidth, int requestedHeight) {
- final long origId = Binder.clearCallingIdentity();
- synchronized (mGlobalLock) {
- final WindowState win = windowForClientLocked(session, client, false);
- if (win == null) {
- return;
- }
- win.setFrames(clientWindowFrames, requestedWidth, requestedHeight);
-
- // TODO(b/161810301): Finish the implementation.
- }
- Binder.restoreCallingIdentity(origId);
- }
-
public boolean outOfMemoryWindow(Session session, IWindow client) {
final long origId = Binder.clearCallingIdentity();
@@ -2704,7 +2718,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
boolean checkCallingPermission(String permission, String func, boolean printLog) {
- if (Binder.getCallingPid() == myPid()) {
+ if (Binder.getCallingPid() == MY_PID) {
return true;
}
@@ -2864,7 +2878,7 @@ public class WindowManagerService extends IWindowManager.Stub
// registration in DisplayContent#onParentChanged at DisplayContent initialization.
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
- if (Binder.getCallingPid() != myPid()) {
+ if (Binder.getCallingPid() != MY_PID) {
throw new WindowManager.InvalidDisplayException("attachToDisplayContent: "
+ "trying to attach to a non-existing display:" + displayId);
}
@@ -4260,12 +4274,13 @@ public class WindowManagerService extends IWindowManager.Stub
.notifyOnActivityRotation(displayContent.mDisplayId);
}
- final boolean pendingRemoteRotation = rotationChanged
- && (displayContent.getDisplayRotation().isWaitingForRemoteRotation()
+ final boolean pendingRemoteDisplayChange = rotationChanged
+ && (displayContent.mRemoteDisplayChangeController
+ .isWaitingForRemoteDisplayChange()
|| displayContent.mTransitionController.isCollecting());
// Even if alwaysSend, we are waiting for a transition or remote to provide
- // rotated configuration, so we can't update configuration yet.
- if (!pendingRemoteRotation) {
+ // updated configuration, so we can't update configuration yet.
+ if (!pendingRemoteDisplayChange) {
if (!rotationChanged || forceRelayout) {
displayContent.setLayoutNeeded();
layoutNeeded = true;
@@ -4297,17 +4312,17 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void setDisplayWindowRotationController(IDisplayWindowRotationController controller) {
+ public void setDisplayChangeWindowController(IDisplayChangeWindowController controller) {
mAtmService.enforceTaskPermission("setDisplayWindowRotationController");
try {
synchronized (mGlobalLock) {
- if (mDisplayRotationController != null) {
- mDisplayRotationController.asBinder().unlinkToDeath(
- mDisplayRotationControllerDeath, 0);
- mDisplayRotationController = null;
+ if (mDisplayChangeController != null) {
+ mDisplayChangeController.asBinder().unlinkToDeath(
+ mDisplayChangeControllerDeath, 0);
+ mDisplayChangeController = null;
}
- controller.asBinder().linkToDeath(mDisplayRotationControllerDeath, 0);
- mDisplayRotationController = controller;
+ controller.asBinder().linkToDeath(mDisplayChangeControllerDeath, 0);
+ mDisplayChangeController = controller;
}
} catch (RemoteException e) {
throw new RuntimeException("Unable to set rotation controller");
@@ -5694,6 +5709,25 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ void setSandboxDisplayApis(int displayId, boolean sandboxDisplayApis) {
+ if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent != null) {
+ displayContent.setSandboxDisplayApis(sandboxDisplayApis);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
/** The global settings only apply to default display. */
private boolean applyForcedPropertiesForDefaultDisplay() {
boolean changed = false;
@@ -5874,6 +5908,21 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public void startTransitionTrace() {
+ mTransitionTracer.startTrace(null /* printwriter */);
+ }
+
+ @Override
+ public void stopTransitionTrace() {
+ mTransitionTracer.stopTrace(null /* printwriter */);
+ }
+
+ @Override
+ public boolean isTransitionTraceEnabled() {
+ return mTransitionTracer.isEnabled();
+ }
+
+ @Override
public boolean registerCrossWindowBlurEnabledListener(
ICrossWindowBlurEnabledListener listener) {
return mBlurController.registerCrossWindowBlurEnabledListener(listener);
@@ -5919,10 +5968,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
void makeWindowFreezingScreenIfNeededLocked(WindowState w) {
- // If the screen is currently frozen or off, then keep
- // it frozen/off until this window draws at its new
- // orientation.
- if (!w.mToken.okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
+ // If the screen is currently frozen, then keep it frozen until this window draws at its
+ // new orientation.
+ if (mFrozenDisplayId != INVALID_DISPLAY && mFrozenDisplayId == w.getDisplayId()
+ && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
ProtoLog.v(WM_DEBUG_ORIENTATION, "Changing surface while display frozen: %s", w);
w.setOrientationChanging(true);
if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) {
@@ -5998,9 +6047,7 @@ public class WindowManagerService extends IWindowManager.Stub
/** Note that Locked in this case is on mLayoutToAnim */
void scheduleAnimationLocked() {
- if (mAnimator != null) {
- mAnimator.scheduleAnimation();
- }
+ mAnimator.scheduleAnimation();
}
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
@@ -6087,24 +6134,24 @@ public class WindowManagerService extends IWindowManager.Stub
final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
final int numOpeningApps;
final boolean waitingForConfig;
- final boolean waitingForRemoteRotation;
+ final boolean waitingForRemoteDisplayChange;
if (displayContent != null) {
numOpeningApps = displayContent.mOpeningApps.size();
waitingForConfig = displayContent.mWaitingForConfig;
- waitingForRemoteRotation =
- displayContent.getDisplayRotation().isWaitingForRemoteRotation();
+ waitingForRemoteDisplayChange = displayContent.mRemoteDisplayChangeController
+ .isWaitingForRemoteDisplayChange();
} else {
- waitingForConfig = waitingForRemoteRotation = false;
+ waitingForConfig = waitingForRemoteDisplayChange = false;
numOpeningApps = 0;
}
- if (waitingForConfig || waitingForRemoteRotation || mAppsFreezingScreen > 0
+ if (waitingForConfig || waitingForRemoteDisplayChange || mAppsFreezingScreen > 0
|| mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
|| mClientFreezingScreen || numOpeningApps > 0) {
ProtoLog.d(WM_DEBUG_ORIENTATION, "stopFreezingDisplayLocked: Returning "
- + "waitingForConfig=%b, waitingForRemoteRotation=%b, "
+ + "waitingForConfig=%b, waitingForRemoteDisplayChange=%b, "
+ "mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, "
+ "mClientFreezingScreen=%b, mOpeningApps.size()=%d",
- waitingForConfig, waitingForRemoteRotation,
+ waitingForConfig, waitingForRemoteDisplayChange,
mAppsFreezingScreen, mWindowsFreezingScreen,
mClientFreezingScreen, numOpeningApps);
return;
@@ -8215,7 +8262,7 @@ public class WindowManagerService extends IWindowManager.Stub
.setContainerLayer()
.setName("IME Handwriting Surface")
.setCallsite("getHandwritingSurfaceForDisplay")
- .setParent(dc.getOverlayLayer())
+ .setParent(dc.getSurfaceControl())
.build();
}
}
@@ -8236,6 +8283,26 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void setContentRecordingSession(@Nullable ContentRecordingSession incomingSession) {
synchronized (mGlobalLock) {
+ // Allow the controller to handle teardown or a non-task session.
+ if (incomingSession == null
+ || incomingSession.getContentToRecord() != RECORD_CONTENT_TASK) {
+ mContentRecordingController.setContentRecordingSessionLocked(incomingSession,
+ WindowManagerService.this);
+ return;
+ }
+ // For a task session, find the activity identified by the launch cookie.
+ final WindowContainerToken wct = getTaskWindowContainerTokenForLaunchCookie(
+ incomingSession.getTokenToRecord());
+ if (wct == null) {
+ Slog.w(TAG, "Handling a new recording session; unable to find the "
+ + "WindowContainerToken");
+ mContentRecordingController.setContentRecordingSessionLocked(null,
+ WindowManagerService.this);
+ return;
+ }
+ // Replace the launch cookie in the session details with the task's
+ // WindowContainerToken.
+ incomingSession.setTokenToRecord(wct.asBinder());
mContentRecordingController.setContentRecordingSessionLocked(incomingSession,
WindowManagerService.this);
}
@@ -8494,6 +8561,38 @@ public class WindowManagerService extends IWindowManager.Stub
}
/**
+ * Retrieve the {@link WindowContainerToken} of the task that contains the activity started
+ * with the given launch cookie.
+ *
+ * @param launchCookie the launch cookie set on the {@link ActivityOptions} when starting an
+ * activity
+ * @return a token representing the task containing the activity started with the given launch
+ * cookie, or {@code null} if the token couldn't be found.
+ */
+ @VisibleForTesting
+ @Nullable
+ WindowContainerToken getTaskWindowContainerTokenForLaunchCookie(@NonNull IBinder launchCookie) {
+ // Find the activity identified by the launch cookie.
+ final ActivityRecord targetActivity = mRoot.getActivity(
+ activity -> activity.mLaunchCookie == launchCookie);
+ if (targetActivity == null) {
+ Slog.w(TAG, "Unable to find the activity for this launch cookie");
+ return null;
+ }
+ if (targetActivity.getTask() == null) {
+ Slog.w(TAG, "Unable to find the task for this launch cookie");
+ return null;
+ }
+ WindowContainerToken taskWindowContainerToken =
+ targetActivity.getTask().mRemoteToken.toWindowContainerToken();
+ if (taskWindowContainerToken == null) {
+ Slog.w(TAG, "Unable to find the WindowContainerToken for " + targetActivity.getName());
+ return null;
+ }
+ return taskWindowContainerToken;
+ }
+
+ /**
* You need ALLOW_SLIPPERY_TOUCHES permission to be able to set FLAG_SLIPPERY.
*/
private int sanitizeFlagSlippery(int flags, String windowName, int callingUid, int callingPid) {
@@ -8753,7 +8852,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public boolean getWindowInsets(WindowManager.LayoutParams attrs, int displayId,
InsetsState outInsetsState) {
- final boolean fromLocal = Binder.getCallingPid() == myPid();
+ final boolean fromLocal = Binder.getCallingPid() == MY_PID;
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 34c93482ecfe..ff43a96d6afc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -19,6 +19,19 @@ package com.android.server.wm;
import static android.os.Build.IS_USER;
import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
+
+import android.content.res.Resources.NotFoundException;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.ParcelFileDescriptor;
@@ -36,6 +49,9 @@ import com.android.internal.os.ByteTransferPipe;
import com.android.internal.protolog.ProtoLogImpl;
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
+import com.android.server.wm.LetterboxConfiguration.LetterboxHorizontalReachabilityPosition;
+import com.android.server.wm.LetterboxConfiguration.LetterboxVerticalReachabilityPosition;
import java.io.IOException;
import java.io.PrintWriter;
@@ -58,10 +74,12 @@ public class WindowManagerShellCommand extends ShellCommand {
// Internal service impl -- must perform security checks before touching.
private final WindowManagerService mInternal;
+ private final LetterboxConfiguration mLetterboxConfiguration;
public WindowManagerShellCommand(WindowManagerService service) {
mInterface = service;
mInternal = service;
+ mLetterboxConfiguration = service.mLetterboxConfiguration;
}
@Override
@@ -113,6 +131,14 @@ public class WindowManagerShellCommand extends ShellCommand {
return runGetIgnoreOrientationRequest(pw);
case "dump-visible-window-views":
return runDumpVisibleWindowViews(pw);
+ case "set-letterbox-style":
+ return runSetLetterboxStyle(pw);
+ case "get-letterbox-style":
+ return runGetLetterboxStyle(pw);
+ case "reset-letterbox-style":
+ return runResetLetterboxStyle(pw);
+ case "set-sandbox-display-apis":
+ return runSandboxDisplayApis(pw);
case "set-multi-window-config":
return runSetMultiWindowConfig();
case "get-multi-window-config":
@@ -123,6 +149,8 @@ public class WindowManagerShellCommand extends ShellCommand {
return runReset(pw);
case "disable-blur":
return runSetBlurDisabled(pw);
+ case "shell":
+ return runWmShellCommand(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -331,6 +359,37 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ /**
+ * Override display size and metrics to reflect the DisplayArea of the calling activity.
+ */
+ private int runSandboxDisplayApis(PrintWriter pw) throws RemoteException {
+ int displayId = Display.DEFAULT_DISPLAY;
+ String arg = getNextArgRequired();
+ if ("-d".equals(arg)) {
+ displayId = Integer.parseInt(getNextArgRequired());
+ arg = getNextArgRequired();
+ }
+
+ final boolean sandboxDisplayApis;
+ switch (arg) {
+ case "true":
+ case "1":
+ sandboxDisplayApis = true;
+ break;
+ case "false":
+ case "0":
+ sandboxDisplayApis = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we "
+ + "get " + arg);
+ return -1;
+ }
+
+ mInternal.setSandboxDisplayApis(displayId, sandboxDisplayApis);
+ return 0;
+ }
+
private int runDismissKeyguard(PrintWriter pw) throws RemoteException {
mInterface.dismissKeyguard(null /* callback */, null /* message */);
return 0;
@@ -553,6 +612,497 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
+ final float aspectRatio;
+ try {
+ String arg = getNextArgRequired();
+ aspectRatio = Float.parseFloat(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad aspect ratio format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: aspect ratio should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(aspectRatio);
+ }
+ return 0;
+ }
+
+ private int runSetDefaultMinAspectRatioForUnresizableApps(PrintWriter pw)
+ throws RemoteException {
+ final float aspectRatio;
+ try {
+ String arg = getNextArgRequired();
+ aspectRatio = Float.parseFloat(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad aspect ratio format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: aspect ratio should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setDefaultMinAspectRatioForUnresizableApps(aspectRatio);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException {
+ final int cornersRadius;
+ try {
+ String arg = getNextArgRequired();
+ cornersRadius = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad corners radius format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: corners radius should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxActivityCornersRadius(cornersRadius);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
+ @LetterboxBackgroundType final int backgroundType;
+ try {
+ String arg = getNextArgRequired();
+ switch (arg) {
+ case "solid_color":
+ backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
+ break;
+ case "app_color_background":
+ backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+ break;
+ case "app_color_background_floating":
+ backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+ break;
+ case "wallpaper":
+ backgroundType = LETTERBOX_BACKGROUND_WALLPAPER;
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: 'solid_color', 'app_color_background' or "
+ + "'wallpaper' should be provided as an argument");
+ return -1;
+ }
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'solid_color', 'app_color_background' or "
+ + "'wallpaper' should be provided as an argument" + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundColorResource(PrintWriter pw) throws RemoteException {
+ final int colorId;
+ try {
+ String arg = getNextArgRequired();
+ colorId = mInternal.mContext.getResources()
+ .getIdentifier(arg, "color", "com.android.internal");
+ } catch (NotFoundException e) {
+ getErrPrintWriter().println(
+ "Error: color in '@android:color/resource_name' format should be provided as "
+ + "an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundColorResourceId(colorId);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
+ final Color color;
+ try {
+ String arg = getNextArgRequired();
+ color = Color.valueOf(Color.parseColor(arg));
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: color in #RRGGBB format should be provided as "
+ + "an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundColor(color);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw)
+ throws RemoteException {
+ final int radius;
+ try {
+ String arg = getNextArgRequired();
+ radius = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: blur radius format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: blur radius should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundWallpaperDarkScrimAlpha(PrintWriter pw)
+ throws RemoteException {
+ final float alpha;
+ try {
+ String arg = getNextArgRequired();
+ alpha = Float.parseFloat(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad alpha format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: alpha should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException {
+ final float multiplier;
+ try {
+ String arg = getNextArgRequired();
+ multiplier = Float.parseFloat(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad multiplier format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: multiplier should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxVerticalPositionMultiplier(PrintWriter pw) throws RemoteException {
+ final float multiplier;
+ try {
+ String arg = getNextArgRequired();
+ multiplier = Float.parseFloat(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad multiplier format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: multiplier should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(multiplier);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxIsHorizontalReachabilityEnabled(PrintWriter pw)
+ throws RemoteException {
+ String arg = getNextArg();
+ final boolean enabled;
+ switch (arg) {
+ case "true":
+ case "1":
+ enabled = true;
+ break;
+ case "false":
+ case "0":
+ enabled = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg);
+ return -1;
+ }
+
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(enabled);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxIsVerticalReachabilityEnabled(PrintWriter pw)
+ throws RemoteException {
+ String arg = getNextArg();
+ final boolean enabled;
+ switch (arg) {
+ case "true":
+ case "1":
+ enabled = true;
+ break;
+ case "false":
+ case "0":
+ enabled = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg);
+ return -1;
+ }
+
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setIsVerticalReachabilityEnabled(enabled);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxDefaultPositionForHorizontalReachability(PrintWriter pw)
+ throws RemoteException {
+ @LetterboxHorizontalReachabilityPosition final int position;
+ try {
+ String arg = getNextArgRequired();
+ switch (arg) {
+ case "left":
+ position = LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
+ break;
+ case "center":
+ position = LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
+ break;
+ case "right":
+ position = LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT;
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: 'left', 'center' or 'right' are expected as an argument");
+ return -1;
+ }
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'left', 'center' or 'right' are expected as an argument" + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setDefaultPositionForHorizontalReachability(position);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxDefaultPositionForVerticalReachability(PrintWriter pw)
+ throws RemoteException {
+ @LetterboxVerticalReachabilityPosition final int position;
+ try {
+ String arg = getNextArgRequired();
+ switch (arg) {
+ case "top":
+ position = LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
+ break;
+ case "center":
+ position = LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
+ break;
+ case "bottom":
+ position = LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM;
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: 'top', 'center' or 'bottom' are expected as an argument");
+ return -1;
+ }
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'top', 'center' or 'bottom' are expected as an argument" + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setDefaultPositionForVerticalReachability(position);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxIsEducationEnabled(PrintWriter pw) throws RemoteException {
+ String arg = getNextArg();
+ final boolean enabled;
+ switch (arg) {
+ case "true":
+ case "1":
+ enabled = true;
+ break;
+ case "false":
+ case "0":
+ enabled = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg);
+ return -1;
+ }
+
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setIsEducationEnabled(enabled);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled(PrintWriter pw)
+ throws RemoteException {
+ String arg = getNextArg();
+ final boolean enabled;
+ switch (arg) {
+ case "true":
+ case "1":
+ enabled = true;
+ break;
+ case "false":
+ case "0":
+ enabled = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg);
+ return -1;
+ }
+
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setIsSplitScreenAspectRatioForUnresizableAppsEnabled(enabled);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException {
+ if (peekNextArg() == null) {
+ getErrPrintWriter().println("Error: No arguments provided.");
+ }
+ while (peekNextArg() != null) {
+ String arg = getNextArg();
+ switch (arg) {
+ case "--aspectRatio":
+ runSetFixedOrientationLetterboxAspectRatio(pw);
+ break;
+ case "--minAspectRatioForUnresizable":
+ runSetDefaultMinAspectRatioForUnresizableApps(pw);
+ break;
+ case "--cornerRadius":
+ runSetLetterboxActivityCornersRadius(pw);
+ break;
+ case "--backgroundType":
+ runSetLetterboxBackgroundType(pw);
+ break;
+ case "--backgroundColor":
+ runSetLetterboxBackgroundColor(pw);
+ break;
+ case "--backgroundColorResource":
+ runSetLetterboxBackgroundColorResource(pw);
+ break;
+ case "--wallpaperBlurRadius":
+ runSetLetterboxBackgroundWallpaperBlurRadius(pw);
+ break;
+ case "--wallpaperDarkScrimAlpha":
+ runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
+ break;
+ case "--horizontalPositionMultiplier":
+ runSetLetterboxHorizontalPositionMultiplier(pw);
+ break;
+ case "--verticalPositionMultiplier":
+ runSetLetterboxVerticalPositionMultiplier(pw);
+ break;
+ case "--isHorizontalReachabilityEnabled":
+ runSetLetterboxIsHorizontalReachabilityEnabled(pw);
+ break;
+ case "--isVerticalReachabilityEnabled":
+ runSetLetterboxIsVerticalReachabilityEnabled(pw);
+ break;
+ case "--defaultPositionForHorizontalReachability":
+ runSetLetterboxDefaultPositionForHorizontalReachability(pw);
+ break;
+ case "--defaultPositionForVerticalReachability":
+ runSetLetterboxDefaultPositionForVerticalReachability(pw);
+ break;
+ case "--isEducationEnabled":
+ runSetLetterboxIsEducationEnabled(pw);
+ break;
+ case "--isSplitScreenAspectRatioForUnresizableAppsEnabled":
+ runSetLetterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled(pw);
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: Unrecognized letterbox style option: " + arg);
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ private int runResetLetterboxStyle(PrintWriter pw) throws RemoteException {
+ if (peekNextArg() == null) {
+ resetLetterboxStyle();
+ }
+ synchronized (mInternal.mGlobalLock) {
+ while (peekNextArg() != null) {
+ String arg = getNextArg();
+ switch (arg) {
+ case "aspectRatio":
+ mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
+ break;
+ case "minAspectRatioForUnresizable":
+ mLetterboxConfiguration.resetDefaultMinAspectRatioForUnresizableApps();
+ break;
+ case "cornerRadius":
+ mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
+ break;
+ case "backgroundType":
+ mLetterboxConfiguration.resetLetterboxBackgroundType();
+ break;
+ case "backgroundColor":
+ mLetterboxConfiguration.resetLetterboxBackgroundColor();
+ break;
+ case "wallpaperBlurRadius":
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
+ break;
+ case "wallpaperDarkScrimAlpha":
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
+ break;
+ case "horizontalPositionMultiplier":
+ mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+ break;
+ case "verticalPositionMultiplier":
+ mLetterboxConfiguration.resetLetterboxVerticalPositionMultiplier();
+ break;
+ case "isHorizontalReachabilityEnabled":
+ mLetterboxConfiguration.getIsHorizontalReachabilityEnabled();
+ break;
+ case "isVerticalReachabilityEnabled":
+ mLetterboxConfiguration.getIsVerticalReachabilityEnabled();
+ break;
+ case "defaultPositionForHorizontalReachability":
+ mLetterboxConfiguration.getDefaultPositionForHorizontalReachability();
+ break;
+ case "defaultPositionForVerticalReachability":
+ mLetterboxConfiguration.getDefaultPositionForVerticalReachability();
+ break;
+ case "isEducationEnabled":
+ mLetterboxConfiguration.getIsEducationEnabled();
+ break;
+ case "isSplitScreenAspectRatioForUnresizableAppsEnabled":
+ mLetterboxConfiguration
+ .getIsSplitScreenAspectRatioForUnresizableAppsEnabled();
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: Unrecognized letterbox style option: " + arg);
+ return -1;
+ }
+ }
+ }
+ return 0;
+ }
+
private int runSetMultiWindowConfig() {
if (peekNextArg() == null) {
getErrPrintWriter().println("Error: No arguments provided.");
@@ -627,6 +1177,107 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ private void resetLetterboxStyle() {
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
+ mLetterboxConfiguration.resetDefaultMinAspectRatioForUnresizableApps();
+ mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
+ mLetterboxConfiguration.resetLetterboxBackgroundType();
+ mLetterboxConfiguration.resetLetterboxBackgroundColor();
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
+ mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+ mLetterboxConfiguration.resetIsHorizontalReachabilityEnabled();
+ mLetterboxConfiguration.resetIsVerticalReachabilityEnabled();
+ mLetterboxConfiguration.resetDefaultPositionForHorizontalReachability();
+ mLetterboxConfiguration.resetDefaultPositionForVerticalReachability();
+ mLetterboxConfiguration.resetIsEducationEnabled();
+ mLetterboxConfiguration.resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
+ }
+ }
+
+ private int runGetLetterboxStyle(PrintWriter pw) throws RemoteException {
+ synchronized (mInternal.mGlobalLock) {
+ pw.println("Corner radius: "
+ + mLetterboxConfiguration.getLetterboxActivityCornersRadius());
+ pw.println("Horizontal position multiplier: "
+ + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier());
+ pw.println("Vertical position multiplier: "
+ + mLetterboxConfiguration.getLetterboxVerticalPositionMultiplier());
+ pw.println("Aspect ratio: "
+ + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
+ pw.println("Default min aspect ratio for unresizable apps: "
+ + mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps());
+ pw.println("Is horizontal reachability enabled: "
+ + mLetterboxConfiguration.getIsHorizontalReachabilityEnabled());
+ pw.println("Is vertical reachability enabled: "
+ + mLetterboxConfiguration.getIsVerticalReachabilityEnabled());
+ pw.println("Default position for horizontal reachability: "
+ + LetterboxConfiguration.letterboxHorizontalReachabilityPositionToString(
+ mLetterboxConfiguration.getDefaultPositionForHorizontalReachability()));
+ pw.println("Default position for vertical reachability: "
+ + LetterboxConfiguration.letterboxVerticalReachabilityPositionToString(
+ mLetterboxConfiguration.getDefaultPositionForVerticalReachability()));
+ pw.println("Is education enabled: "
+ + mLetterboxConfiguration.getIsEducationEnabled());
+ pw.println("Is using split screen aspect ratio as aspect ratio for unresizable apps: "
+ + mLetterboxConfiguration
+ .getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
+
+ pw.println("Background type: "
+ + LetterboxConfiguration.letterboxBackgroundTypeToString(
+ mLetterboxConfiguration.getLetterboxBackgroundType()));
+ pw.println(" Background color: " + Integer.toHexString(
+ mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb()));
+ pw.println(" Wallpaper blur radius: "
+ + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius());
+ pw.println(" Wallpaper dark scrim alpha: "
+ + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha());
+ }
+ return 0;
+ }
+
+ private int runWmShellCommand(PrintWriter pw) {
+ String arg = getNextArg();
+
+ switch (arg) {
+ case "tracing":
+ return runWmShellTracing(pw);
+ case "help":
+ default:
+ return runHelp(pw);
+ }
+ }
+
+ private int runHelp(PrintWriter pw) {
+ pw.println("Window Manager Shell commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" tracing <start/stop>");
+ pw.println(" Start/stop shell transition tracing.");
+
+ return 0;
+ }
+
+ private int runWmShellTracing(PrintWriter pw) {
+ String arg = getNextArg();
+
+ switch (arg) {
+ case "start":
+ mInternal.mTransitionTracer.startTrace(pw);
+ break;
+ case "stop":
+ mInternal.mTransitionTracer.stopTrace(pw);
+ break;
+ default:
+ getErrPrintWriter()
+ .println("Error: expected 'start' or 'stop', but got '" + arg + "'");
+ return -1;
+ }
+
+ return 0;
+ }
+
private int runReset(PrintWriter pw) throws RemoteException {
int displayId = getDisplayId(getNextArg());
@@ -651,6 +1302,12 @@ public class WindowManagerShellCommand extends ShellCommand {
// set-ignore-orientation-request
mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
+ // set-letterbox-style
+ resetLetterboxStyle();
+
+ // set-sandbox-display-apis
+ mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true);
+
// set-multi-window-config
runResetMultiWindowConfig();
@@ -685,7 +1342,12 @@ public class WindowManagerShellCommand extends ShellCommand {
pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] ");
pw.println(" If app requested orientation should be ignored.");
+ pw.println(" set-sandbox-display-apis [true|1|false|0]");
+ pw.println(" Sets override of Display APIs getRealSize / getRealMetrics to reflect ");
+ pw.println(" DisplayArea of the activity, or the window bounds if in letterbox or");
+ pw.println(" Size Compat Mode.");
+ printLetterboxHelp(pw);
printMultiWindowConfigHelp(pw);
pw.println(" reset [-d DISPLAY_ID]");
@@ -698,6 +1360,84 @@ public class WindowManagerShellCommand extends ShellCommand {
}
}
+ private void printLetterboxHelp(PrintWriter pw) {
+ pw.println(" set-letterbox-style");
+ pw.println(" Sets letterbox style using the following options:");
+ pw.println(" --aspectRatio aspectRatio");
+ pw.println(" Aspect ratio of letterbox for fixed orientation. If aspectRatio <= "
+ + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
+ pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will");
+ pw.println(" be ignored and framework implementation will determine aspect ratio.");
+ pw.println(" --minAspectRatioForUnresizable aspectRatio");
+ pw.println(" Default min aspect ratio for unresizable apps which is used when an");
+ pw.println(" app is eligible for the size compat mode. If aspectRatio <= "
+ + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
+ pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will");
+ pw.println(" be ignored and framework implementation will determine aspect ratio.");
+ pw.println(" --cornerRadius radius");
+ pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,");
+ pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be");
+ pw.println(" ignored and corners of the activity won't be rounded.");
+ pw.println(" --backgroundType [reset|solid_color|app_color_background");
+ pw.println(" |app_color_background_floating|wallpaper]");
+ pw.println(" Type of background used in the letterbox mode.");
+ pw.println(" --backgroundColor color");
+ pw.println(" Color of letterbox which is be used when letterbox background type");
+ pw.println(" is 'solid-color'. Use (set)get-letterbox-style to check and control");
+ pw.println(" letterbox background type. See Color#parseColor for allowed color");
+ pw.println(" formats (#RRGGBB and some colors by name, e.g. magenta or olive).");
+ pw.println(" --backgroundColorResource resource_name");
+ pw.println(" Color resource name of letterbox background which is used when");
+ pw.println(" background type is 'solid-color'. Use (set)get-letterbox-style to");
+ pw.println(" check and control background type. Parameter is a color resource");
+ pw.println(" name, for example, @android:color/system_accent2_50.");
+ pw.println(" --wallpaperBlurRadius radius");
+ pw.println(" Blur radius for 'wallpaper' letterbox background. If radius <= 0");
+ pw.println(" both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius");
+ pw.println(" are ignored and 0 is used.");
+ pw.println(" --wallpaperDarkScrimAlpha alpha");
+ pw.println(" Alpha of a black translucent scrim shown over 'wallpaper'");
+ pw.println(" letterbox background. If alpha < 0 or >= 1 both it and");
+ pw.println(" R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored");
+ pw.println(" and 0.0 (transparent) is used instead.");
+ pw.println(" --horizontalPositionMultiplier multiplier");
+ pw.println(" Horizontal position of app window center. If multiplier < 0 or > 1,");
+ pw.println(" both it and R.dimen.config_letterboxHorizontalPositionMultiplier");
+ pw.println(" are ignored and central position (0.5) is used.");
+ pw.println(" --verticalPositionMultiplier multiplier");
+ pw.println(" Vertical position of app window center. If multiplier < 0 or > 1,");
+ pw.println(" both it and R.dimen.config_letterboxVerticalPositionMultiplier");
+ pw.println(" are ignored and central position (0.5) is used.");
+ pw.println(" --isHorizontalReachabilityEnabled [true|1|false|0]");
+ pw.println(" Whether horizontal reachability repositioning is allowed for ");
+ pw.println(" letterboxed fullscreen apps in landscape device orientation.");
+ pw.println(" --isVerticalReachabilityEnabled [true|1|false|0]");
+ pw.println(" Whether vertical reachability repositioning is allowed for ");
+ pw.println(" letterboxed fullscreen apps in portrait device orientation.");
+ pw.println(" --defaultPositionForHorizontalReachability [left|center|right]");
+ pw.println(" Default position of app window when horizontal reachability is.");
+ pw.println(" enabled.");
+ pw.println(" --defaultPositionForVerticalReachability [top|center|bottom]");
+ pw.println(" Default position of app window when vertical reachability is.");
+ pw.println(" enabled.");
+ pw.println(" --isEducationEnabled [true|1|false|0]");
+ pw.println(" Whether education is allowed for letterboxed fullscreen apps.");
+ pw.println(" --isSplitScreenAspectRatioForUnresizableAppsEnabled [true|1|false|0]");
+ pw.println(" Whether using split screen aspect ratio as a default aspect ratio for");
+ pw.println(" unresizable apps.");
+ pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
+ pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
+ pw.println(" |horizontalPositionMultiplier|verticalPositionMultiplier");
+ pw.println(" |isHorizontalReachabilityEnabled|isVerticalReachabilityEnabled");
+ pw.println(" isEducationEnabled||defaultPositionMultiplierForHorizontalReachability");
+ pw.println(" ||defaultPositionMultiplierForVerticalReachability]");
+ pw.println(" Resets overrides to default values for specified properties separated");
+ pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
+ pw.println(" If no arguments provided, all values will be reset.");
+ pw.println(" get-letterbox-style");
+ pw.println(" Prints letterbox style configuration.");
+ }
+
private void printMultiWindowConfigHelp(PrintWriter pw) {
pw.println(" set-multi-window-config");
pw.println(" Sets options to determine if activity should be shown in multi window:");
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index aee66faa8faa..ee6435493699 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.isStartResultSuccessful;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER;
@@ -43,6 +44,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
@@ -148,7 +150,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
void setWindowManager(WindowManagerService wms) {
- mTransitionController = new TransitionController(mService, wms.mTaskSnapshotController);
+ mTransitionController = new TransitionController(mService, wms.mTaskSnapshotController,
+ wms.mTransitionTracer);
mTransitionController.registerLegacyListener(wms.mActivityManagerAppTransitionNotifier);
}
@@ -326,7 +329,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
adapter.setCallingPidUid(caller.mPid, caller.mUid);
dc.prepareAppTransition(type);
- dc.mAppTransition.overridePendingAppTransitionRemote(adapter, true /* sync */);
+ dc.mAppTransition.overridePendingAppTransitionRemote(adapter, true /* sync */,
+ false /* isActivityEmbedding */);
syncId = startSyncWithOrganizer(callback);
applyTransaction(t, syncId, null /* transition */, caller);
setSyncReady(syncId);
@@ -393,11 +397,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
// Go through all tasks and collect them before the rotation
// TODO(shell-transitions): move collect() to onConfigurationChange once
// wallpaper handling is synchronized.
- dc.mTransitionController.collectForDisplayChange(dc, transition);
+ dc.mTransitionController.collectForDisplayAreaChange(dc, transition);
dc.sendNewConfiguration();
effects |= TRANSACT_EFFECTS_LIFECYCLE;
}
}
+ final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
+ final int hopSize = hops.size();
ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
t.getChanges().entrySet().iterator();
@@ -415,19 +421,53 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
addToSyncSet(syncId, wc);
}
if (transition != null) transition.collect(wc);
+ final DisplayArea da = wc.asDisplayArea();
+ // Only check DisplayArea here as a similar thing is done for DisplayContent above.
+ if (da != null && wc.asDisplayContent() == null
+ && entry.getValue().getWindowingMode() != da.getWindowingMode()) {
+ // Go through all tasks and collect them before changing the windowing mode of a
+ // display-level container.
+ // TODO(shell-transitions): handle this more elegantly.
+ da.mTransitionController.collectForDisplayAreaChange(da, transition);
+ }
- if (finishTransition != null) {
- // Deal with edge-cases in recents where it pretends to finish itself.
- if ((entry.getValue().getChangeMask()
- & WindowContainerTransaction.Change.CHANGE_FORCE_NO_PIP) != 0) {
+ if ((entry.getValue().getChangeMask()
+ & WindowContainerTransaction.Change.CHANGE_FORCE_NO_PIP) != 0) {
+ // Disable entering pip (eg. when recents pretends to finish itself)
+ if (finishTransition != null) {
finishTransition.setCanPipOnFinish(false /* canPipOnFinish */);
+ } else if (transition != null) {
+ transition.setCanPipOnFinish(false /* canPipOnFinish */);
}
}
+ // A bit hacky, but we need to detect "remove PiP" so that we can "wrap" the
+ // setWindowingMode call in force-hidden.
+ boolean forceHiddenForPip = false;
+ if (wc.asTask() != null && wc.inPinnedWindowingMode()
+ && entry.getValue().getWindowingMode() == WINDOWING_MODE_UNDEFINED) {
+ // We are in pip and going to undefined. Now search hierarchy ops to determine
+ // whether we are removing pip or expanding pip.
+ for (int i = 0; i < hopSize; ++i) {
+ final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
+ if (hop.getType() != HIERARCHY_OP_TYPE_REORDER) continue;
+ final WindowContainer hopWc = WindowContainer.fromBinder(
+ hop.getContainer());
+ if (!wc.equals(hopWc)) continue;
+ forceHiddenForPip = !hop.getToTop();
+ }
+ }
+ if (forceHiddenForPip) {
+ wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);
+ }
int containerEffect = applyWindowContainerChange(wc, entry.getValue(),
t.getErrorCallbackToken());
effects |= containerEffect;
+ if (forceHiddenForPip) {
+ wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */);
+ }
+
// Lifecycle changes will trigger ensureConfig for everything.
if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
&& (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
@@ -435,8 +475,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
}
// Hierarchy changes
- final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
- final int hopSize = hops.size();
if (hopSize > 0) {
final boolean isInLockTaskMode = mService.isInLockTaskMode();
for (int i = 0; i < hopSize; ++i) {
@@ -498,7 +536,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
}
- if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) == 0) {
+ if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
mService.addWindowLayoutReasons(LAYOUT_REASON_CONFIG_CHANGED);
}
} finally {
@@ -552,6 +590,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
+ " windowing mode during locked task mode.");
}
+ if (windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED) {
+ // Do not directly put the container into PINNED mode as it may not support it or
+ // the app may not want to enter it. Instead, send a signal to request PIP
+ // mode to the app if they wish to support it below in #applyTaskChanges.
+ return effects;
+ }
+
final int prevMode = container.getWindowingMode();
container.setWindowingMode(windowingMode);
if (prevMode != container.getWindowingMode()) {
@@ -608,6 +653,28 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
tr.mDisplayContent.mPinnedTaskController.setEnterPipBounds(enterPipBounds);
}
+ if (c.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_PINNED
+ && !tr.inPinnedWindowingMode()) {
+ final ActivityRecord activity = tr.getTopNonFinishingActivity();
+ if (activity != null) {
+ final boolean lastSupportsEnterPipOnTaskSwitch =
+ activity.supportsEnterPipOnTaskSwitch;
+ // Temporarily force enable enter PIP on task switch so that PIP is requested
+ // regardless of whether the activity is resumed or paused.
+ activity.supportsEnterPipOnTaskSwitch = true;
+ boolean canEnterPip = activity.checkEnterPictureInPictureState(
+ "applyTaskChanges", true /* beforeStopping */);
+ if (canEnterPip) {
+ canEnterPip = mService.mActivityClientController
+ .requestPictureInPictureMode(activity);
+ }
+ if (!canEnterPip) {
+ // Restore the flag to its previous state when the activity cannot enter PIP.
+ activity.supportsEnterPipOnTaskSwitch = lastSupportsEnterPipOnTaskSwitch;
+ }
+ }
+ }
+
return effects;
}
@@ -794,7 +861,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
break;
}
- tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */);
+ tf1.setAdjacentTaskFragment(tf2);
effects |= TRANSACT_EFFECTS_LIFECYCLE;
final Bundle bundle = hop.getLaunchOptions();
@@ -836,22 +903,22 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
tf.getDisplayContent().setFocusedApp(targetFocus);
break;
}
- default: {
- // The other operations may change task order so they are skipped while in lock
- // task mode. The above operations are still allowed because they don't move
- // tasks. And it may be necessary such as clearing launch root after entering
- // lock task mode.
- if (isInLockTaskMode) {
- Slog.w(TAG, "Skip applying hierarchy operation " + hop
- + " while in lock task mode");
- return effects;
- }
- }
- }
-
- switch (type) {
case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: {
- effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
+ effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId,
+ isInLockTaskMode);
+ break;
+ }
+ case HIERARCHY_OP_TYPE_LAUNCH_TASK: {
+ mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
+ "launchTask HierarchyOp");
+ final Bundle launchOpts = hop.getLaunchOptions();
+ final int taskId = launchOpts.getInt(
+ WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
+ launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
+ final SafeActivityOptions safeOptions =
+ SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid);
+ waitAsyncStart(() -> mService.mTaskSupervisor.startActivityFromRecents(
+ caller.mPid, caller.mUid, taskId, safeOptions));
break;
}
case HIERARCHY_OP_TYPE_REORDER:
@@ -861,6 +928,16 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
Slog.e(TAG, "Attempt to operate on detached container: " + wc);
break;
}
+ // There is no use case to ask the reparent operation in lock-task mode now, so keep
+ // skipping this operation as usual.
+ if (isInLockTaskMode && type == HIERARCHY_OP_TYPE_REPARENT) {
+ Slog.w(TAG, "Skip applying hierarchy operation " + hop
+ + " while in lock task mode");
+ break;
+ }
+ if (isLockTaskModeViolation(wc.getParent(), wc.asTask(), isInLockTaskMode)) {
+ break;
+ }
if (syncId >= 0) {
addToSyncSet(syncId, wc);
}
@@ -886,19 +963,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
effects |= sanitizeAndApplyHierarchyOp(wc, hop);
break;
}
- case HIERARCHY_OP_TYPE_LAUNCH_TASK: {
- mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
- "launchTask HierarchyOp");
- final Bundle launchOpts = hop.getLaunchOptions();
- final int taskId = launchOpts.getInt(
- WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
- launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
- final SafeActivityOptions safeOptions =
- SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid);
- waitAsyncStart(() -> mService.mTaskSupervisor.startActivityFromRecents(
- caller.mPid, caller.mUid, taskId, safeOptions));
- break;
+ default: {
+ // The other operations may change task order so they are skipped while in lock
+ // task mode. The above operations are still allowed because they don't move
+ // tasks. And it may be necessary such as clearing launch root after entering
+ // lock task mode.
+ if (isInLockTaskMode) {
+ Slog.w(TAG, "Skip applying hierarchy operation " + hop
+ + " while in lock task mode");
+ return effects;
+ }
}
+ }
+
+ switch (type) {
case HIERARCHY_OP_TYPE_PENDING_INTENT: {
String resolvedType = hop.getActivityIntent() != null
? hop.getActivityIntent().resolveTypeIfNeeded(
@@ -1090,8 +1168,25 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return TRANSACT_EFFECTS_LIFECYCLE;
}
+ private boolean isLockTaskModeViolation(WindowContainer parent, Task task,
+ boolean isInLockTaskMode) {
+ if (!isInLockTaskMode || parent == null || task == null) {
+ return false;
+ }
+ final LockTaskController lockTaskController = mService.getLockTaskController();
+ boolean taskViolation = lockTaskController.isLockTaskModeViolation(task);
+ if (!taskViolation && parent.asTask() != null) {
+ taskViolation = lockTaskController.isLockTaskModeViolation(parent.asTask());
+ }
+ if (taskViolation) {
+ Slog.w(TAG, "Can't support the operation since in lock task mode violation. "
+ + " Task: " + task + " Parent : " + parent);
+ }
+ return taskViolation;
+ }
+
private int reparentChildrenTasksHierarchyOp(WindowContainerTransaction.HierarchyOp hop,
- @Nullable Transition transition, int syncId) {
+ @Nullable Transition transition, int syncId, boolean isInLockTaskMode) {
WindowContainer<?> currentParent = hop.getContainer() != null
? WindowContainer.fromBinder(hop.getContainer()) : null;
WindowContainer newParent = hop.getNewParent() != null
@@ -1129,6 +1224,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
? newParent.asTask().getDisplayArea()
: newParent.asTaskDisplayArea();
final WindowContainer finalCurrentParent = currentParent;
+ final WindowContainer finalNewParent = newParent;
Slog.i(TAG, "reparentChildrenTasksHierarchyOp"
+ " currentParent=" + currentParent + " newParent=" + newParent + " hop=" + hop);
@@ -1148,8 +1244,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
+ " task=" + task);
return false;
}
- if (!ArrayUtils.contains(hop.getActivityTypes(), task.getActivityType())
- || !ArrayUtils.contains(hop.getWindowingModes(), task.getWindowingMode())) {
+ if (!ArrayUtils.isEmpty(hop.getActivityTypes())
+ && !ArrayUtils.contains(hop.getActivityTypes(), task.getActivityType())) {
+ return false;
+ }
+ if (!ArrayUtils.isEmpty(hop.getWindowingModes())
+ && !ArrayUtils.contains(hop.getWindowingModes(), task.getWindowingMode())) {
+ return false;
+ }
+ if (isLockTaskModeViolation(finalNewParent, task, isInLockTaskMode)) {
return false;
}
@@ -1196,7 +1299,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
Slog.e(TAG, "Attempt to set adjacent TaskFragment in PIP Task");
return 0;
}
- root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether());
+ root1.setAdjacentTaskFragment(root2);
return TRANSACT_EFFECTS_LIFECYCLE;
}
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index de87ab9dcce0..3e165e442d79 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -296,9 +296,9 @@ public abstract class WindowOrientationListener {
/**
* Whether the device is in the lock screen.
- * @return returns true if the screen is locked. Otherwise, returns false.
+ * @return returns true if the key guard is showing on the lock screen.
*/
- public abstract boolean isKeyguardLocked();
+ public abstract boolean isKeyguardShowingAndNotOccluded();
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
@@ -1151,7 +1151,7 @@ public abstract class WindowOrientationListener {
FrameworkStatsLog.DEVICE_ROTATED__ROTATION_EVENT_TYPE__ACTUAL_EVENT);
if (isRotationResolverEnabled()) {
- if (isKeyguardLocked()) {
+ if (isKeyguardShowingAndNotOccluded()) {
if (mLastRotationResolution != ROTATION_UNSET
&& SystemClock.uptimeMillis() - mLastRotationResolutionTimeStamp
< mRotationMemorizationTimeoutMillis) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 40417a4857d3..3ff912cca091 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.content.res.Configuration.ASSETS_SEQ_UNDEFINED;
@@ -24,7 +25,6 @@ import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.server.am.ActivityManagerService.MY_PID;
import static com.android.server.wm.ActivityRecord.State.DESTROYED;
import static com.android.server.wm.ActivityRecord.State.DESTROYING;
import static com.android.server.wm.ActivityRecord.State.PAUSED;
@@ -39,6 +39,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.wm.WindowManagerService.MY_PID;
import android.Manifest;
import android.annotation.NonNull;
@@ -195,6 +196,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
/** Whether the process configuration is waiting to be dispatched to the process. */
private boolean mHasPendingConfigurationChange;
+ /** If the process state is in (<=) the cached state, then defer delivery of the config. */
+ private static final int CACHED_CONFIG_PROC_STATE = PROCESS_STATE_CACHED_ACTIVITY;
+ /** Whether {@link #mLastReportedConfiguration} is deferred by the cached state. */
+ private volatile boolean mHasCachedConfiguration;
+
/**
* Registered {@link DisplayArea} as a listener to override config changes. {@code null} if not
* registered.
@@ -316,8 +322,27 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return mCurProcState;
}
+ /**
+ * Sets the computed process state from the oom adjustment calculation. This is frequently
+ * called in activity manager's lock, so don't use window manager lock here.
+ */
+ @HotPath(caller = HotPath.OOM_ADJUSTMENT)
public void setReportedProcState(int repProcState) {
+ final int prevProcState = mRepProcState;
mRepProcState = repProcState;
+
+ // Deliver the cached config if the app changes from cached state to non-cached state.
+ final IApplicationThread thread = mThread;
+ if (prevProcState >= CACHED_CONFIG_PROC_STATE && repProcState < CACHED_CONFIG_PROC_STATE
+ && thread != null && mHasCachedConfiguration) {
+ final Configuration config;
+ synchronized (mLastReportedConfiguration) {
+ config = new Configuration(mLastReportedConfiguration);
+ }
+ // Schedule immediately to make sure the app component (e.g. receiver, service) can get
+ // the latest configuration in their lifecycle callbacks (e.g. onReceive, onCreate).
+ scheduleConfigurationChange(thread, config);
+ }
}
int getReportedProcState() {
@@ -1126,6 +1151,13 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
mAtm.mH.sendMessage(m);
}
+ /** Refreshes oom adjustment and process state of this process. */
+ void scheduleUpdateOomAdj() {
+ mAtm.mH.sendMessage(PooledLambda.obtainMessage(WindowProcessListener::updateProcessInfo,
+ mListener, false /* updateServiceConnectionActivities */,
+ false /* activityChange */, true /* updateOomAdj */));
+ }
+
/** Makes the process have top state before oom-adj is computed from a posted message. */
void addToPendingTop() {
mAtm.mAmInternal.addPendingTopUid(mUid, mPid, mThread);
@@ -1328,12 +1360,22 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
@Override
public void onConfigurationChanged(Configuration newGlobalConfig) {
super.onConfigurationChanged(newGlobalConfig);
- updateConfiguration();
- }
+ final Configuration config = getConfiguration();
+ if (mLastReportedConfiguration.equals(config)) {
+ // Nothing changed.
+ if (Build.IS_DEBUGGABLE && mHasImeService) {
+ // TODO (b/135719017): Temporary log for debugging IME service.
+ Slog.w(TAG_CONFIGURATION, "Current config: " + config
+ + " unchanged for IME proc " + mName);
+ }
+ return;
+ }
- @Override
- public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
- super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
+ if (mPauseConfigurationDispatchCount > 0) {
+ mHasPendingConfigurationChange = true;
+ return;
+ }
+ dispatchConfiguration(config);
}
@Override
@@ -1359,25 +1401,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
resolvedConfig.seq = newParentConfig.seq;
}
- private void updateConfiguration() {
- final Configuration config = getConfiguration();
- if (mLastReportedConfiguration.diff(config) == 0) {
- // Nothing changed.
- if (Build.IS_DEBUGGABLE && mHasImeService) {
- // TODO (b/135719017): Temporary log for debugging IME service.
- Slog.w(TAG_CONFIGURATION, "Current config: " + config
- + " unchanged for IME proc " + mName);
- }
- return;
- }
-
- if (mPauseConfigurationDispatchCount > 0) {
- mHasPendingConfigurationChange = true;
- return;
- }
- dispatchConfiguration(config);
- }
-
void dispatchConfiguration(Configuration config) {
mHasPendingConfigurationChange = false;
if (mThread == null) {
@@ -1388,29 +1411,47 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
return;
}
+
+ config.seq = mAtm.increaseConfigurationSeqLocked();
+ setLastReportedConfiguration(config);
+
+ // A cached process doesn't have running application components, so it is unnecessary to
+ // notify the configuration change. The last-reported-configuration is still set because
+ // setReportedProcState() should not write any fields that require WM lock.
+ if (mRepProcState >= CACHED_CONFIG_PROC_STATE) {
+ mHasCachedConfiguration = true;
+ // Because there are 2 volatile accesses in setReportedProcState(): mRepProcState and
+ // mHasCachedConfiguration, check again in case mRepProcState is changed but hasn't
+ // read the change of mHasCachedConfiguration.
+ if (mRepProcState >= CACHED_CONFIG_PROC_STATE) {
+ return;
+ }
+ }
+
+ scheduleConfigurationChange(mThread, config);
+ }
+
+ private void scheduleConfigurationChange(IApplicationThread thread, Configuration config) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s new config %s", mName,
config);
if (Build.IS_DEBUGGABLE && mHasImeService) {
// TODO (b/135719017): Temporary log for debugging IME service.
Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName + " new config " + config);
}
-
+ mHasCachedConfiguration = false;
try {
- config.seq = mAtm.increaseConfigurationSeqLocked();
- mAtm.getLifecycleManager().scheduleTransaction(mThread,
+ mAtm.getLifecycleManager().scheduleTransaction(thread,
ConfigurationChangeItem.obtain(config));
- setLastReportedConfiguration(config);
} catch (Exception e) {
- Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
+ Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change: " + mOwner, e);
}
}
void setLastReportedConfiguration(Configuration config) {
- mLastReportedConfiguration.setTo(config);
- }
-
- Configuration getLastReportedConfiguration() {
- return mLastReportedConfiguration;
+ // Synchronize for the access from setReportedProcState().
+ synchronized (mLastReportedConfiguration) {
+ mLastReportedConfiguration.setTo(config);
+ }
}
void pauseConfigurationDispatch() {
@@ -1461,6 +1502,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// config seq. This increment ensures that the client won't ignore the configuration.
config.seq = mAtm.increaseConfigurationSeqLocked();
}
+ // LaunchActivityItem includes the latest process configuration.
+ mHasCachedConfiguration = false;
return config;
}
@@ -1688,7 +1731,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
pw.println(prefix + " Configuration=" + getConfiguration());
pw.println(prefix + " OverrideConfiguration=" + getRequestedOverrideConfiguration());
- pw.println(prefix + " mLastReportedConfiguration=" + mLastReportedConfiguration);
+ pw.println(prefix + " mLastReportedConfiguration=" + (mHasCachedConfiguration
+ ? ("(cached) " + mLastReportedConfiguration) : mLastReportedConfiguration));
final int stateFlags = mActivityStateFlags;
if (stateFlags != ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4c32edc6d709..af8c4c8e9370 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -115,8 +115,8 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RESIZE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_INSETS;
-import static com.android.server.am.ActivityManagerService.MY_PID;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
@@ -151,6 +151,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
+import static com.android.server.wm.WindowManagerService.MY_PID;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
@@ -392,6 +393,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
int mSyncSeqId = 0;
int mLastSeqIdSentToRelayout = 0;
+ /** The last syncId associated with a prepareSync or 0 when no sync is active. */
+ int mPrepareSyncSeqId = 0;
+
/**
* {@code true} when the client was still drawing for sync when the sync-set was finished or
* cancelled. This can happen if the window goes away during a sync. In this situation we need
@@ -616,11 +620,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
int mLastVisibleLayoutRotation = -1;
/**
- * Set when we need to report the orientation change to client to trigger a relayout.
- */
- boolean mReportOrientationChanged;
-
- /**
* How long we last kept the screen frozen.
*/
int mLastFreezeDuration;
@@ -814,7 +813,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
};
private final Consumer<SurfaceControl.Transaction> mSetSurfacePositionConsumer = t -> {
- if (mSurfaceControl != null && mSurfaceControl.isValid()) {
+ // Only apply the position to the surface when there's no leash created.
+ if (mSurfaceControl != null && mSurfaceControl.isValid() && !mSurfaceAnimator.hasLeash()) {
t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
}
};
@@ -1251,7 +1251,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mSession.windowAddedLocked();
}
- boolean updateGlobalScale() {
+ void updateGlobalScale() {
if (hasCompatScale()) {
if (mOverrideScale != 1f) {
mGlobalScale = mToken.hasSizeCompatBounds()
@@ -1261,11 +1261,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mGlobalScale = mToken.getSizeCompatScale();
}
mInvGlobalScale = 1f / mGlobalScale;
- return true;
+ return;
}
mGlobalScale = mInvGlobalScale = 1f;
- return false;
}
/**
@@ -1481,15 +1480,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mAttrs;
}
- WindowManager.LayoutParams getLayoutingAttrs(int rotation) {
- final WindowManager.LayoutParams[] paramsForRotation = mAttrs.paramsForRotation;
- if (paramsForRotation == null || paramsForRotation.length != 4
- || paramsForRotation[rotation] == null) {
- return mAttrs;
- }
- return paramsForRotation[rotation];
- }
-
/** Retrieves the flags used to disable system UI functions. */
int getDisableFlags() {
return mDisableFlags;
@@ -1527,29 +1517,27 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final boolean dragResizingChanged = isDragResizeChanged()
&& !isDragResizingChangeReported();
+ final boolean attachedFrameChanged = LOCAL_LAYOUT
+ && mLayoutAttached && getParentWindow().frameChanged();
+
if (DEBUG) {
Slog.v(TAG_WM, "Resizing " + this + ": configChanged=" + configChanged
+ " dragResizingChanged=" + dragResizingChanged
+ " last=" + mWindowFrames.mLastFrame + " frame=" + mWindowFrames.mFrame);
}
- // We update mLastFrame always rather than in the conditional with the last inset
- // variables, because mFrameSizeChanged only tracks the width and height changing.
- updateLastFrames();
-
// Add a window that is using blastSync to the resizing list if it hasn't been reported
// already. This because the window is waiting on a finishDrawing from the client.
if (didFrameInsetsChange
|| configChanged
|| insetsChanged
|| dragResizingChanged
- || mReportOrientationChanged
- || shouldSendRedrawForSync()) {
+ || shouldSendRedrawForSync()
+ || attachedFrameChanged) {
ProtoLog.v(WM_DEBUG_RESIZE,
- "Resize reasons for w=%s: %s configChanged=%b "
- + "dragResizingChanged=%b reportOrientationChanged=%b",
+ "Resize reasons for w=%s: %s configChanged=%b dragResizingChanged=%b",
this, mWindowFrames.getInsetsChangedInfo(),
- configChanged, dragResizingChanged, mReportOrientationChanged);
+ configChanged, dragResizingChanged);
if (insetsChanged) {
mWindowFrames.setInsetsChanged(false);
@@ -1601,6 +1589,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
+ private boolean frameChanged() {
+ return !mWindowFrames.mFrame.equals(mWindowFrames.mLastFrame);
+ }
+
boolean getOrientationChanging() {
// In addition to the local state flag, we must also consider the difference in the last
// reported configuration vs. the current state. If the client code has not been informed of
@@ -1711,7 +1703,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mFrozenInsetsState != null ? mFrozenInsetsState : getMergedInsetsState();
final InsetsState insetsStateForWindow = insetsPolicy
.enforceInsetsPolicyForTarget(insetTypeProvidedByWindow,
- getWindowingMode(), isAlwaysOnTop(), rawInsetsState);
+ getWindowingMode(), isAlwaysOnTop(), mAttrs.type, rawInsetsState);
return insetsPolicy.adjustInsetsForWindow(this, insetsStateForWindow,
includeTransient);
}
@@ -2180,6 +2172,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return;
}
if (mActivityRecord != null) {
+ if (!mActivityRecord.mVisibleRequested) return;
if (mActivityRecord.allDrawn) {
// The allDrawn of activity is reset when the visibility is changed to visible, so
// the content should be ready if allDrawn is set.
@@ -3468,10 +3461,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mClient.asBinder().isBinderAlive();
}
- boolean isClosing() {
- return mAnimatingExit || (mActivityRecord != null && mActivityRecord.isClosingOrEnteringPip());
- }
-
void sendAppVisibilityToClients() {
super.sendAppVisibilityToClients();
@@ -3579,10 +3568,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mAnimatingExit = false;
ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=destroySurface win=%s", this);
- // Clear the flag so the buffer requested for the next new surface won't be dropped by
- // mistaking the surface size needs to update.
- mReportOrientationChanged = false;
-
if (useBLASTSync()) {
immediatelyNotifyBlastSync();
}
@@ -3855,6 +3840,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mInvGlobalScale != 1.0f && hasCompatScale()) {
outFrames.displayFrame.scale(mInvGlobalScale);
}
+ if (mLayoutAttached) {
+ if (outFrames.attachedFrame == null) {
+ outFrames.attachedFrame = new Rect();
+ }
+ outFrames.attachedFrame.set(getParentWindow().getCompatFrame());
+ }
// Note: in the cases where the window is tied to an activity, we should not send a
// configuration update when the window has requested to be hidden. Doing so can lead to
@@ -3899,21 +3890,25 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
ProtoLog.i(WM_DEBUG_ORIENTATION, "Resizing %s WITH DRAW PENDING", this);
}
- final boolean reportOrientation = mReportOrientationChanged;
// Always reset these states first, so if {@link IWindow#resized} fails, this
// window won't be added to {@link WindowManagerService#mResizingWindows} and set
// {@link #mOrientationChanging} to true again by {@link #updateResizingWindowIfNeeded}
// that may cause WINDOW_FREEZE_TIMEOUT because resizing the client keeps failing.
- mReportOrientationChanged = false;
mDragResizingChangeReported = true;
mWindowFrames.clearReportResizeHints();
+ // We update mLastFrame always rather than in the conditional with the last inset
+ // variables, because mFrameSizeChanged only tracks the width and height changing.
+ updateLastFrames();
+
+ final int prevRotation = mLastReportedConfiguration
+ .getMergedConfiguration().windowConfiguration.getRotation();
fillClientWindowFramesAndConfiguration(mClientWindowFrames, mLastReportedConfiguration,
true /* useLatestConfig */, false /* relayoutVisible */);
final boolean syncRedraw = shouldSendRedrawForSync();
final boolean reportDraw = syncRedraw || drawPending;
final boolean isDragResizeChanged = isDragResizeChanged();
- final boolean forceRelayout = syncRedraw || reportOrientation || isDragResizeChanged;
+ final boolean forceRelayout = syncRedraw || isDragResizeChanged;
final DisplayContent displayContent = getDisplayContent();
final boolean alwaysConsumeSystemBars =
displayContent.getDisplayPolicy().areSystemBarsForcedShownLw();
@@ -3940,7 +3935,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mClient.resized(mClientWindowFrames, reportDraw, mLastReportedConfiguration,
getCompatInsetsState(), forceRelayout, alwaysConsumeSystemBars, displayId,
mSyncSeqId, resizeMode);
- if (drawPending && reportOrientation && mOrientationChanging) {
+ if (drawPending && prevRotation >= 0 && prevRotation != mLastReportedConfiguration
+ .getMergedConfiguration().windowConfiguration.getRotation()) {
mOrientationChangeRedrawRequestTime = SystemClock.elapsedRealtime();
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Requested redraw for orientation change: %s", this);
@@ -4365,12 +4361,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
+ " mDestroying=" + mDestroying
+ " mRemoved=" + mRemoved);
}
- if (getOrientationChanging() || mAppFreezing || mReportOrientationChanged) {
+ if (getOrientationChanging() || mAppFreezing) {
pw.println(prefix + "mOrientationChanging=" + mOrientationChanging
+ " configOrientationChanging="
+ (getLastReportedConfiguration().orientation != getConfiguration().orientation)
- + " mAppFreezing=" + mAppFreezing
- + " mReportOrientationChanged=" + mReportOrientationChanged);
+ + " mAppFreezing=" + mAppFreezing);
}
if (mLastFreezeDuration != 0) {
pw.print(prefix + "mLastFreezeDuration=");
@@ -4424,6 +4419,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
pw.println(prefix + "Requested visibilities: " + visibilityString);
}
}
+
+ pw.println(prefix + "mPrepareSyncSeqId=" + mPrepareSyncSeqId);
}
@Override
@@ -4977,18 +4974,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION);
}
- /**
- * @return {@code true} if self or the parent container of the window is in transition.
- * (e.g. The app or recents transition)
- */
- boolean inTransitionSelfOrParent() {
- if (!mTransitionController.isShellTransitionsEnabled()) {
- return isAnimating(PARENTS | TRANSITION,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
- }
- return mTransitionController.inTransition(this);
- }
-
private boolean shouldFinishAnimatingExit() {
// Exit animation might be applied soon.
if (inTransition()) {
@@ -5575,7 +5560,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
}
if (surfaceSizeChanged && mWinAnimator.getShown() && !canPlayMoveAnimation()
- && okToDisplay()) {
+ && okToDisplay() && mSyncState == SYNC_STATE_NONE) {
applyWithNextDraw(mSetSurfacePositionConsumer);
} else {
mSetSurfacePositionConsumer.accept(t);
@@ -5927,8 +5912,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mWinAnimator.getSurfaceControl();
}
+ /** Drops a buffer for this window's view-root from a transaction */
+ private void dropBufferFrom(Transaction t) {
+ SurfaceControl viewSurface = getClientViewRootSurface();
+ if (viewSurface == null) return;
+ t.setBuffer(viewSurface, (android.hardware.HardwareBuffer) null);
+ }
+
@Override
boolean prepareSync() {
+ if (!mDrawHandlers.isEmpty()) {
+ Slog.w(TAG, "prepareSync with mDrawHandlers, " + this + ", " + Debug.getCallers(8));
+ }
if (!super.prepareSync()) {
return false;
}
@@ -5939,7 +5934,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// to draw even if the children draw first or don't need to sync, so we start
// in WAITING state rather than READY.
mSyncState = SYNC_STATE_WAITING_FOR_DRAW;
+
+ if (mPrepareSyncSeqId > 0) {
+ // another prepareSync during existing sync (eg. reparented), so pre-emptively
+ // drop buffer (if exists). If the buffer hasn't been received yet, it will be
+ // dropped in finishDrawing.
+ ProtoLog.d(WM_DEBUG_SYNC_ENGINE, "Preparing to sync a window that was already in the"
+ + " sync, so try dropping buffer. win=%s", this);
+ dropBufferFrom(mSyncTransaction);
+ }
+
mSyncSeqId++;
+ mPrepareSyncSeqId = mSyncSeqId;
requestRedrawForSync();
return true;
}
@@ -5960,6 +5966,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mRedrawForSyncReported) {
mClientWasDrawingForSync = true;
}
+ mPrepareSyncSeqId = 0;
+ if (cancel) {
+ // This is leaving sync so any buffers left in the sync have a chance of
+ // being applied out-of-order and can also block the buffer queue for this
+ // window. To prevent this, drop the buffer.
+ dropBufferFrom(mSyncTransaction);
+ }
super.finishSync(outMergedTransaction, cancel);
}
@@ -5981,6 +5994,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
.notifyStartingWindowDrawn(mActivityRecord);
}
+ final boolean syncActive = mPrepareSyncSeqId > 0;
+ final boolean syncStillPending = syncActive && mPrepareSyncSeqId > syncSeqId;
+ if (syncStillPending && postDrawTransaction != null) {
+ ProtoLog.d(WM_DEBUG_SYNC_ENGINE, "Got a buffer for request id=%d but latest request is"
+ + " id=%d. Since the buffer is out-of-date, drop it. win=%s", syncSeqId,
+ mPrepareSyncSeqId, this);
+ // sync is waiting for a newer seqId, so this buffer is obsolete and can be dropped
+ // to free up the buffer queue.
+ dropBufferFrom(postDrawTransaction);
+ }
+
final boolean hasSyncHandlers = executeDrawHandlers(postDrawTransaction, syncSeqId);
boolean skipLayout = false;
@@ -5990,15 +6014,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (asyncRotationController != null
&& asyncRotationController.handleFinishDrawing(this, postDrawTransaction)) {
// Consume the transaction because the controller will apply it with fade animation.
- // Layout is not needed because the window will be hidden by the fade leash. Clear
- // sync state because its sync transaction doesn't need to be merged to sync group.
+ // Layout is not needed because the window will be hidden by the fade leash.
postDrawTransaction = null;
skipLayout = true;
- clearSyncState();
- } else if (onSyncFinishedDrawing() && postDrawTransaction != null) {
- mSyncTransaction.merge(postDrawTransaction);
- // Consume the transaction because the sync group will merge it.
- postDrawTransaction = null;
+ } else if (syncActive) {
+ if (!syncStillPending) {
+ onSyncFinishedDrawing();
+ }
+ if (postDrawTransaction != null) {
+ mSyncTransaction.merge(postDrawTransaction);
+ // Consume the transaction because the sync group will merge it.
+ postDrawTransaction = null;
+ }
}
final boolean layoutNeeded =
@@ -6012,7 +6039,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// We could be more subtle with Integer.MAX_VALUE and track a seqId in the timeout.
finishDrawing(null, Integer.MAX_VALUE);
mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
- if (!useBLASTSync()) return;
}
@Override
@@ -6049,6 +6075,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (mRedrawForSyncReported) {
return false;
}
+ // TODO(b/233286785): Remove mIsWallpaper once WallpaperService handles syncId of relayout.
+ if (mInRelayout && !mIsWallpaper) {
+ // The last sync seq id will return to the client, so there is no need to request the
+ // client to redraw.
+ return false;
+ }
return useBLASTSync();
}
@@ -6081,6 +6113,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* See {@link WindowState#mDrawHandlers}
*/
void applyWithNextDraw(Consumer<SurfaceControl.Transaction> consumer) {
+ if (mSyncState != SYNC_STATE_NONE) {
+ Slog.w(TAG, "applyWithNextDraw with mSyncState=" + mSyncState + ", " + this
+ + ", " + Debug.getCallers(8));
+ }
mSyncSeqId++;
mDrawHandlers.add(new DrawHandler(mSyncSeqId, consumer));
@@ -6219,4 +6255,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@WindowTraceLogLevel int logLevel) {
dumpDebug(proto, fieldId, logLevel);
}
+
+ public boolean cancelAndRedraw() {
+ // Cancel any draw requests during a sync.
+ return mPrepareSyncSeqId > 0;
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 5f43800bd9d5..4a5c4737cbbc 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -126,8 +126,10 @@ class WindowSurfaceController {
try {
transaction.hide(mSurfaceControl);
if (mAnimator.mIsWallpaper) {
+ final DisplayContent dc = mAnimator.mWin.getDisplayContent();
EventLog.writeEvent(EventLogTags.WM_WALLPAPER_SURFACE,
- mAnimator.mWin.getDisplayId(), 0 /* request hidden */);
+ dc.mDisplayId, 0 /* request hidden */,
+ String.valueOf(dc.mWallpaperController.getWallpaperTarget()));
}
} catch (RuntimeException e) {
Slog.w(TAG, "Exception hiding surface in " + this);
@@ -139,6 +141,12 @@ class WindowSurfaceController {
"Destroying surface %s called by %s", this, Debug.getCallers(8));
try {
if (mSurfaceControl != null) {
+ if (mAnimator.mIsWallpaper && !mAnimator.mWin.mWindowRemovalAllowed
+ && !mAnimator.mWin.mRemoveOnExit) {
+ // The wallpaper surface should have the same lifetime as its window.
+ Slog.e(TAG, "Unexpected removing wallpaper surface of " + mAnimator.mWin
+ + " by " + Debug.getCallers(8));
+ }
t.remove(mSurfaceControl);
}
} catch (RuntimeException e) {
@@ -260,8 +268,10 @@ class WindowSurfaceController {
setShown(true);
t.show(mSurfaceControl);
if (mAnimator.mIsWallpaper) {
+ final DisplayContent dc = mAnimator.mWin.getDisplayContent();
EventLog.writeEvent(EventLogTags.WM_WALLPAPER_SURFACE,
- mAnimator.mWin.getDisplayId(), 1 /* request shown */);
+ dc.mDisplayId, 1 /* request shown */,
+ String.valueOf(dc.mWallpaperController.getWallpaperTarget()));
}
return true;
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 18f60b1a9a20..bbb21f8122c5 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -18,15 +18,12 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
-import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
-import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.WINDOW_TOKEN;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -37,6 +34,7 @@ import static com.android.server.wm.WindowTokenProto.WAITING_TO_SHOW;
import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER;
import android.annotation.CallSuper;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -363,12 +361,8 @@ class WindowToken extends WindowContainer<WindowState> {
@Override
void assignLayer(SurfaceControl.Transaction t, int layer) {
- if (windowType == TYPE_DOCK_DIVIDER) {
- // See {@link DisplayContent#mSplitScreenDividerAnchor}
- super.assignRelativeLayer(t,
- mDisplayContent.getDefaultTaskDisplayArea().getSplitScreenDividerAnchor(), 1);
- } else if (mRoundedCornerOverlay) {
- super.assignLayer(t, WindowManagerPolicy.SCREEN_DECOR_DISPLAY_OVERLAY_LAYER);
+ if (mRoundedCornerOverlay) {
+ super.assignLayer(t, WindowManagerPolicy.COLOR_FADE_LAYER + 1);
} else {
super.assignLayer(t, layer);
}
@@ -378,7 +372,7 @@ class WindowToken extends WindowContainer<WindowState> {
SurfaceControl.Builder makeSurface() {
final SurfaceControl.Builder builder = super.makeSurface();
if (mRoundedCornerOverlay) {
- builder.setParent(getDisplayContent().getOverlayLayer());
+ builder.setParent(null);
}
return builder;
}
@@ -454,8 +448,14 @@ class WindowToken extends WindowContainer<WindowState> {
if (mFixedRotationTransformState != null) {
mFixedRotationTransformState.disassociate(this);
}
+ // TODO(b/233855302): Remove TaskFragment override if the DisplayContent uses the same
+ // bounds for screenLayout calculation.
+ final Configuration overrideConfig = new Configuration(config);
+ overrideConfig.screenLayout = TaskFragment.computeScreenLayoutOverride(
+ overrideConfig.screenLayout, overrideConfig.screenWidthDp,
+ overrideConfig.screenHeightDp);
mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames,
- new Configuration(config), mDisplayContent.getRotation());
+ overrideConfig, mDisplayContent.getRotation());
mFixedRotationTransformState.mAssociatedTokens.add(this);
mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames);
onFixedRotationStatePrepared();
@@ -505,7 +505,7 @@ class WindowToken extends WindowContainer<WindowState> {
for (int i = mFixedRotationTransformState.mAssociatedTokens.size() - 1; i >= 0; i--) {
final ActivityRecord r =
mFixedRotationTransformState.mAssociatedTokens.get(i).asActivityRecord();
- if (r != null && r.isAnimating(TRANSITION | PARENTS)) {
+ if (r != null && r.isInTransition()) {
return true;
}
}
@@ -569,13 +569,12 @@ class WindowToken extends WindowContainer<WindowState> {
* the same rotation.
*/
@Nullable
- SurfaceControl getOrCreateFixedRotationLeash() {
+ SurfaceControl getOrCreateFixedRotationLeash(@NonNull SurfaceControl.Transaction t) {
if (!mTransitionController.isShellTransitionsEnabled()) return null;
final int rotation = getRelativeDisplayRotation();
if (rotation == Surface.ROTATION_0) return mFixedRotationTransformLeash;
if (mFixedRotationTransformLeash != null) return mFixedRotationTransformLeash;
- final SurfaceControl.Transaction t = getSyncTransaction();
final SurfaceControl leash = makeSurface().setContainerLayer()
.setParent(getParentSurfaceControl())
.setName(getSurfaceControl() + " - rotation-leash")
@@ -591,6 +590,15 @@ class WindowToken extends WindowContainer<WindowState> {
return mFixedRotationTransformLeash;
}
+ /**
+ * @return the leash which represents this window as if it was non-rotated. Will be null if
+ * there isn't one.
+ */
+ @Nullable
+ SurfaceControl getFixedRotationLeash() {
+ return mFixedRotationTransformLeash;
+ }
+
void removeFixedRotationLeash() {
if (mFixedRotationTransformLeash == null) return;
final SurfaceControl.Transaction t = getSyncTransaction();
@@ -664,6 +672,15 @@ class WindowToken extends WindowContainer<WindowState> {
}
}
+ @Override
+ boolean prepareSync() {
+ if (mDisplayContent != null && mDisplayContent.isRotationChanging()
+ && AsyncRotationController.canBeAsync(this)) {
+ return false;
+ }
+ return super.prepareSync();
+ }
+
@CallSuper
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9d708add5ca5..06fb4b0a01d8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -12533,8 +12533,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.setContentIntent(locationSettingsIntent)
.setAutoCancel(true)
.build();
- mInjector.getNotificationManager().notify(SystemMessage.NOTE_LOCATION_CHANGED,
- notification);
+ mHandler.post(() -> mInjector.getNotificationManager().notify(
+ SystemMessage.NOTE_LOCATION_CHANGED, notification));
}
private String getLocationChangedTitle() {
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java b/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java
index dcffc9e73c0e..f041fbd7bf90 100644
--- a/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java
@@ -334,18 +334,7 @@ public class SmartspacePerUserService extends
@NonNull
private final SmartspaceConfig mSmartspaceConfig;
private final RemoteCallbackList<ISmartspaceCallback> mCallbacks =
- new RemoteCallbackList<ISmartspaceCallback>() {
- @Override
- public void onCallbackDied(ISmartspaceCallback callback) {
- if (DEBUG) {
- Slog.d(TAG, "Binder died for session Id=" + mSessionId
- + " and callback=" + callback.asBinder());
- }
- if (mCallbacks.getRegisteredCallbackCount() == 0) {
- destroy();
- }
- }
- };
+ new RemoteCallbackList<>();
SmartspaceSessionInfo(
@NonNull final SmartspaceSessionId id,
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 494246491e47..80de823a6a1b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -722,6 +722,25 @@ public class AlarmManagerServiceTest {
}
@Test
+ public void testAlarmBroadcastOption() throws Exception {
+ final long triggerTime = mNowElapsedTest + 5000;
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi);
+
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+
+ final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor =
+ ArgumentCaptor.forClass(PendingIntent.OnFinished.class);
+ final ArgumentCaptor<Bundle> optionsCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class),
+ onFinishedCaptor.capture(), any(Handler.class), isNull(),
+ optionsCaptor.capture());
+ assertTrue(optionsCaptor.getValue()
+ .getBoolean(BroadcastOptions.KEY_ALARM_BROADCAST, false));
+ }
+
+ @Test
public void testUpdateConstants() {
setDeviceConfigLong(KEY_MIN_FUTURITY, 5);
setDeviceConfigLong(KEY_MIN_INTERVAL, 10);
@@ -2338,10 +2357,15 @@ public class AlarmManagerServiceTest {
mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0,
alarmPi, null, null, null, alarmClock);
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mService).setImpl(eq(RTC_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L),
eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE | FLAG_WAKE_FROM_IDLE),
- isNull(), eq(alarmClock), eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), isNull(),
- eq(EXACT_ALLOW_REASON_COMPAT));
+ isNull(), eq(alarmClock), eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE),
+ bundleCaptor.capture(), eq(EXACT_ALLOW_REASON_COMPAT));
+
+ final BroadcastOptions idleOptions = new BroadcastOptions(bundleCaptor.getValue());
+ final int type = idleOptions.getTemporaryAppAllowlistType();
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type);
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 09565b42e515..d675b0aa4973 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -19,6 +19,7 @@ package com.android.server.app;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@@ -49,6 +50,7 @@ import android.content.res.XmlResourceParser;
import android.hardware.power.Mode;
import android.os.Bundle;
import android.os.PowerManagerInternal;
+import android.os.UserManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
@@ -69,6 +71,8 @@ import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
+import java.io.File;
+import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -79,7 +83,7 @@ import java.util.function.Supplier;
@Presubmit
public class GameManagerServiceTests {
@Mock MockContext mMockContext;
- private static final String TAG = "GameServiceTests";
+ private static final String TAG = "GameManagerServiceTests";
private static final String PACKAGE_NAME_INVALID = "com.android.app";
private static final int USER_ID_1 = 1001;
private static final int USER_ID_2 = 1002;
@@ -91,6 +95,8 @@ public class GameManagerServiceTests {
private PackageManager mMockPackageManager;
@Mock
private PowerManagerInternal mMockPowerManager;
+ @Mock
+ private UserManager mMockUserManager;
// Stolen from ConnectivityServiceTest.MockContext
class MockContext extends ContextWrapper {
@@ -150,6 +156,15 @@ public class GameManagerServiceTests {
public PackageManager getPackageManager() {
return mMockPackageManager;
}
+
+ @Override
+ public Object getSystemService(String name) {
+ switch (name) {
+ case Context.USER_SERVICE:
+ return mMockUserManager;
+ }
+ throw new UnsupportedOperationException("Couldn't find system service: " + name);
+ }
}
@Before
@@ -198,6 +213,19 @@ public class GameManagerServiceTests {
mTestLooper.dispatchAll();
}
+ private void switchUser(GameManagerService gameManagerService, int from, int to) {
+ UserInfo userInfoFrom = new UserInfo(from, "name", 0);
+ UserInfo userInfoTo = new UserInfo(to, "name", 0);
+ gameManagerService.onUserSwitching(/* from */ new SystemService.TargetUser(userInfoFrom),
+ /* to */ new SystemService.TargetUser(userInfoTo));
+ mTestLooper.dispatchAll();
+ }
+
+ private void mockManageUsersGranted() {
+ mMockContext.setPermission(Manifest.permission.MANAGE_USERS,
+ PackageManager.PERMISSION_GRANTED);
+ }
+
private void mockModifyGameModeGranted() {
mMockContext.setPermission(Manifest.permission.MANAGE_GAME_MODE,
PackageManager.PERMISSION_GRANTED);
@@ -819,6 +847,7 @@ public class GameManagerServiceTests {
GameManagerService gameManagerService = new GameManagerService(
mMockContext, mTestLooper.getLooper());
startUser(gameManagerService, USER_ID_1);
+
gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
GameManager.GAME_MODE_PERFORMANCE, "120", "0.3");
gameManagerService.setGameModeConfigOverride(mPackageName, USER_ID_1,
@@ -1246,4 +1275,133 @@ public class GameManagerServiceTests {
public void testSetGameStateNotLoading() {
setGameState(false);
}
+
+ private List<String> readGameModeInterventionList() throws Exception {
+ final File interventionFile = new File(InstrumentationRegistry.getContext().getFilesDir(),
+ "system/game_mode_intervention.list");
+ assertNotNull(interventionFile);
+ List<String> output = Files.readAllLines(interventionFile.toPath());
+ return output;
+ }
+
+ private void mockInterventionListForMultipleUsers() {
+ final String[] packageNames = new String[] {"com.android.app0",
+ "com.android.app1", "com.android.app2"};
+
+ final ApplicationInfo[] applicationInfos = new ApplicationInfo[3];
+ final PackageInfo[] pis = new PackageInfo[3];
+ for (int i = 0; i < 3; ++i) {
+ applicationInfos[i] = new ApplicationInfo();
+ applicationInfos[i].category = ApplicationInfo.CATEGORY_GAME;
+ applicationInfos[i].packageName = packageNames[i];
+
+ pis[i] = new PackageInfo();
+ pis[i].packageName = packageNames[i];
+ pis[i].applicationInfo = applicationInfos[i];
+ }
+
+ final List<PackageInfo> userOnePackages = new ArrayList<>();
+ final List<PackageInfo> userTwoPackages = new ArrayList<>();
+ userOnePackages.add(pis[1]);
+ userTwoPackages.add(pis[0]);
+ userTwoPackages.add(pis[2]);
+
+ final List<UserInfo> userInfos = new ArrayList<>(2);
+ userInfos.add(new UserInfo());
+ userInfos.add(new UserInfo());
+ userInfos.get(0).id = USER_ID_1;
+ userInfos.get(1).id = USER_ID_2;
+
+ when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), eq(USER_ID_1)))
+ .thenReturn(userOnePackages);
+ when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), eq(USER_ID_2)))
+ .thenReturn(userTwoPackages);
+ when(mMockUserManager.getUsers()).thenReturn(userInfos);
+ }
+
+ @Test
+ public void testVerifyInterventionList() throws Exception {
+ mockDeviceConfigAll();
+ mockInterventionListForMultipleUsers();
+ mockManageUsersGranted();
+ mockModifyGameModeGranted();
+ final Context context = InstrumentationRegistry.getContext();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext,
+ mTestLooper.getLooper(),
+ context.getFilesDir());
+ startUser(gameManagerService, USER_ID_1);
+ startUser(gameManagerService, USER_ID_2);
+
+ gameManagerService.setGameModeConfigOverride("com.android.app0", USER_ID_2,
+ GameManager.GAME_MODE_PERFORMANCE, "120", "0.6");
+ gameManagerService.setGameModeConfigOverride("com.android.app2", USER_ID_2,
+ GameManager.GAME_MODE_BATTERY, "60", "0.5");
+ mTestLooper.dispatchAll();
+
+ /* Expected fileOutput (order may vary)
+ com.android.app2 <UID> 0 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.5,fps=60
+ com.android.app1 <UID> 1 2 angle=0,scaling=0.5,fps=90 3 angle=0,scaling=0.7,fps=30
+ com.android.app0 <UID> 0 2 angle=0,scaling=0.6,fps=120 3 angle=0,scaling=0.7,fps=30
+
+ The current game mode would only be set to non-zero if the current user have that game
+ installed.
+ */
+
+ List<String> fileOutput = readGameModeInterventionList();
+ assertEquals(fileOutput.size(), 3);
+
+ String[] splitLine = fileOutput.get(0).split("\\s+");
+ assertEquals(splitLine[0], "com.android.app2");
+ assertEquals(splitLine[2], "3");
+ assertEquals(splitLine[3], "2");
+ assertEquals(splitLine[4], "angle=0,scaling=0.5,fps=90");
+ assertEquals(splitLine[5], "3");
+ assertEquals(splitLine[6], "angle=0,scaling=0.5,fps=60");
+ splitLine = fileOutput.get(1).split("\\s+");
+ assertEquals(splitLine[0], "com.android.app1");
+ assertEquals(splitLine[2], "0");
+ assertEquals(splitLine[3], "2");
+ assertEquals(splitLine[4], "angle=0,scaling=0.5,fps=90");
+ assertEquals(splitLine[5], "3");
+ assertEquals(splitLine[6], "angle=0,scaling=0.7,fps=30");
+ splitLine = fileOutput.get(2).split("\\s+");
+ assertEquals(splitLine[0], "com.android.app0");
+ assertEquals(splitLine[2], "2");
+ assertEquals(splitLine[3], "2");
+ assertEquals(splitLine[4], "angle=0,scaling=0.6,fps=120");
+ assertEquals(splitLine[5], "3");
+ assertEquals(splitLine[6], "angle=0,scaling=0.7,fps=30");
+
+ switchUser(gameManagerService, USER_ID_2, USER_ID_1);
+ gameManagerService.setGameMode("com.android.app1",
+ GameManager.GAME_MODE_BATTERY, USER_ID_1);
+ mTestLooper.dispatchAll();
+
+ fileOutput = readGameModeInterventionList();
+ assertEquals(fileOutput.size(), 3);
+
+ splitLine = fileOutput.get(0).split("\\s+");
+ assertEquals(splitLine[0], "com.android.app2");
+ assertEquals(splitLine[2], "0");
+ assertEquals(splitLine[3], "2");
+ assertEquals(splitLine[4], "angle=0,scaling=0.5,fps=90");
+ assertEquals(splitLine[5], "3");
+ assertEquals(splitLine[6], "angle=0,scaling=0.5,fps=60");
+ splitLine = fileOutput.get(1).split("\\s+");
+ assertEquals(splitLine[0], "com.android.app1");
+ assertEquals(splitLine[2], "3");
+ assertEquals(splitLine[3], "2");
+ assertEquals(splitLine[4], "angle=0,scaling=0.5,fps=90");
+ assertEquals(splitLine[5], "3");
+ assertEquals(splitLine[6], "angle=0,scaling=0.7,fps=30");
+ splitLine = fileOutput.get(2).split("\\s+");
+ assertEquals(splitLine[0], "com.android.app0");
+ assertEquals(splitLine[2], "0");
+ assertEquals(splitLine[3], "2");
+ assertEquals(splitLine[4], "angle=0,scaling=0.6,fps=120");
+ assertEquals(splitLine[5], "3");
+ assertEquals(splitLine[6], "angle=0,scaling=0.7,fps=30");
+
+ }
}
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 158bd39a4fd0..7092092fef25 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -50,5 +50,6 @@
<option name="package" value="com.android.frameworks.servicestests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
+ <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
</test>
</configuration>
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
index 91c45b4f8b60..adcbe6bb12c0 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -15,12 +15,18 @@
*/
package com.android.server.audio;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.app.AppOpsManager;
import android.content.Context;
+import android.media.AudioSystem;
import android.os.Looper;
import android.os.UserHandle;
import android.util.Log;
@@ -33,6 +39,7 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.Spy;
@MediumTest
@@ -46,6 +53,7 @@ public class AudioServiceTest {
private AudioSystemAdapter mAudioSystem;
@Spy private SystemServerAdapter mSpySystemServer;
private SettingsAdapter mSettingsAdapter;
+ @Mock private AppOpsManager mMockAppOpsManager;
// the class being unit-tested here
private AudioService mAudioService;
@@ -61,8 +69,11 @@ public class AudioServiceTest {
mAudioSystem = new NoOpAudioSystemAdapter();
mSpySystemServer = spy(new NoOpSystemServerAdapter());
mSettingsAdapter = new NoOpSettingsAdapter();
+ mMockAppOpsManager = mock(AppOpsManager.class);
+ when(mMockAppOpsManager.noteOp(anyInt(), anyInt(), anyString(), anyString(), anyString()))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
mAudioService = new AudioService(mContext, mAudioSystem, mSpySystemServer,
- mSettingsAdapter, null);
+ mSettingsAdapter, null, mMockAppOpsManager);
}
/**
@@ -113,4 +124,33 @@ public class AudioServiceTest {
reset(mSpySystemServer);
}
}
+
+ @Test
+ public void testRingNotifAlias() throws Exception {
+ Log.i(TAG, "running testRingNotifAlias");
+ Assert.assertNotNull(mAudioService);
+ // TODO add initialization message that can be caught here instead of sleeping
+ Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // wait for full AudioService initialization
+
+ // test with aliasing RING and NOTIFICATION
+ mAudioService.setNotifAliasRingForTest(true);
+ final int ringMaxVol = mAudioService.getStreamMaxVolume(AudioSystem.STREAM_RING);
+ final int ringMinVol = mAudioService.getStreamMinVolume(AudioSystem.STREAM_RING);
+ final int ringVol = ringMinVol + 1;
+ // set a value for NOTIFICATION so it's not at the target test value (ringMaxVol)
+ mAudioService.setStreamVolume(AudioSystem.STREAM_NOTIFICATION,
+ ringVol, 0, "bla");
+ mAudioService.setStreamVolume(AudioSystem.STREAM_RING, ringMaxVol, 0, "bla");
+ Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
+ Assert.assertEquals(ringMaxVol,
+ mAudioService.getStreamVolume(AudioSystem.STREAM_NOTIFICATION));
+
+ // test with no aliasing between RING and NOTIFICATION
+ mAudioService.setNotifAliasRingForTest(false);
+ mAudioService.setStreamVolume(AudioSystem.STREAM_RING, ringVol, 0, "bla");
+ mAudioService.setStreamVolume(AudioSystem.STREAM_NOTIFICATION, ringMaxVol, 0, "bla");
+ Assert.assertEquals(ringVol, mAudioService.getStreamVolume(AudioSystem.STREAM_RING));
+ Assert.assertEquals(ringMaxVol, mAudioService.getStreamVolume(
+ AudioSystem.STREAM_NOTIFICATION));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index f242fda15f06..c80547ce61c0 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -213,7 +213,7 @@ public class VirtualDeviceManagerServiceTest {
mContext.getSystemService(WindowManager.class), threadVerifier);
mAssociationInfo = new AssociationInfo(1, 0, null,
- MacAddress.BROADCAST_ADDRESS, "", null, true, false, 0, 0);
+ MacAddress.BROADCAST_ADDRESS, "", null, true, false, false, 0, 0);
VirtualDeviceParams params = new VirtualDeviceParams
.Builder()
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index fe3034dc569d..038cbc032375 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -76,12 +76,15 @@ public final class DeviceStateManagerServiceTest {
private TestDeviceStatePolicy mPolicy;
private TestDeviceStateProvider mProvider;
private DeviceStateManagerService mService;
+ private TestSystemPropertySetter mSysPropSetter;
@Before
public void setup() {
mProvider = new TestDeviceStateProvider();
mPolicy = new TestDeviceStatePolicy(mProvider);
- mService = new DeviceStateManagerService(InstrumentationRegistry.getContext(), mPolicy);
+ mSysPropSetter = new TestSystemPropertySetter();
+ mService = new DeviceStateManagerService(InstrumentationRegistry.getContext(), mPolicy,
+ mSysPropSetter);
// Necessary to allow us to check for top app process id in tests
mService.mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
@@ -107,6 +110,8 @@ public final class DeviceStateManagerServiceTest {
public void baseStateChanged() {
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
@@ -115,6 +120,8 @@ public final class DeviceStateManagerServiceTest {
flushHandler();
assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mSysPropSetter.getValue(),
+ OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
@@ -128,6 +135,8 @@ public final class DeviceStateManagerServiceTest {
flushHandler();
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
@@ -136,6 +145,8 @@ public final class DeviceStateManagerServiceTest {
flushHandler();
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
@@ -157,6 +168,8 @@ public final class DeviceStateManagerServiceTest {
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
@@ -170,6 +183,8 @@ public final class DeviceStateManagerServiceTest {
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
@@ -182,6 +197,8 @@ public final class DeviceStateManagerServiceTest {
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
OTHER_DEVICE_STATE);
@@ -193,6 +210,8 @@ public final class DeviceStateManagerServiceTest {
// supported.
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE);
@@ -211,6 +230,8 @@ public final class DeviceStateManagerServiceTest {
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
OTHER_DEVICE_STATE);
@@ -223,6 +244,8 @@ public final class DeviceStateManagerServiceTest {
// supported.
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
OTHER_DEVICE_STATE);
@@ -315,6 +338,8 @@ public final class DeviceStateManagerServiceTest {
TestDeviceStateManagerCallback.STATUS_ACTIVE);
// Committed state changes as there is a requested override.
assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
@@ -333,6 +358,8 @@ public final class DeviceStateManagerServiceTest {
TestDeviceStateManagerCallback.STATUS_CANCELED);
// Committed state is set back to the requested state once the override is cleared.
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertFalse(mService.getOverrideState().isPresent());
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
@@ -385,9 +412,10 @@ public final class DeviceStateManagerServiceTest {
// callback.
assertEquals(callback.getLastNotifiedStatus(secondRequestToken),
TestDeviceStateManagerCallback.STATUS_UNKNOWN);
-
assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.of(DEFAULT_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
@@ -397,6 +425,8 @@ public final class DeviceStateManagerServiceTest {
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
@@ -412,6 +442,8 @@ public final class DeviceStateManagerServiceTest {
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
@@ -458,6 +490,8 @@ public final class DeviceStateManagerServiceTest {
// Committed state changes as there is a requested override.
assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
@@ -471,6 +505,8 @@ public final class DeviceStateManagerServiceTest {
// Committed state is set back to the requested state once the override is cleared.
assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
assertFalse(mService.getOverrideState().isPresent());
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
@@ -495,6 +531,8 @@ public final class DeviceStateManagerServiceTest {
TestDeviceStateManagerCallback.STATUS_ACTIVE);
// Committed state changes as there is a requested override.
assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
@@ -509,6 +547,8 @@ public final class DeviceStateManagerServiceTest {
// Committed state is set back to the requested state as the override state is no longer
// supported.
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
assertFalse(mService.getOverrideState().isPresent());
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
@@ -673,4 +713,18 @@ public final class DeviceStateManagerServiceTest {
return mLastNotifiedStatus.getOrDefault(requestToken, STATUS_UNKNOWN);
}
}
+
+ private static final class TestSystemPropertySetter implements
+ DeviceStateManagerService.SystemPropertySetter {
+ private String mValue;
+
+ @Override
+ public void setDebugTracingDeviceStateProperty(String value) {
+ mValue = value;
+ }
+
+ public String getValue() {
+ return mValue;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
index 0ed90d27db99..6a6cd6c914a2 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -16,13 +16,11 @@
package com.android.server.display;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -34,17 +32,18 @@ import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.Message;
import android.os.PowerManager;
-import android.os.Temperature.ThrottlingStatus;
import android.os.Temperature;
+import android.os.Temperature.ThrottlingStatus;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.os.BackgroundThread;
import com.android.server.display.BrightnessThrottler.Injector;
-import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel;
import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData;
+import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel;
import org.junit.Before;
import org.junit.Test;
@@ -55,7 +54,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
@SmallTest
@@ -70,6 +68,8 @@ public class BrightnessThrottlerTest {
@Mock IThermalService mThermalServiceMock;
@Mock Injector mInjectorMock;
+ DisplayModeDirectorTest.FakeDeviceConfig mDeviceConfigFake;
+
@Captor ArgumentCaptor<IThermalEventListener> mThermalEventListenerCaptor;
@Before
@@ -83,6 +83,8 @@ public class BrightnessThrottlerTest {
return true;
}
});
+ mDeviceConfigFake = new DisplayModeDirectorTest.FakeDeviceConfig();
+ when(mInjectorMock.getDeviceConfig()).thenReturn(mDeviceConfigFake);
}
@@ -292,6 +294,170 @@ public class BrightnessThrottlerTest {
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
}
+ @Test public void testUpdateThrottlingData() throws Exception {
+ // Initialise brightness throttling levels
+ // Ensure that they are overridden by setting the data through device config.
+ final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
+ 0.25f);
+ List<ThrottlingLevel> levels = new ArrayList<>();
+ levels.add(level);
+ final BrightnessThrottlingData data = BrightnessThrottlingData.create(levels);
+ mDeviceConfigFake.setBrightnessThrottlingData("123,1,critical,0.4");
+ final BrightnessThrottler throttler = createThrottlerSupported(data);
+
+ verify(mThermalServiceMock).registerThermalEventListenerWithType(
+ mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
+ final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+
+ // Set status too low to trigger throttling
+ listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+
+ // Set status high enough to trigger throttling
+ listener.notifyThrottling(getSkinTemp(level.thermalStatus));
+ mTestLooper.dispatchAll();
+ assertEquals(0.4f, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+
+ // Update thresholds
+ // This data is equivalent to the string "123,1,critical,0.8", passed below
+ final ThrottlingLevel newLevel = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
+ 0.8f);
+ // Set new (valid) data from device config
+ mDeviceConfigFake.setBrightnessThrottlingData("123,1,critical,0.8");
+
+ // Set status too low to trigger throttling
+ listener.notifyThrottling(getSkinTemp(newLevel.thermalStatus - 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+
+ // Set status high enough to trigger throttling
+ listener.notifyThrottling(getSkinTemp(newLevel.thermalStatus));
+ mTestLooper.dispatchAll();
+ assertEquals(newLevel.brightness, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ }
+
+ @Test public void testInvalidThrottlingStrings() throws Exception {
+ // Initialise brightness throttling levels
+ // Ensure that they are not overridden by invalid data through device config.
+ final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
+ 0.25f);
+ List<ThrottlingLevel> levels = new ArrayList<>();
+ levels.add(level);
+ final BrightnessThrottlingData data = BrightnessThrottlingData.create(levels);
+ final BrightnessThrottler throttler = createThrottlerSupported(data);
+ verify(mThermalServiceMock).registerThermalEventListenerWithType(
+ mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
+ final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+
+ // None of these are valid so shouldn't override the original data
+ mDeviceConfigFake.setBrightnessThrottlingData("321,1,critical,0.4"); // Not the current id
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
+ mDeviceConfigFake.setBrightnessThrottlingData("123,0,critical,0.4"); // Incorrect number
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
+ mDeviceConfigFake.setBrightnessThrottlingData("123,2,critical,0.4"); // Incorrect number
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
+ mDeviceConfigFake.setBrightnessThrottlingData("123,1,invalid,0.4"); // Invalid level
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
+ mDeviceConfigFake.setBrightnessThrottlingData("123,1,critical,none"); // Invalid brightness
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
+ mDeviceConfigFake.setBrightnessThrottlingData("123,1,critical,-3"); // Invalid brightness
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
+ mDeviceConfigFake.setBrightnessThrottlingData("invalid string"); // Invalid format
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
+ mDeviceConfigFake.setBrightnessThrottlingData(""); // Invalid format
+ testThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
+ }
+
+ private void testThrottling(BrightnessThrottler throttler, IThermalEventListener listener,
+ float tooLowCap, float tooHighCap) throws Exception {
+ final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
+ tooHighCap);
+
+ // Set status too low to trigger throttling
+ listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
+ mTestLooper.dispatchAll();
+ assertEquals(tooLowCap, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+
+ // Set status high enough to trigger throttling
+ listener.notifyThrottling(getSkinTemp(level.thermalStatus));
+ mTestLooper.dispatchAll();
+ assertEquals(tooHighCap, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ }
+
+ @Test public void testMultipleConfigPoints() throws Exception {
+ // Initialise brightness throttling levels
+ final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
+ 0.25f);
+ List<ThrottlingLevel> levels = new ArrayList<>();
+ levels.add(level);
+ final BrightnessThrottlingData data = BrightnessThrottlingData.create(levels);
+
+ // These are identical to the string set below
+ final ThrottlingLevel levelSevere = new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE,
+ 0.9f);
+ final ThrottlingLevel levelCritical = new ThrottlingLevel(
+ PowerManager.THERMAL_STATUS_CRITICAL, 0.5f);
+ final ThrottlingLevel levelEmergency = new ThrottlingLevel(
+ PowerManager.THERMAL_STATUS_EMERGENCY, 0.1f);
+
+ mDeviceConfigFake.setBrightnessThrottlingData(
+ "123,3,severe,0.9,critical,0.5,emergency,0.1");
+ final BrightnessThrottler throttler = createThrottlerSupported(data);
+
+ verify(mThermalServiceMock).registerThermalEventListenerWithType(
+ mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
+ final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+
+ // Ensure that the multiple levels set via the string through the device config correctly
+ // override the original display device config ones.
+
+ // levelSevere
+ // Set status too low to trigger throttling
+ listener.notifyThrottling(getSkinTemp(levelSevere.thermalStatus - 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+
+ // Set status high enough to trigger throttling
+ listener.notifyThrottling(getSkinTemp(levelSevere.thermalStatus));
+ mTestLooper.dispatchAll();
+ assertEquals(0.9f, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+
+ // levelCritical
+ // Set status too low to trigger throttling
+ listener.notifyThrottling(getSkinTemp(levelCritical.thermalStatus - 1));
+ mTestLooper.dispatchAll();
+ assertEquals(0.9f, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+
+ // Set status high enough to trigger throttling
+ listener.notifyThrottling(getSkinTemp(levelCritical.thermalStatus));
+ mTestLooper.dispatchAll();
+ assertEquals(0.5f, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+
+ //levelEmergency
+ // Set status too low to trigger throttling
+ listener.notifyThrottling(getSkinTemp(levelEmergency.thermalStatus - 1));
+ mTestLooper.dispatchAll();
+ assertEquals(0.5f, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+
+ // Set status high enough to trigger throttling
+ listener.notifyThrottling(getSkinTemp(levelEmergency.thermalStatus));
+ mTestLooper.dispatchAll();
+ assertEquals(0.1f, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ }
+
private void assertThrottlingLevelsEquals(
List<ThrottlingLevel> expected,
List<ThrottlingLevel> actual) {
@@ -307,12 +473,13 @@ public class BrightnessThrottlerTest {
}
private BrightnessThrottler createThrottlerUnsupported() {
- return new BrightnessThrottler(mInjectorMock, mHandler, null, () -> {});
+ return new BrightnessThrottler(mInjectorMock, mHandler, mHandler, null, () -> {}, null);
}
private BrightnessThrottler createThrottlerSupported(BrightnessThrottlingData data) {
assertNotNull(data);
- return new BrightnessThrottler(mInjectorMock, mHandler, data, () -> {});
+ return new BrightnessThrottler(mInjectorMock, mHandler, BackgroundThread.getHandler(),
+ data, () -> {}, "123");
}
private Temperature getSkinTemp(@ThrottlingStatus int status) {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
new file mode 100644
index 000000000000..793930395daa
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.display;
+
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public final class DisplayDeviceConfigTest {
+ private DisplayDeviceConfig mDisplayDeviceConfig;
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private Resources mResources;
+
+ @Before
+ public void setUp() throws IOException {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getResources()).thenReturn(mResources);
+ mockDeviceConfigs();
+ try {
+ Path tempFile = Files.createTempFile("display_config", ".tmp");
+ Files.write(tempFile, getContent().getBytes(StandardCharsets.UTF_8));
+ mDisplayDeviceConfig = new DisplayDeviceConfig(mContext);
+ mDisplayDeviceConfig.initFromFile(tempFile.toFile());
+ } catch (IOException e) {
+ throw new IOException("Failed to setup the display device config.", e);
+ }
+ }
+
+ @Test
+ public void testConfigValues() {
+ assertEquals(mDisplayDeviceConfig.getAmbientHorizonLong(), 5000);
+ assertEquals(mDisplayDeviceConfig.getAmbientHorizonShort(), 50);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis(), 3000);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis(), 2000);
+ assertEquals(mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(), 10.0f, 0.0f);
+ assertEquals(mDisplayDeviceConfig.getAmbientLuxDarkeningMinThreshold(), 2.0f, 0.0f);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampFastDecrease(), 0.01f, 0.0f);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampFastIncrease(), 0.02f, 0.0f);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowIncrease(), 0.04f, 0.0f);
+ assertEquals(mDisplayDeviceConfig.getBrightnessRampSlowDecrease(), 0.03f, 0.0f);
+ assertEquals(mDisplayDeviceConfig.getBrightnessDefault(), 0.5f, 0.0f);
+ assertArrayEquals(mDisplayDeviceConfig.getBrightness(), new float[]{0.0f, 0.62f, 1.0f},
+ 0.0f);
+ assertArrayEquals(mDisplayDeviceConfig.getNits(), new float[]{2.0f, 500.0f, 800.0f}, 0.0f);
+ assertArrayEquals(mDisplayDeviceConfig.getBacklight(), new float[]{0.0f, 0.62f, 1.0f},
+ 0.0f);
+ assertEquals(mDisplayDeviceConfig.getScreenBrighteningMinThreshold(), 0.001, 0.000001f);
+ assertEquals(mDisplayDeviceConfig.getScreenDarkeningMinThreshold(), 0.002, 0.000001f);
+
+ // Todo(brup): Add asserts for BrightnessThrottlingData, DensityMapping,
+ // HighBrightnessModeData AmbientLightSensor, RefreshRateLimitations and ProximitySensor.
+ // Also add test for the case where optional display configs are null
+ }
+
+ private String getContent() {
+ return "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<displayConfiguration>\n"
+ + "<screenBrightnessMap>\n"
+ + "<point>\n"
+ + "<value>0.0</value>\n"
+ + "<nits>2.0</nits>\n"
+ + "</point>\n"
+ + "<point>\n"
+ + "<value>0.62</value>\n"
+ + "<nits>500.0</nits>\n"
+ + "</point>\n"
+ + "<point>\n"
+ + "<value>1.0</value>\n"
+ + "<nits>800.0</nits>\n"
+ + "</point>\n"
+ + "</screenBrightnessMap>\n"
+ + "<highBrightnessMode enabled=\"true\">\n"
+ + "<transitionPoint>0.62</transitionPoint>\n"
+ + "<minimumLux>10000</minimumLux>\n"
+ + "<timing>\n"
+ + "<!-- allow for 5 minutes out of every 30 minutes -->\n"
+ + "<timeWindowSecs>1800</timeWindowSecs>\n"
+ + "<timeMaxSecs>300</timeMaxSecs>\n"
+ + "<timeMinSecs>60</timeMinSecs>\n"
+ + "</timing>\n"
+ + "<refreshRate>\n"
+ + "<minimum>120</minimum>\n"
+ + "<maximum>120</maximum>\n"
+ + "</refreshRate>\n"
+ + "<thermalStatusLimit>light</thermalStatusLimit>\n"
+ + "<allowInLowPowerMode>false</allowInLowPowerMode>\n"
+ + "</highBrightnessMode>\n"
+ + "<ambientBrightnessChangeThresholds>\n"
+ + "<brighteningThresholds>\n"
+ + "<minimum>10</minimum>\n"
+ + "</brighteningThresholds>\n"
+ + "<darkeningThresholds>\n"
+ + "<minimum>2</minimum>\n"
+ + "</darkeningThresholds>\n"
+ + "</ambientBrightnessChangeThresholds>\n"
+ + "<screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease> "
+ + "<screenBrightnessRampFastIncrease>0.02</screenBrightnessRampFastIncrease> "
+ + "<screenBrightnessRampSlowDecrease>0.03</screenBrightnessRampSlowDecrease>"
+ + "<screenBrightnessRampSlowIncrease>0.04</screenBrightnessRampSlowIncrease>"
+ + "<screenBrightnessRampIncreaseMaxMillis>"
+ + "2000"
+ + "</screenBrightnessRampIncreaseMaxMillis>"
+ + "<screenBrightnessRampDecreaseMaxMillis>"
+ + "3000"
+ + "</screenBrightnessRampDecreaseMaxMillis>"
+ + "<ambientLightHorizonLong>5000</ambientLightHorizonLong>\n"
+ + "<ambientLightHorizonShort>50</ambientLightHorizonShort>\n"
+ + "<displayBrightnessChangeThresholds>"
+ + "<brighteningThresholds>"
+ + "<minimum>"
+ + "0.001"
+ + "</minimum>"
+ + "</brighteningThresholds>"
+ + "<darkeningThresholds>"
+ + "<minimum>"
+ + "0.002"
+ + "</minimum>"
+ + "</darkeningThresholds>"
+ + "</displayBrightnessChangeThresholds>"
+ + "<screenBrightnessRampIncreaseMaxMillis>"
+ + "2000"
+ + "</screenBrightnessRampIncreaseMaxMillis>\n"
+ + "<thermalThrottling>\n"
+ + "<brightnessThrottlingMap>\n"
+ + "<brightnessThrottlingPoint>\n"
+ + "<thermalStatus>emergency</thermalStatus>\n"
+ + "<!-- Throttling to 250 nits: (250-2.0)/(500-2.0)*(0.62-0.0)+0"
+ + ".0 = 0.30875502 -->\n"
+ + "<brightness>0.30875502</brightness>\n"
+ + "</brightnessThrottlingPoint>\n"
+ + "</brightnessThrottlingMap>\n"
+ + "</thermalThrottling>\n"
+ + "</displayConfiguration>\n";
+ }
+
+ private void mockDeviceConfigs() {
+ when(mResources.getFloat(com.android.internal.R.dimen
+ .config_screenBrightnessSettingDefaultFloat)).thenReturn(0.5f);
+ when(mResources.getFloat(com.android.internal.R.dimen
+ .config_screenBrightnessSettingMaximumFloat)).thenReturn(1.0f);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 5f1ff6be49be..1e97c1c5c5bc 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -287,7 +287,7 @@ public class DisplayManagerServiceTest {
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
- final int[] displayIds = bs.getDisplayIds(/* includeDisabled= */ false);
+ final int displayIds[] = bs.getDisplayIds();
final int size = displayIds.length;
assertTrue(size > 0);
@@ -297,9 +297,7 @@ public class DisplayManagerServiceTest {
);
for (int i = 0; i < size; i++) {
DisplayInfo info = bs.getDisplayInfo(displayIds[i]);
- if (info != null) {
- assertTrue(expectedDisplayTypeToViewPortTypeMapping.keySet().contains(info.type));
- }
+ assertTrue(expectedDisplayTypeToViewPortTypeMapping.keySet().contains(info.type));
}
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -1176,8 +1174,7 @@ public class DisplayManagerServiceTest {
DisplayManagerService.BinderService displayManagerBinderService,
FakeDisplayDevice displayDevice) {
- final int[] displayIds = displayManagerBinderService.getDisplayIds(
- /* includeDisabled= */ false);
+ final int[] displayIds = displayManagerBinderService.getDisplayIds();
assertTrue(displayIds.length > 0);
int displayId = Display.INVALID_DISPLAY;
for (int i = 0; i < displayIds.length; i++) {
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 864f31552ae3..968e1d8c546b 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -16,6 +16,7 @@
package com.android.server.display;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS;
@@ -1902,6 +1903,11 @@ public class DisplayModeDirectorTest {
KEY_REFRESH_RATE_IN_HBM_HDR, String.valueOf(fps));
}
+ void setBrightnessThrottlingData(String brightnessThrottlingData) {
+ putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ KEY_BRIGHTNESS_THROTTLING_DATA, brightnessThrottlingData);
+ }
+
void setLowDisplayBrightnessThresholds(int[] brightnessThresholds) {
String thresholds = toPropertyValue(brightnessThresholds);
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 5b131454e4db..cc68ba88f76e 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -33,7 +33,6 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -54,8 +53,6 @@ import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.display.layout.Layout;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -82,7 +79,6 @@ public class LogicalDisplayMapperTest {
private TestLooper mLooper;
private Handler mHandler;
private PowerManager mPowerManager;
- private DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy;
@Mock LogicalDisplayMapper.Listener mListenerMock;
@Mock Context mContextMock;
@@ -137,11 +133,8 @@ public class LogicalDisplayMapperTest {
mLooper = new TestLooper();
mHandler = new Handler(mLooper.getLooper());
-
- mDeviceStateToLayoutMapSpy = spy(new DeviceStateToLayoutMap());
mLogicalDisplayMapper = new LogicalDisplayMapper(mContextMock, mDisplayDeviceRepo,
- mListenerMock, new DisplayManagerService.SyncRoot(), mHandler,
- mDeviceStateToLayoutMapSpy);
+ mListenerMock, new DisplayManagerService.SyncRoot(), mHandler);
}
@@ -420,86 +413,6 @@ public class LogicalDisplayMapperTest {
/* isBootCompleted= */true));
}
- @Test public void testEnabledAndDisabledDisplays() {
- DisplayAddress displayAddressOne = new TestUtils.TestDisplayAddress();
- DisplayAddress displayAddressTwo = new TestUtils.TestDisplayAddress();
- DisplayAddress displayAddressThree = new TestUtils.TestDisplayAddress();
-
- TestDisplayDevice device1 = createDisplayDevice(displayAddressOne, Display.TYPE_INTERNAL,
- 600, 800,
- DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
- TestDisplayDevice device2 = createDisplayDevice(displayAddressTwo, Display.TYPE_INTERNAL,
- 200, 800,
- DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
- TestDisplayDevice device3 = createDisplayDevice(displayAddressThree, Display.TYPE_INTERNAL,
- 600, 900, DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
-
- Layout threeDevicesEnabledLayout = new Layout();
- threeDevicesEnabledLayout.createDisplayLocked(
- displayAddressOne,
- /* isDefault= */ true,
- /* isEnabled= */ true);
-
- threeDevicesEnabledLayout.createDisplayLocked(
- displayAddressTwo,
- /* isDefault= */ false,
- /* isEnabled= */ true);
- threeDevicesEnabledLayout.createDisplayLocked(
- displayAddressThree,
- /* isDefault= */ false,
- /* isEnabled= */ true);
-
- when(mDeviceStateToLayoutMapSpy.get(DeviceStateToLayoutMap.STATE_DEFAULT))
- .thenReturn(threeDevicesEnabledLayout);
-
- LogicalDisplay display1 = add(device1);
- LogicalDisplay display2 = add(device2);
- LogicalDisplay display3 = add(device3);
-
- // ensure 3 displays are returned
- int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID);
- assertEquals(3, ids.length);
- Arrays.sort(ids);
- assertEquals(DEFAULT_DISPLAY, ids[0]);
-
- Layout oneDeviceEnabledLayout = new Layout();
- oneDeviceEnabledLayout.createDisplayLocked(
- display1.getDisplayInfoLocked().address,
- /* isDefault= */ true,
- /* isEnabled= */ true);
- oneDeviceEnabledLayout.createDisplayLocked(
- display2.getDisplayInfoLocked().address,
- /* isDefault= */ false,
- /* isEnabled= */ false);
- oneDeviceEnabledLayout.createDisplayLocked(
- display3.getDisplayInfoLocked().address,
- /* isDefault= */ false,
- /* isEnabled= */ false);
-
- when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(oneDeviceEnabledLayout);
- when(mDeviceStateToLayoutMapSpy.get(1)).thenReturn(threeDevicesEnabledLayout);
-
- mLogicalDisplayMapper
- .setDeviceStateLocked(0, false);
- mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
- final int[] allDisplayIds = mLogicalDisplayMapper.getDisplayIdsLocked(
- Process.SYSTEM_UID, false);
- mLooper.dispatchAll();
-
- // ensure only one display is returned
- assertEquals(1, allDisplayIds.length);
-
- mLogicalDisplayMapper
- .setDeviceStateLocked(1, false);
- mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
- final int[] threeDisplaysEnabled = mLogicalDisplayMapper.getDisplayIdsLocked(
- Process.SYSTEM_UID, false);
- mLooper.dispatchAll();
-
- // ensure all three displays are returned
- assertEquals(3, threeDisplaysEnabled.length);
- }
-
/////////////////
// Helper Methods
/////////////////
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
index ece0a627f051..b0738fdb78d0 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -17,7 +17,11 @@
package com.android.server.display;
import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.PropertyInvalidatedCache;
@@ -45,18 +49,21 @@ public class LogicalDisplayTest {
private LogicalDisplay mLogicalDisplay;
private DisplayDevice mDisplayDevice;
+ private final DisplayDeviceInfo mDisplayDeviceInfo = new DisplayDeviceInfo();
@Before
public void setUp() {
// Share classloader to allow package private access.
System.setProperty("dexmaker.share_classloader", "true");
mDisplayDevice = mock(DisplayDevice.class);
- DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
- displayDeviceInfo.width = DISPLAY_WIDTH;
- displayDeviceInfo.height = DISPLAY_HEIGHT;
- displayDeviceInfo.flags = DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice);
- when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(displayDeviceInfo);
+
+ mDisplayDeviceInfo.copyFrom(new DisplayDeviceInfo());
+ mDisplayDeviceInfo.width = DISPLAY_WIDTH;
+ mDisplayDeviceInfo.height = DISPLAY_HEIGHT;
+ mDisplayDeviceInfo.flags = DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
+ mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
+ when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(mDisplayDeviceInfo);
// Disable binder caches in this process.
PropertyInvalidatedCache.disableForTestMode();
@@ -103,4 +110,33 @@ public class LogicalDisplayTest {
mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition());
}
+
+ @Test
+ public void testDisplayInputFlags() {
+ SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
+ verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT));
+ reset(t);
+
+ mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
+ mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
+ verify(t).setDisplayFlags(any(), eq(0));
+ reset(t);
+
+ mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL;
+ mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
+ verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT));
+ reset(t);
+
+ mLogicalDisplay.setPhase(LogicalDisplay.DISPLAY_PHASE_DISABLED);
+ mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
+ verify(t).setDisplayFlags(any(), eq(0));
+ reset(t);
+
+ mLogicalDisplay.setPhase(LogicalDisplay.DISPLAY_PHASE_ENABLED);
+ mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
+ mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
+ verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT));
+ reset(t);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
index 363c26b63bae..bbed1b60f8bf 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
@@ -16,11 +16,14 @@
package com.android.server.display.color;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -1130,6 +1133,15 @@ public class ColorDisplayServiceTest {
eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(), eq(Display.COLOR_MODE_INVALID));
}
+ @Test
+ public void getColorMode_noAvailableModes_returnsNotSet() {
+ when(mResourcesSpy.getIntArray(R.array.config_availableColorModes))
+ .thenReturn(new int[] {});
+ startService();
+ verify(mDisplayTransformManager, never()).setColorMode(anyInt(), any(), anyInt());
+ assertThat(mBinderService.getColorMode()).isEqualTo(-1);
+ }
+
/**
* Configures Night display to use a custom schedule.
*
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index c016406fc96a..308a4b67de24 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -928,6 +928,7 @@ public class AppStandbyControllerTests {
}
@Test
+ @FlakyTest(bugId = 185169504)
public void testNotificationEvent_quotaBump() throws Exception {
mInjector.mSettingsBuilder
.setBoolean("trigger_quota_bump_on_notification_seen", true);
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index 75bd2ccbe635..bc2c57e8bf84 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -22,6 +22,7 @@ import static junit.framework.TestCase.fail;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents.Event;
@@ -440,6 +441,7 @@ public class UsageStatsDatabaseTest {
prevDB.readMappingsLocked();
prevDB.init(1);
prevDB.putUsageStats(UsageStatsManager.INTERVAL_DAILY, mIntervalStats);
+ Set<String> prevDBApps = mIntervalStats.packageStats.keySet();
// Create a backup with a specific version
byte[] blob = prevDB.getBackupPayload(KEY_USAGE_STATS, version);
if (version >= 1 && version <= 3) {
@@ -447,6 +449,11 @@ public class UsageStatsDatabaseTest {
"UsageStatsDatabase shouldn't be able to write backups as XML");
return;
}
+ if (version < 1 || version > UsageStatsDatabase.BACKUP_VERSION) {
+ assertFalse(blob != null && blob.length != 0,
+ "UsageStatsDatabase shouldn't be able to write backups for unknown versions");
+ return;
+ }
clearUsageStatsFiles();
@@ -454,9 +461,11 @@ public class UsageStatsDatabaseTest {
newDB.readMappingsLocked();
newDB.init(1);
// Attempt to restore the usage stats from the backup
- newDB.applyRestoredPayload(KEY_USAGE_STATS, blob);
- List<IntervalStats> stats = newDB.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, 0, mEndTime,
- mIntervalStatsVerifier);
+ Set<String> restoredApps = newDB.applyRestoredPayload(KEY_USAGE_STATS, blob);
+ assertTrue(restoredApps.containsAll(prevDBApps),
+ "List of restored apps does not match list backed-up apps list.");
+ List<IntervalStats> stats = newDB.queryUsageStats(
+ UsageStatsManager.INTERVAL_DAILY, 0, mEndTime, mIntervalStatsVerifier);
if (version > UsageStatsDatabase.BACKUP_VERSION || version < 1) {
assertFalse(stats != null && !stats.isEmpty(),
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index c735bb7add0a..8a96febcd1e9 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -778,8 +778,7 @@ public class VibratorManagerServiceTest {
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
service, TEST_TIMEOUT_MILLIS));
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
- HAPTIC_FEEDBACK_ATTRS);
+ vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), HAPTIC_FEEDBACK_ATTRS);
// Wait before checking it never played a second effect.
assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
@@ -793,49 +792,78 @@ public class VibratorManagerServiceTest {
}
@Test
- public void vibrate_withOngoingAlarmVibration_ignoresEffect() throws Exception {
+ public void vibrate_withNewRepeatingVibration_cancelsOngoingEffect() throws Exception {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorManagerService service = createSystemReadyService();
VibrationEffect alarmEffect = VibrationEffect.createWaveform(
new long[]{10_000, 10_000}, new int[]{128, 255}, -1);
- vibrate(service, alarmEffect, new VibrationAttributes.Builder().setUsage(
- VibrationAttributes.USAGE_ALARM).build());
+ vibrate(service, alarmEffect, ALARM_ATTRS);
// VibrationThread will start this vibration async, so wait before checking it started.
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
service, TEST_TIMEOUT_MILLIS));
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
- HAPTIC_FEEDBACK_ATTRS);
+ VibrationEffect repeatingEffect = VibrationEffect.createWaveform(
+ new long[]{10_000, 10_000}, new int[]{128, 255}, 1);
+ vibrate(service, repeatingEffect, NOTIFICATION_ATTRS);
- // Wait before checking it never played a second effect.
- assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
- service, /* timeout= */ 50));
+ // VibrationThread will start this vibration async, so wait before checking it started.
+ assertTrue(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
+ service, TEST_TIMEOUT_MILLIS));
+
+ // The second vibration should have recorded that the vibrators were turned on.
+ verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong());
}
@Test
- public void vibrate_withOngoingRingtoneVibration_ignoresEffect() throws Exception {
+ public void vibrate_withOngoingHigherImportanceVibration_ignoresEffect() throws Exception {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorManagerService service = createSystemReadyService();
- VibrationEffect alarmEffect = VibrationEffect.createWaveform(
+ VibrationEffect effect = VibrationEffect.createWaveform(
new long[]{10_000, 10_000}, new int[]{128, 255}, -1);
- vibrate(service, alarmEffect, new VibrationAttributes.Builder().setUsage(
- VibrationAttributes.USAGE_RINGTONE).build());
+ vibrate(service, effect, ALARM_ATTRS);
// VibrationThread will start this vibration async, so wait before checking it started.
assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
service, TEST_TIMEOUT_MILLIS));
- vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
- HAPTIC_FEEDBACK_ATTRS);
+ vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
// Wait before checking it never played a second effect.
assertFalse(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
service, /* timeout= */ 50));
+
+ // The second vibration shouldn't have recorded that the vibrators were turned on.
+ verify(mBatteryStatsMock, times(1)).noteVibratorOn(anyInt(), anyLong());
+ }
+
+ @Test
+ public void vibrate_withOngoingLowerImportanceVibration_cancelsOngoingEffect()
+ throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ new long[]{10_000, 10_000}, new int[]{128, 255}, -1);
+ vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
+
+ // VibrationThread will start this vibration async, so wait before checking it started.
+ assertTrue(waitUntil(s -> !mVibratorProviders.get(1).getAllEffectSegments().isEmpty(),
+ service, TEST_TIMEOUT_MILLIS));
+
+ vibrate(service, effect, RINGTONE_ATTRS);
+
+ // VibrationThread will start this vibration async, so wait before checking it started.
+ assertTrue(waitUntil(s -> mVibratorProviders.get(1).getAllEffectSegments().size() > 1,
+ service, TEST_TIMEOUT_MILLIS));
+
+ // The second vibration should have recorded that the vibrators were turned on.
+ verify(mBatteryStatsMock, times(2)).noteVibratorOn(anyInt(), anyLong());
}
@Test
@@ -1052,15 +1080,15 @@ public class VibratorManagerServiceTest {
IVibrator.CAP_COMPOSE_EFFECTS);
VibratorManagerService service = createSystemReadyService();
- vibrate(service, CombinedVibration.startSequential()
- .addNext(1, VibrationEffect.createOneShot(100, 125))
- .combine(), NOTIFICATION_ATTRS);
- assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
- service, TEST_TIMEOUT_MILLIS));
-
vibrate(service, VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
.compose(), HAPTIC_FEEDBACK_ATTRS);
+ assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1,
+ service, TEST_TIMEOUT_MILLIS));
+
+ vibrate(service, CombinedVibration.startSequential()
+ .addNext(1, VibrationEffect.createOneShot(100, 125))
+ .combine(), NOTIFICATION_ATTRS);
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2,
service, TEST_TIMEOUT_MILLIS));
@@ -1070,25 +1098,25 @@ public class VibratorManagerServiceTest {
assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 3,
service, TEST_TIMEOUT_MILLIS));
+ // Ring vibrations have intensity OFF and are not played.
vibrate(service, VibrationEffect.createOneShot(100, 125), RINGTONE_ATTRS);
assertFalse(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() > 3,
- service, TEST_TIMEOUT_MILLIS));
+ service, /* timeout= */ 50));
+ // Only 3 effects played successfully.
assertEquals(3, fakeVibrator.getAllEffectSegments().size());
+ // Haptic feedback vibrations will be scaled with SCALE_LOW or none if default is low.
+ assertEquals(defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW,
+ 0.5 > ((PrimitiveSegment) fakeVibrator.getAllEffectSegments().get(0)).getScale());
+
// Notification vibrations will be scaled with SCALE_HIGH or none if default is high.
assertEquals(defaultNotificationIntensity < Vibrator.VIBRATION_INTENSITY_HIGH,
0.6 < fakeVibrator.getAmplitudes().get(0));
- // Haptic feedback vibrations will be scaled with SCALE_LOW or none if default is low.
- assertEquals(defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW,
- 0.5 > ((PrimitiveSegment) fakeVibrator.getAllEffectSegments().get(1)).getScale());
-
// Alarm vibration will be scaled with SCALE_NONE.
assertEquals(1f,
((PrimitiveSegment) fakeVibrator.getAllEffectSegments().get(2)).getScale(), 1e-5);
-
- // Ring vibrations have intensity OFF and are not played.
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
index c64ff9e128e6..3de65c19a1a4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
@@ -185,7 +185,7 @@ public class WindowOrientationListenerTest {
}
@Override
- public boolean isKeyguardLocked() {
+ public boolean isKeyguardShowingAndNotOccluded() {
return mIsScreenLocked;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 2f054b004d42..8ba9af0d85d0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -840,6 +840,156 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ public void reregisterService_checksAppIsApproved_pkg() throws Exception {
+ Context context = mock(Context.class);
+ PackageManager pm = mock(PackageManager.class);
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+ when(context.getPackageName()).thenReturn(mContext.getPackageName());
+ when(context.getUserId()).thenReturn(mContext.getUserId());
+ when(context.getPackageManager()).thenReturn(pm);
+ when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+ ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_PACKAGE);
+ ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+ when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ ServiceConnection sc = (ServiceConnection) args[1];
+ sc.onServiceConnected(cn, mock(IBinder.class));
+ return true;
+ });
+
+ service.addApprovedList("a", 0, true);
+
+ service.reregisterService(cn, 0);
+
+ assertTrue(service.isBound(cn, 0));
+ }
+
+ @Test
+ public void reregisterService_checksAppIsApproved_pkg_secondary() throws Exception {
+ Context context = mock(Context.class);
+ PackageManager pm = mock(PackageManager.class);
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+ when(context.getPackageName()).thenReturn(mContext.getPackageName());
+ when(context.getUserId()).thenReturn(mContext.getUserId());
+ when(context.getPackageManager()).thenReturn(pm);
+ when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+ ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_PACKAGE);
+ ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+ when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ ServiceConnection sc = (ServiceConnection) args[1];
+ sc.onServiceConnected(cn, mock(IBinder.class));
+ return true;
+ });
+
+ service.addApprovedList("a", 0, false);
+
+ service.reregisterService(cn, 0);
+
+ assertTrue(service.isBound(cn, 0));
+ }
+
+ @Test
+ public void reregisterService_checksAppIsApproved_cn() throws Exception {
+ Context context = mock(Context.class);
+ PackageManager pm = mock(PackageManager.class);
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+ when(context.getPackageName()).thenReturn(mContext.getPackageName());
+ when(context.getUserId()).thenReturn(mContext.getUserId());
+ when(context.getPackageManager()).thenReturn(pm);
+ when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+ ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+ ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+ when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ ServiceConnection sc = (ServiceConnection) args[1];
+ sc.onServiceConnected(cn, mock(IBinder.class));
+ return true;
+ });
+
+ service.addApprovedList("a/a", 0, true);
+
+ service.reregisterService(cn, 0);
+
+ assertTrue(service.isBound(cn, 0));
+ }
+
+ @Test
+ public void reregisterService_checksAppIsApproved_cn_secondary() throws Exception {
+ Context context = mock(Context.class);
+ PackageManager pm = mock(PackageManager.class);
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+ when(context.getPackageName()).thenReturn(mContext.getPackageName());
+ when(context.getUserId()).thenReturn(mContext.getUserId());
+ when(context.getPackageManager()).thenReturn(pm);
+ when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+ ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+ ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+ when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ ServiceConnection sc = (ServiceConnection) args[1];
+ sc.onServiceConnected(cn, mock(IBinder.class));
+ return true;
+ });
+
+ service.addApprovedList("a/a", 0, false);
+
+ service.reregisterService(cn, 0);
+
+ assertTrue(service.isBound(cn, 0));
+ }
+
+ @Test
+ public void reregisterService_checksAppIsNotApproved_cn_secondary() throws Exception {
+ Context context = mock(Context.class);
+ PackageManager pm = mock(PackageManager.class);
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+ when(context.getPackageName()).thenReturn(mContext.getPackageName());
+ when(context.getUserId()).thenReturn(mContext.getUserId());
+ when(context.getPackageManager()).thenReturn(pm);
+ when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+ ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_COMPONENT);
+ ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+ when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ ServiceConnection sc = (ServiceConnection) args[1];
+ sc.onServiceConnected(cn, mock(IBinder.class));
+ return true;
+ });
+
+ service.addApprovedList("b/b", 0, false);
+
+ service.reregisterService(cn, 0);
+
+ assertFalse(service.isBound(cn, 0));
+ }
+
+ @Test
public void unbindOtherUserServices() throws PackageManager.NameNotFoundException {
Context context = mock(Context.class);
PackageManager pm = mock(PackageManager.class);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index 76d4059eb436..c5131c8f8c1d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -28,6 +28,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.atLeast;
@@ -40,6 +41,9 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -49,6 +53,7 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.NotificationListenerFilter;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationRankingUpdate;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.testing.TestableContext;
@@ -60,6 +65,8 @@ import android.util.Xml;
import com.android.server.UiServiceTestCase;
+import com.google.common.collect.ImmutableList;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -397,6 +404,36 @@ public class NotificationListenersTest extends UiServiceTestCase {
}
@Test
+ public void testImplicitGrant() {
+ String pkg = "pkg";
+ int uid = 9;
+ NotificationChannel channel = new NotificationChannel("id", "name",
+ NotificationManager.IMPORTANCE_HIGH);
+ Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setTimeoutAfter(1);
+
+ StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(uid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, channel);
+
+ ManagedServices.ManagedServiceInfo info = mListeners.new ManagedServiceInfo(
+ null, new ComponentName("a", "a"), sbn.getUserId(), false, null, 33, 33);
+ List<ManagedServices.ManagedServiceInfo> services = ImmutableList.of(info);
+ when(mListeners.getServices()).thenReturn(services);
+
+ when(mNm.isVisibleToListener(any(), anyInt(), any())).thenReturn(true);
+ when(mNm.makeRankingUpdateLocked(info)).thenReturn(mock(NotificationRankingUpdate.class));
+ mNm.mPackageManagerInternal = mPmi;
+
+ mListeners.notifyPostedLocked(r, null);
+
+ verify(mPmi).grantImplicitAccess(sbn.getUserId(), null, UserHandle.getAppId(33),
+ sbn.getUid(), false, false);
+ }
+
+ @Test
public void testNotifyPostedLockedInLockdownMode() {
NotificationRecord r = mock(NotificationRecord.class);
NotificationRecord old = mock(NotificationRecord.class);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 22721a1bcc92..b1b323b734bd 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3380,39 +3380,98 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
- public void testSnoozeRunnable_reSnoozeASingleSnoozedNotification() throws Exception {
+ public void testSnoozeRunnable_tooManySnoozed_singleNotification() {
final NotificationRecord notification = generateNotificationRecord(
mTestNotificationChannel, 1, null, true);
mService.addNotification(notification);
- when(mSnoozeHelper.getNotification(any())).thenReturn(notification);
+
+ when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true);
+ when(mSnoozeHelper.canSnooze(1)).thenReturn(false);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
mService.new SnoozeNotificationRunnable(
- notification.getKey(), 100, null);
+ notification.getKey(), 100, null);
snoozeNotificationRunnable.run();
- NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable2 =
+
+ verify(mSnoozeHelper, never()).snooze(any(NotificationRecord.class), anyLong());
+ assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void testSnoozeRunnable_tooManySnoozed_singleGroupChildNotification() {
+ final NotificationRecord notification = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord notificationChild = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", false);
+ mService.addNotification(notification);
+ mService.addNotification(notificationChild);
+
+ when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true);
+ when(mSnoozeHelper.canSnooze(2)).thenReturn(false);
+
+ NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
+ mService.new SnoozeNotificationRunnable(
+ notificationChild.getKey(), 100, null);
+ snoozeNotificationRunnable.run();
+
+ verify(mSnoozeHelper, never()).snooze(any(NotificationRecord.class), anyLong());
+ assertThat(mService.getNotificationRecordCount()).isEqualTo(2);
+ }
+
+ @Test
+ public void testSnoozeRunnable_tooManySnoozed_summaryNotification() {
+ final NotificationRecord notification = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true);
+ final NotificationRecord notificationChild = generateNotificationRecord(
+ mTestNotificationChannel, 12, "group", false);
+ final NotificationRecord notificationChild2 = generateNotificationRecord(
+ mTestNotificationChannel, 13, "group", false);
+ mService.addNotification(notification);
+ mService.addNotification(notificationChild);
+ mService.addNotification(notificationChild2);
+
+ when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true);
+ when(mSnoozeHelper.canSnooze(3)).thenReturn(false);
+
+ NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
+ mService.new SnoozeNotificationRunnable(
+ notification.getKey(), 100, null);
+ snoozeNotificationRunnable.run();
+
+ verify(mSnoozeHelper, never()).snooze(any(NotificationRecord.class), anyLong());
+ assertThat(mService.getNotificationRecordCount()).isEqualTo(3);
+ }
+
+ @Test
+ public void testSnoozeRunnable_reSnoozeASingleSnoozedNotification() {
+ final NotificationRecord notification = generateNotificationRecord(
+ mTestNotificationChannel, 1, null, true);
+ mService.addNotification(notification);
+ when(mSnoozeHelper.getNotification(any())).thenReturn(notification);
+ when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true);
+
+ NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
mService.new SnoozeNotificationRunnable(
notification.getKey(), 100, null);
snoozeNotificationRunnable.run();
+ snoozeNotificationRunnable.run();
// snooze twice
verify(mSnoozeHelper, times(2)).snooze(any(NotificationRecord.class), anyLong());
}
@Test
- public void testSnoozeRunnable_reSnoozeASnoozedNotificationWithGroupKey() throws Exception {
+ public void testSnoozeRunnable_reSnoozeASnoozedNotificationWithGroupKey() {
final NotificationRecord notification = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
mService.addNotification(notification);
when(mSnoozeHelper.getNotification(any())).thenReturn(notification);
+ when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
mService.new SnoozeNotificationRunnable(
notification.getKey(), 100, null);
snoozeNotificationRunnable.run();
- NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable2 =
- mService.new SnoozeNotificationRunnable(
- notification.getKey(), 100, null);
snoozeNotificationRunnable.run();
// snooze twice
@@ -3430,6 +3489,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mSnoozeHelper.getNotification(any())).thenReturn(notification);
when(mSnoozeHelper.getNotifications(
anyString(), anyString(), anyInt())).thenReturn(new ArrayList<>());
+ when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
mService.new SnoozeNotificationRunnable(
@@ -3439,8 +3499,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.thenReturn(new ArrayList<>(Arrays.asList(notification, notification2)));
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable2 =
mService.new SnoozeNotificationRunnable(
- notification.getKey(), 100, null);
- snoozeNotificationRunnable.run();
+ notification2.getKey(), 100, null);
+ snoozeNotificationRunnable2.run();
// snooze twice
verify(mSnoozeHelper, times(4)).snooze(any(NotificationRecord.class), anyLong());
@@ -3454,6 +3514,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mTestNotificationChannel, 2, "group", false);
mService.addNotification(grouped);
mService.addNotification(nonGrouped);
+ when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
mService.new SnoozeNotificationRunnable(
@@ -3483,6 +3544,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.addNotification(parent);
mService.addNotification(child);
mService.addNotification(child2);
+ when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
mService.new SnoozeNotificationRunnable(
@@ -3504,6 +3566,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.addNotification(parent);
mService.addNotification(child);
mService.addNotification(child2);
+ when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
mService.new SnoozeNotificationRunnable(
@@ -3529,6 +3592,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mTestNotificationChannel, 2, "group", false);
mService.addNotification(parent);
mService.addNotification(child);
+ when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
mService.new SnoozeNotificationRunnable(
@@ -3556,6 +3620,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
mService.addNotification(child);
+ when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
mService.new SnoozeNotificationRunnable(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index 4c7e8433b15b..f2b1dc9132d5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -18,6 +18,7 @@ package com.android.server.notification;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -74,6 +75,8 @@ public class PermissionHelperTest extends UiServiceTestCase {
private PermissionHelper mPermissionHelper;
+ private static final int USER_FLAG_MASK = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -183,7 +186,8 @@ public class PermissionHelperTest extends UiServiceTestCase {
verify(mPermManager).grantRuntimePermission(
"pkg", Manifest.permission.POST_NOTIFICATIONS, 10);
verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
- FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, 10);
+ USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
+ FLAG_PERMISSION_USER_SET, true, 10);
}
@Test
@@ -201,7 +205,8 @@ public class PermissionHelperTest extends UiServiceTestCase {
verify(mPermManager).grantRuntimePermission(
"pkg", Manifest.permission.POST_NOTIFICATIONS, 10);
verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
- FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, 10);
+ USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
+ FLAG_PERMISSION_USER_SET, true, 10);
}
@Test
@@ -214,7 +219,8 @@ public class PermissionHelperTest extends UiServiceTestCase {
verify(mPermManager).revokeRuntimePermission(
eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString());
verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
- FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, 10);
+ USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT,
+ FLAG_PERMISSION_USER_SET, true, 10);
}
@Test
@@ -227,7 +233,7 @@ public class PermissionHelperTest extends UiServiceTestCase {
verify(mPermManager).grantRuntimePermission(
"pkg", Manifest.permission.POST_NOTIFICATIONS, 10);
verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
- 0, FLAG_PERMISSION_USER_SET, true, 10);
+ USER_FLAG_MASK, 0, true, 10);
}
@Test
@@ -240,7 +246,8 @@ public class PermissionHelperTest extends UiServiceTestCase {
verify(mPermManager).revokeRuntimePermission(
eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString());
verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS,
- 0, FLAG_PERMISSION_USER_SET, true, 10);
+ USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0,
+ true, 10);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 2ae2ef7162a5..7817e8176e76 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -15,10 +15,12 @@
*/
package com.android.server.notification;
+import static com.android.server.notification.SnoozeHelper.CONCURRENT_SNOOZE_LIMIT;
import static com.android.server.notification.SnoozeHelper.EXTRA_KEY;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
@@ -38,7 +40,6 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
@@ -49,7 +50,6 @@ import android.util.Xml;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.FastXmlSerializer;
import com.android.server.UiServiceTestCase;
import com.android.server.pm.PackageManagerService;
@@ -59,9 +59,7 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -197,18 +195,6 @@ public class SnoozeHelperTest extends UiServiceTestCase {
}
@Test
- public void testCleanupContextShouldRemovePersistedRecord() {
- NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
- mSnoozeHelper.snooze(r, "context");
- mSnoozeHelper.cleanupPersistedContext(r.getSbn().getKey());
- assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification(
- r.getUser().getIdentifier(),
- r.getSbn().getPackageName(),
- r.getSbn().getKey()
- ));
- }
-
- @Test
public void testReadNoneSnoozedNotification() throws XmlPullParserException,
IOException, InterruptedException {
NotificationRecord r = getNotificationRecord(
@@ -218,8 +204,9 @@ public class SnoozeHelperTest extends UiServiceTestCase {
assertEquals("should see a zero value for unsnoozed notification",
0L,
mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
- UserHandle.SYSTEM.getIdentifier(),
- "not_my_package", r.getKey()).longValue());
+ UserHandle.SYSTEM.getIdentifier(), "not_my_package",
+ getNotificationRecord("not_my_package", 1, "one",
+ UserHandle.SYSTEM).getKey()).longValue());
}
@Test
@@ -281,6 +268,22 @@ public class SnoozeHelperTest extends UiServiceTestCase {
}
@Test
+ public void testSnoozeLimit() {
+ for (int i = 0; i < CONCURRENT_SNOOZE_LIMIT; i++ ) {
+ NotificationRecord r = getNotificationRecord("pkg", i, i+"", UserHandle.SYSTEM);
+
+ assertTrue("cannot snooze record " + i, mSnoozeHelper.canSnooze(1));
+
+ if (i % 2 == 0) {
+ mSnoozeHelper.snooze(r, null);
+ } else {
+ mSnoozeHelper.snooze(r, 9000);
+ }
+ }
+ assertFalse(mSnoozeHelper.canSnooze(1));
+ }
+
+ @Test
public void testCancelByApp() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
@@ -575,7 +578,7 @@ public class SnoozeHelperTest extends UiServiceTestCase {
}
@Test
- public void testClearData() {
+ public void testClearData_userPackage() {
// snooze 2 from same package
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
@@ -599,17 +602,72 @@ public class SnoozeHelperTest extends UiServiceTestCase {
}
@Test
+ public void testClearData_user() {
+ // snooze 2 from same package
+ NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+ NotificationRecord r2 = getNotificationRecord("pkg2", 2, "two", UserHandle.SYSTEM);
+ NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
+ NotificationRecord r4 = getNotificationRecord("pkg", 2, "two", UserHandle.ALL);
+ mSnoozeHelper.snooze(r, 1000);
+ mSnoozeHelper.snooze(r2, 1000);
+ mSnoozeHelper.snooze(r3, "until");
+ mSnoozeHelper.snooze(r4, "until");
+
+ assertTrue(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
+ assertTrue(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
+ assertTrue(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
+ assertTrue(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_ALL, r4.getSbn().getPackageName(), r4.getKey()));
+
+ // clear data
+ mSnoozeHelper.clearData(UserHandle.USER_SYSTEM);
+
+ // nothing in USER_SYSTEM snoozed; alarms canceled
+ assertFalse(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
+ assertFalse(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
+ assertFalse(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
+ assertTrue(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r4.getSbn().getPackageName(), r4.getKey()));
+
+ assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+ r3.getUser().getIdentifier(), r3.getSbn().getPackageName(),
+ r3.getSbn().getKey()));
+ assertNotNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+ r4.getUser().getIdentifier(), r4.getSbn().getPackageName(),
+ r4.getSbn().getKey()));
+ assertEquals(0L, mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+ r.getUser().getIdentifier(), r.getSbn().getPackageName(),
+ r.getSbn().getKey()).longValue());
+ assertEquals(0L, mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+ r2.getUser().getIdentifier(), r2.getSbn().getPackageName(),
+ r2.getSbn().getKey()).longValue());
+
+ // 2 for initial timed-snoozes, once each for canceling the USER_SYSTEM snoozes
+ verify(mAm, times(5)).cancel(any(PendingIntent.class));
+ }
+
+ @Test
public void testClearData_otherRecordsUntouched() {
// 2 packages, 2 users
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+ NotificationRecord rb = getNotificationRecord("pkg", 1, "oneb", UserHandle.SYSTEM);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.ALL);
NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
mSnoozeHelper.snooze(r, 1000);
+ mSnoozeHelper.snooze(rb, "until");
mSnoozeHelper.snooze(r2, 1000);
mSnoozeHelper.snooze(r3, 1000);
assertTrue(mSnoozeHelper.isSnoozed(
UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, rb.getSbn().getPackageName(), rb.getKey()));
+ assertTrue(mSnoozeHelper.isSnoozed(
UserHandle.USER_ALL, r2.getSbn().getPackageName(), r2.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
@@ -619,12 +677,22 @@ public class SnoozeHelperTest extends UiServiceTestCase {
assertFalse(mSnoozeHelper.isSnoozed(
UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
+ assertFalse(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, rb.getSbn().getPackageName(), rb.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
UserHandle.USER_ALL, r2.getSbn().getPackageName(), r2.getKey()));
assertTrue(mSnoozeHelper.isSnoozed(
UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
+
+ assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+ rb.getUser().getIdentifier(), rb.getSbn().getPackageName(),
+ rb.getSbn().getKey()));
+ assertEquals(0L, mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+ r.getUser().getIdentifier(), r.getSbn().getPackageName(),
+ r.getSbn().getKey()).longValue());
+
// once for each initial snooze, once for canceling one snooze
- verify(mAm, times(4)).cancel(any(PendingIntent.class));
+ verify(mAm, times(5)).cancel(any(PendingIntent.class));
}
private NotificationRecord getNotificationRecord(String pkg, int id, String tag,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index fd1536c5c0f1..4550b56f6fd0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -1622,7 +1622,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ZenModeConfig.toScheduleConditionId(si),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule, "test");
+ // We need the package name to be something that's not "android" so there aren't any
+ // existing rules under that package.
+ String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
assertNotNull(id);
}
try {
@@ -1632,12 +1634,41 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
new ZenPolicy.Builder().build(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule, "test");
+ String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
fail("allowed too many rules to be created");
} catch (IllegalArgumentException e) {
// yay
}
+ }
+ @Test
+ public void testAddAutomaticZenRule_beyondSystemLimit_differentComponents() {
+ // Make sure the system limit is enforced per-package even with different component provider
+ // names.
+ for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) {
+ ScheduleInfo si = new ScheduleInfo();
+ si.startHour = i;
+ AutomaticZenRule zenRule = new AutomaticZenRule("name" + i,
+ null,
+ new ComponentName("android", "ScheduleConditionProvider" + i),
+ ZenModeConfig.toScheduleConditionId(si),
+ new ZenPolicy.Builder().build(),
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
+ assertNotNull(id);
+ }
+ try {
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName("android", "ScheduleConditionProviderFinal"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ new ZenPolicy.Builder().build(),
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
+ fail("allowed too many rules to be created");
+ } catch (IllegalArgumentException e) {
+ // yay
+ }
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 6fafa491d0ca..3f3d01a14f80 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -29,6 +29,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
@@ -47,7 +49,6 @@ import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.Process.NOBODY_UID;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsState.ITYPE_IME;
-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;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
@@ -182,6 +183,10 @@ public class ActivityRecordTests extends WindowTestsBase {
private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
+ private static final int ORIENTATION_CONFIG_CHANGES =
+ CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT | CONFIG_SCREEN_SIZE
+ | CONFIG_SMALLEST_SCREEN_SIZE;
+
@Before
public void setUp() throws Exception {
setBooted(mAtm);
@@ -487,7 +492,7 @@ public class ActivityRecordTests extends WindowTestsBase {
public void testSetRequestedOrientationUpdatesConfiguration() throws Exception {
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setCreateTask(true)
- .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
+ .setConfigChanges(ORIENTATION_CONFIG_CHANGES)
.build();
activity.setState(RESUMED, "Testing");
@@ -710,7 +715,7 @@ public class ActivityRecordTests extends WindowTestsBase {
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setCreateTask(true)
.setLaunchTaskBehind(true)
- .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
+ .setConfigChanges(ORIENTATION_CONFIG_CHANGES)
.build();
final Task task = activity.getTask();
activity.setState(STOPPED, "Testing");
@@ -1160,6 +1165,45 @@ public class ActivityRecordTests extends WindowTestsBase {
assertTrue(lastTransition.allReady());
}
+ @Test
+ public void testFinishActivityIfPossible_sendResultImmediately() {
+ // Create activity representing the source of the activity result.
+ final ComponentName sourceComponent = ComponentName.createRelative(
+ DEFAULT_COMPONENT_PACKAGE_NAME, ".SourceActivity");
+ final ComponentName targetComponent = ComponentName.createRelative(
+ sourceComponent.getPackageName(), ".TargetActivity");
+
+ final ActivityRecord sourceActivity = new ActivityBuilder(mWm.mAtmService)
+ .setComponent(sourceComponent)
+ .setLaunchMode(ActivityInfo.LAUNCH_SINGLE_INSTANCE)
+ .setCreateTask(true)
+ .build();
+ sourceActivity.finishing = false;
+ sourceActivity.setState(STOPPED, "test");
+
+ final ActivityRecord targetActivity = new ActivityBuilder(mWm.mAtmService)
+ .setComponent(targetComponent)
+ .setTargetActivity(sourceComponent.getClassName())
+ .setLaunchMode(ActivityInfo.LAUNCH_SINGLE_INSTANCE)
+ .setCreateTask(true)
+ .setOnTop(true)
+ .build();
+ targetActivity.finishing = false;
+ targetActivity.setState(RESUMED, "test");
+ targetActivity.resultTo = sourceActivity;
+ targetActivity.setForceSendResultForMediaProjection();
+
+ clearInvocations(mAtm.getLifecycleManager());
+
+ targetActivity.finishIfPossible(0, new Intent(), null, "test", false /* oomAdj */);
+
+ try {
+ verify(mAtm.getLifecycleManager(), atLeastOnce()).scheduleTransaction(
+ any(ClientTransaction.class));
+ } catch (RemoteException ignored) {
+ }
+ }
+
/**
* Verify that complete finish request for non-finishing activity is invalid.
*/
@@ -1786,13 +1830,16 @@ public class ActivityRecordTests extends WindowTestsBase {
public void testActivityOnCancelFixedRotationTransform() {
final ActivityRecord activity = createActivityWithTask();
final DisplayRotation displayRotation = activity.mDisplayContent.getDisplayRotation();
+ final RemoteDisplayChangeController remoteDisplayChangeController = activity
+ .mDisplayContent.mRemoteDisplayChangeController;
spyOn(displayRotation);
+ spyOn(remoteDisplayChangeController);
final DisplayContent display = activity.mDisplayContent;
final int originalRotation = display.getRotation();
// Make {@link DisplayContent#sendNewConfiguration} not apply rotation immediately.
- doReturn(true).when(displayRotation).isWaitingForRemoteRotation();
+ doReturn(true).when(remoteDisplayChangeController).isWaitingForRemoteDisplayChange();
doReturn((originalRotation + 1) % 4).when(displayRotation).rotationForOrientation(
anyInt() /* orientation */, anyInt() /* lastRotation */);
// Set to visible so the activity can freeze the screen.
@@ -1830,7 +1877,7 @@ public class ActivityRecordTests extends WindowTestsBase {
// Simulate the remote rotation has completed and the configuration doesn't change, then
// the rotated activity should also be restored by clearing the transform.
displayRotation.updateRotationUnchecked(true /* forceUpdate */);
- doReturn(false).when(displayRotation).isWaitingForRemoteRotation();
+ doReturn(false).when(remoteDisplayChangeController).isWaitingForRemoteDisplayChange();
clearInvocations(activity);
display.setFixedRotationLaunchingAppUnchecked(activity);
display.sendNewConfiguration();
@@ -1954,7 +2001,8 @@ public class ActivityRecordTests extends WindowTestsBase {
any() /* window */, any() /* attrs */,
anyInt() /* viewVisibility */, anyInt() /* displayId */,
any() /* requestedVisibilities */, any() /* outInputChannel */,
- any() /* outInsetsState */, any() /* outActiveControls */);
+ any() /* outInsetsState */, any() /* outActiveControls */,
+ any() /* outAttachedFrame */);
mAtm.mWindowManager.mStartingSurfaceController
.createTaskSnapshotSurface(activity, snapshot);
} catch (RemoteException ignored) {
@@ -2165,39 +2213,6 @@ public class ActivityRecordTests extends WindowTestsBase {
}
@Test
- public void testSupportsSplitScreenWindowingMode() {
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setCreateTask(true)
- .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
- .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
- .build();
-
- // Not allow non-resizable
- mAtm.mForceResizableActivities = false;
- mAtm.mSupportsNonResizableMultiWindow = -1;
- mAtm.mDevEnableNonResizableMultiWindow = false;
- assertFalse(activity.supportsSplitScreenWindowingMode());
-
- // Force resizable
- mAtm.mForceResizableActivities = true;
- mAtm.mSupportsNonResizableMultiWindow = -1;
- mAtm.mDevEnableNonResizableMultiWindow = false;
- assertTrue(activity.supportsSplitScreenWindowingMode());
-
- // Use development option to allow non-resizable
- mAtm.mForceResizableActivities = false;
- mAtm.mSupportsNonResizableMultiWindow = -1;
- mAtm.mDevEnableNonResizableMultiWindow = true;
- assertTrue(activity.supportsSplitScreenWindowingMode());
-
- // Always allow non-resizable
- mAtm.mForceResizableActivities = false;
- mAtm.mSupportsNonResizableMultiWindow = 1;
- mAtm.mDevEnableNonResizableMultiWindow = false;
- assertTrue(activity.supportsSplitScreenWindowingMode());
- }
-
- @Test
public void testSupportsFreeform() {
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setCreateTask(true)
@@ -2523,21 +2538,6 @@ public class ActivityRecordTests extends WindowTestsBase {
}
@Test
- public void testStuckExitingWindow() {
- final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
- "closingWindow");
- closingWindow.mAnimatingExit = true;
- closingWindow.mRemoveOnExit = true;
- closingWindow.mActivityRecord.commitVisibility(
- false /* visible */, true /* performLayout */);
-
- // We pretended that we were running an exit animation, but that should have been cleared up
- // by changing visibility of ActivityRecord
- closingWindow.removeIfPossible();
- assertTrue(closingWindow.mRemoved);
- }
-
- @Test
public void testSetOrientation() {
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
activity.setVisible(true);
@@ -3133,6 +3133,7 @@ public class ActivityRecordTests extends WindowTestsBase {
mDisplayContent.mOpeningApps.clear();
app.mActivityRecord.commitVisibility(false, false);
app.mActivityRecord.onWindowsGone();
+ mDisplayContent.computeImeTargetIfNeeded(app.mActivityRecord);
assertTrue(app.mActivityRecord.mLastImeShown);
assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 73e409abf0c1..1575336600b4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -192,7 +192,7 @@ public class ActivityStartInterceptorTest {
.thenReturn(PLATFORM_PACKAGE_NAME);
// THEN calling intercept returns true
- assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
// THEN the returned intent is the admin support intent
assertEquals(ADMIN_SUPPORT_INTENT, mInterceptor.mIntent);
@@ -203,7 +203,7 @@ public class ActivityStartInterceptorTest {
final String suspendingPackage = "com.test.suspending.package";
final SuspendDialogInfo dialogInfo = suspendPackage(suspendingPackage);
// THEN calling intercept returns true
- assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
// Check intent parameters
assertEquals(dialogInfo,
@@ -234,7 +234,7 @@ public class ActivityStartInterceptorTest {
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
.thenReturn(false);
- assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
assertTrue(BlockedAppActivity.createIntent(TEST_USER_ID, TEST_PACKAGE_NAME)
.filterEquals(mInterceptor.mIntent));
@@ -246,7 +246,7 @@ public class ActivityStartInterceptorTest {
when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
// THEN calling intercept returns true
- assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
// THEN the returned intent is the quiet mode intent
assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID)
@@ -260,7 +260,7 @@ public class ActivityStartInterceptorTest {
when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
// THEN calling intercept returns true
- assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
// THEN the returned intent is the quiet mode intent
assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID)
@@ -273,7 +273,7 @@ public class ActivityStartInterceptorTest {
when(mAmInternal.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
// THEN calling intercept returns true
- mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null);
+ mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null);
// THEN the returned intent is the quiet mode intent
assertTrue(CONFIRM_CREDENTIALS_INTENT.filterEquals(mInterceptor.mIntent));
@@ -286,7 +286,7 @@ public class ActivityStartInterceptorTest {
.thenReturn("This app is bad");
// THEN calling intercept returns true
- assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
// THEN the returned intent is the harmful app warning intent
assertEquals(HarmfulAppWarningActivity.class.getName(),
@@ -298,7 +298,7 @@ public class ActivityStartInterceptorTest {
// GIVEN that none of the interception conditions are met
// THEN calling intercept returns false
- assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
}
public void addMockInterceptorCallback(
@@ -323,7 +323,7 @@ public class ActivityStartInterceptorTest {
new Intent("android.test.foo"),
ActivityOptions.makeBasic().setLaunchDisplayId(3));
- assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
assertEquals("android.test.foo", mInterceptor.mIntent.getAction());
assertEquals(3, mInterceptor.mActivityOptions.getLaunchDisplayId());
}
@@ -332,7 +332,7 @@ public class ActivityStartInterceptorTest {
public void testInterceptionCallback_singleCallbackReturnsNull() {
addMockInterceptorCallback(null, null);
- assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
}
@Test
@@ -340,7 +340,7 @@ public class ActivityStartInterceptorTest {
addMockInterceptorCallback(null, null);
addMockInterceptorCallback(new Intent("android.test.second"), null);
- assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null));
assertEquals("android.test.second", mInterceptor.mIntent.getAction());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 4ca14ddbd96f..c78bc59612d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -802,7 +802,7 @@ public class ActivityStarterTests extends WindowTestsBase {
// Create adjacent tasks and put one activity under it
final Task parent = new TaskBuilder(mSupervisor).build();
final Task adjacentParent = new TaskBuilder(mSupervisor).build();
- parent.setAdjacentTaskFragment(adjacentParent, true);
+ parent.setAdjacentTaskFragment(adjacentParent);
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setParentTask(parent)
.setCreateTask(true).build();
@@ -1128,28 +1128,27 @@ public class ActivityStarterTests extends WindowTestsBase {
}
@Test
- public void testTargetStackInSplitScreen() {
+ public void testTargetTaskInSplitScreen() {
final ActivityStarter starter =
prepareStarter(FLAG_ACTIVITY_LAUNCH_ADJACENT, false /* mockGetRootTask */);
final ActivityRecord top = new ActivityBuilder(mAtm).setCreateTask(true).build();
final ActivityOptions options = ActivityOptions.makeBasic();
final ActivityRecord[] outActivity = new ActivityRecord[1];
- // Activity must not land on split-screen stack if currently not in split-screen mode.
+ // Activity must not land on split-screen task if currently not in split-screen mode.
starter.setActivityOptions(options.toBundle())
- .setReason("testWindowingModeOptionsLaunchAdjacent")
+ .setReason("testTargetTaskInSplitScreen")
.setOutActivity(outActivity).execute();
assertThat(outActivity[0].inMultiWindowMode()).isFalse();
- // Move activity to split-screen-primary stack and make sure it has the focus.
+ // Move activity to split-screen-primary task and make sure it has the focus.
TestSplitOrganizer splitOrg = new TestSplitOrganizer(mAtm, top.getDisplayContent());
top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM);
- top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent");
+ top.getRootTask().moveToFront("testTargetTaskInSplitScreen");
- // Activity must landed on split-screen-secondary when launch adjacent.
- starter.setActivityOptions(options.toBundle())
- .setReason("testWindowingModeOptionsLaunchAdjacent")
- .setOutActivity(outActivity).execute();
+ // Activity must land on split-screen-secondary when launch adjacent.
+ startActivityInner(starter, outActivity[0], top, options, null /* inTask */,
+ null /* taskFragment*/);
assertThat(outActivity[0].inMultiWindowMode()).isTrue();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index a8571906bb06..20b1120d7d3e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -21,7 +21,6 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
@@ -42,8 +41,8 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
@@ -60,7 +59,6 @@ import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.LocaleList;
-import android.os.PowerManager;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
@@ -136,7 +134,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
assertNull(transaction.getLifecycleStateRequest());
}
- @Test(expected = IllegalStateException.class)
+ @Test
public void testOnPictureInPictureRequested_cannotEnterPip() throws RemoteException {
final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
@@ -146,11 +144,16 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
mAtm.mActivityClientController.requestPictureInPictureMode(activity);
- // Check enter no transactions with enter pip requests are made.
- verify(lifecycleManager, times(0)).scheduleTransaction(any());
+ verify(lifecycleManager, atLeast(0))
+ .scheduleTransaction(mClientTransactionCaptor.capture());
+ final ClientTransaction transaction = mClientTransactionCaptor.getValue();
+ // Check that none are enter pip request items.
+ transaction.getCallbacks().forEach(clientTransactionItem -> {
+ assertFalse(clientTransactionItem instanceof EnterPipRequestedItem);
+ });
}
- @Test(expected = IllegalStateException.class)
+ @Test
public void testOnPictureInPictureRequested_alreadyInPIPMode() throws RemoteException {
final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity();
@@ -159,8 +162,13 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
mAtm.mActivityClientController.requestPictureInPictureMode(activity);
- // Check that no transactions with enter pip requests are made.
- verify(lifecycleManager, times(0)).scheduleTransaction(any());
+ verify(lifecycleManager, atLeast(0))
+ .scheduleTransaction(mClientTransactionCaptor.capture());
+ final ClientTransaction transaction = mClientTransactionCaptor.getValue();
+ // Check that none are enter pip request items.
+ transaction.getCallbacks().forEach(clientTransactionItem -> {
+ assertFalse(clientTransactionItem instanceof EnterPipRequestedItem);
+ });
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index 716612c70aef..75ecfd870eb2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -21,6 +21,7 @@ import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
@@ -34,6 +35,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -284,10 +286,14 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
.setCreateActivity(true).build().getTopMostActivity();
activity2.getTask().setResumedActivity(activity2, "test");
- mAtm.mAmInternal.deletePendingTopUid(activity1.getUid(), Long.MAX_VALUE);
+ final int[] pendingTopUid = new int[1];
+ doAnswer(invocation -> {
+ pendingTopUid[0] = invocation.getArgument(0);
+ return null;
+ }).when(mAtm.mAmInternal).addPendingTopUid(anyInt(), anyInt(), any());
clearInvocations(mAtm);
activity1.moveFocusableActivityToTop("test");
- assertTrue(mAtm.mAmInternal.isPendingTopUid(activity1.getUid()));
+ assertEquals(activity1.getUid(), pendingTopUid[0]);
verify(mAtm).updateOomAdj();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 890a5478602a..513791d2b8a5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -25,6 +26,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
@@ -44,6 +47,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
@@ -86,6 +90,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
@Before
public void setUp() throws Exception {
+ assumeFalse(WindowManagerService.sEnableShellTransitions);
mAppTransitionController = new AppTransitionController(mWm, mDisplayContent);
}
@@ -155,6 +160,32 @@ public class AppTransitionControllerTest extends WindowTestsBase {
}
@Test
+ public void testDreamActivityOpenTransition() {
+ final ActivityRecord dreamActivity = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM);
+ mDisplayContent.prepareAppTransition(TRANSIT_OPEN);
+ mDisplayContent.mOpeningApps.add(dreamActivity);
+
+ assertEquals(TRANSIT_OLD_DREAM_ACTIVITY_OPEN,
+ AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ mDisplayContent.mChangingContainers, null, null, false));
+ }
+
+ @Test
+ public void testDreamActivityCloseTransition() {
+ final ActivityRecord dreamActivity = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM);
+ mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
+ mDisplayContent.mClosingApps.add(dreamActivity);
+
+ assertEquals(TRANSIT_OLD_DREAM_ACTIVITY_CLOSE,
+ AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+ mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+ mDisplayContent.mChangingContainers, null, null, false));
+ }
+
+ @Test
public void testChangeIsNotOverwritten() {
final ActivityRecord behind = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
@@ -564,7 +595,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
.setCreatedByOrganizer(true);
final Task splitRoot1 = builder.build();
final Task splitRoot2 = builder.build();
- splitRoot1.setAdjacentTaskFragment(splitRoot2, false /* moveTogether */);
+ splitRoot1.setAdjacentTaskFragment(splitRoot2);
final ActivityRecord activity1 = createActivityRecordWithParentTask(splitRoot1);
activity1.setVisible(false);
activity1.mVisibleRequested = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 07923005c45e..1f07b20acc13 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -46,8 +47,10 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -77,10 +80,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFORM;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
-import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -123,8 +124,8 @@ import android.view.ContentRecordingSession;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
-import android.view.IDisplayWindowRotationCallback;
-import android.view.IDisplayWindowRotationController;
+import android.view.IDisplayChangeWindowCallback;
+import android.view.IDisplayChangeWindowController;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindowManager;
import android.view.InsetsState;
@@ -135,6 +136,7 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.View;
import android.view.WindowManager;
+import android.window.DisplayAreaInfo;
import android.window.IDisplayAreaOrganizer;
import android.window.WindowContainerToken;
@@ -641,6 +643,7 @@ public class DisplayContentTests extends WindowTestsBase {
final DisplayContent dc = mDisplayContent;
final WindowState ws = createWindow(null, TYPE_APPLICATION, dc, "app window");
dc.setImeLayeringTarget(ws);
+ dc.setImeInputTarget(ws);
// Adjust bounds so that matchesRootDisplayAreaBounds() returns false.
final Rect bounds = new Rect(dc.getBounds());
@@ -1097,6 +1100,25 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
+ public void testOrientationBehind() {
+ final ActivityRecord prev = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setScreenOrientation(getRotatedOrientation(mDisplayContent)).build();
+ prev.mVisibleRequested = false;
+ final ActivityRecord top = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setScreenOrientation(SCREEN_ORIENTATION_BEHIND).build();
+ assertNotEquals(WindowConfiguration.ROTATION_UNDEFINED,
+ mDisplayContent.rotationForActivityInDifferentOrientation(top));
+
+ mDisplayContent.requestTransitionAndLegacyPrepare(WindowManager.TRANSIT_OPEN, 0);
+ top.setVisibility(true);
+ mDisplayContent.updateOrientation();
+ // The top uses "behind", so the orientation is decided by the previous.
+ assertEquals(prev, mDisplayContent.getLastOrientationSource());
+ // The top will use the rotation from "prev" with fixed rotation.
+ assertTrue(top.hasFixedRotationTransform());
+ }
+
+ @Test
public void testFixedToUserRotationChanged() {
final DisplayContent dc = createNewDisplay();
dc.getDisplayRotation().setFixedToUserRotation(
@@ -1133,6 +1155,7 @@ public class DisplayContentTests extends WindowTestsBase {
dc.setImeLayeringTarget(createWindow(null, TYPE_STATUS_BAR, "app"));
dc.getImeTarget(IME_TARGET_LAYERING).getWindow().setWindowingMode(
WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
+ dc.setImeInputTarget(dc.getImeTarget(IME_TARGET_LAYERING).getWindow());
assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
}
@@ -1151,6 +1174,7 @@ public class DisplayContentTests extends WindowTestsBase {
public void testComputeImeParent_noApp() throws Exception {
final DisplayContent dc = createNewDisplay();
dc.setImeLayeringTarget(createWindow(null, TYPE_STATUS_BAR, "statusBar"));
+ dc.setImeInputTarget(dc.getImeTarget(IME_TARGET_LAYERING).getWindow());
assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
}
@@ -1380,6 +1404,28 @@ public class DisplayContentTests extends WindowTestsBase {
win.setHasSurface(false);
}
+ @Test
+ public void testCalculateSystemGestureExclusion_unrestricted() throws Exception {
+ mWm.mConstants.mSystemGestureExcludedByPreQStickyImmersive = true;
+
+ final DisplayContent dc = createNewDisplay();
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
+ win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+ win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ win.getAttrs().privateFlags |= PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION;
+ win.setSystemGestureExclusion(Collections.singletonList(dc.getBounds()));
+
+ performLayout(dc);
+
+ win.setHasSurface(true);
+
+ final Region expected = Region.obtain();
+ expected.set(dc.getBounds());
+ assertEquals(expected, calculateSystemGestureExclusion(dc));
+
+ win.setHasSurface(false);
+ }
+
@UseTestDisplay(addWindows = { W_ABOVE_ACTIVITY, W_ACTIVITY})
@Test
public void testRequestResizeForEmptyFrames() {
@@ -1593,14 +1639,14 @@ public class DisplayContentTests extends WindowTestsBase {
final Task task = app.getTask();
final ActivityRecord app2 = new ActivityBuilder(mWm.mAtmService).setTask(task).build();
mDisplayContent.setFixedRotationLaunchingApp(app2, (mDisplayContent.getRotation() + 1) % 4);
- doReturn(true).when(task).isAppTransitioning();
- // If the task is animating transition, this should be no-op.
+ doReturn(true).when(app).isInTransition();
+ // If the task contains a transition, this should be no-op.
mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
assertTrue(app2.hasFixedRotationTransform());
assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
- doReturn(false).when(task).isAppTransitioning();
+ doReturn(false).when(app).isInTransition();
// Although this notifies app instead of app2 that uses the fixed rotation, app2 should
// still finish the transform because there is no more transition event.
mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
@@ -1677,6 +1723,13 @@ public class DisplayContentTests extends WindowTestsBase {
assertFalse(displayContent.mPinnedTaskController.isFreezingTaskConfig(pinnedTask));
assertEquals(pinnedActivity.getConfiguration().orientation,
displayContent.getConfiguration().orientation);
+
+ // No need to apply rotation if the display ignores orientation request.
+ doCallRealMethod().when(displayContent).rotationForActivityInDifferentOrientation(any());
+ pinnedActivity.mOrientation = SCREEN_ORIENTATION_LANDSCAPE;
+ displayContent.setIgnoreOrientationRequest(true);
+ assertEquals(WindowConfiguration.ROTATION_UNDEFINED,
+ displayContent.rotationForActivityInDifferentOrientation(pinnedActivity));
}
@Test
@@ -1693,8 +1746,7 @@ public class DisplayContentTests extends WindowTestsBase {
// The condition should reject using fixed rotation because the resumed client in real case
// might get display info immediately. And the fixed rotation adjustments haven't arrived
// client side so the info may be inconsistent with the requested orientation.
- verify(mDisplayContent).handleTopActivityLaunchingInDifferentOrientation(eq(app),
- eq(true) /* checkOpening */);
+ verify(mDisplayContent).updateOrientation(eq(app), anyBoolean());
assertFalse(app.isFixedRotationTransforming());
assertFalse(mDisplayContent.hasTopFixedRotationLaunchingApp());
}
@@ -1782,15 +1834,16 @@ public class DisplayContentTests extends WindowTestsBase {
return true;
}).when(dc).updateDisplayOverrideConfigurationLocked();
final boolean[] called = new boolean[1];
- mWm.mDisplayRotationController =
- new IDisplayWindowRotationController.Stub() {
+ mWm.mDisplayChangeController =
+ new IDisplayChangeWindowController.Stub() {
@Override
- public void onRotateDisplay(int displayId, int fromRotation, int toRotation,
- IDisplayWindowRotationCallback callback) {
+ public void onDisplayChange(int displayId, int fromRotation, int toRotation,
+ DisplayAreaInfo newDisplayAreaInfo,
+ IDisplayChangeWindowCallback callback) throws RemoteException {
called[0] = true;
try {
- callback.continueRotateDisplay(toRotation, null);
+ callback.continueDisplayChange(null);
} catch (RemoteException e) {
assertTrue(false);
}
@@ -1824,13 +1877,14 @@ public class DisplayContentTests extends WindowTestsBase {
// Rotate 180 degree so the display doesn't have configuration change. This condition is
// used for the later verification of stop-freezing (without setting mWaitingForConfig).
doReturn((dr.getRotation() + 2) % 4).when(dr).rotationForOrientation(anyInt(), anyInt());
- mWm.mDisplayRotationController =
- new IDisplayWindowRotationController.Stub() {
+ mWm.mDisplayChangeController =
+ new IDisplayChangeWindowController.Stub() {
@Override
- public void onRotateDisplay(int displayId, int fromRotation, int toRotation,
- IDisplayWindowRotationCallback callback) {
+ public void onDisplayChange(int displayId, int fromRotation, int toRotation,
+ DisplayAreaInfo newDisplayAreaInfo,
+ IDisplayChangeWindowCallback callback) throws RemoteException {
try {
- callback.continueRotateDisplay(toRotation, null);
+ callback.continueDisplayChange(null);
} catch (RemoteException e) {
assertTrue(false);
}
@@ -2088,7 +2142,6 @@ public class DisplayContentTests extends WindowTestsBase {
final WindowState appWin2 = createWindow(null, TYPE_BASE_APPLICATION, act2, "appWin2");
appWin2.setHasSurface(true);
assertTrue(appWin2.canBeImeTarget());
- doReturn(true).when(appWin1).isClosing();
doReturn(true).when(appWin1).inTransitionSelfOrParent();
// Test step 3: Verify appWin2 will be the next IME target and the IME snapshot surface will
@@ -2193,23 +2246,28 @@ public class DisplayContentTests extends WindowTestsBase {
*/
@Test
public void testCreateTestDisplayContentFromDimensions() {
- final int displayWidth = 1000;
- final int displayHeight = 2000;
+ final int displayWidth = 540;
+ final int displayHeight = 960;
+ final int density = 192;
+ final int expectedWidthDp = 450; // = 540/(192/160)
+ final int expectedHeightDp = 800; // = 960/(192/160)
final int windowingMode = WINDOWING_MODE_FULLSCREEN;
final boolean ignoreOrientationRequests = false;
final float fixedOrientationLetterboxRatio = 0;
final DisplayContent testDisplayContent = new TestDisplayContent.Builder(mAtm, displayWidth,
- displayHeight).build();
+ displayHeight).setDensityDpi(density).build();
// test display info
final DisplayInfo di = testDisplayContent.getDisplayInfo();
assertEquals(displayWidth, di.logicalWidth);
assertEquals(displayHeight, di.logicalHeight);
- assertEquals(TestDisplayContent.DEFAULT_LOGICAL_DISPLAY_DENSITY, di.logicalDensityDpi);
+ assertEquals(density, di.logicalDensityDpi);
// test configuration
- final WindowConfiguration windowConfig = testDisplayContent.getConfiguration()
- .windowConfiguration;
+ final Configuration config = testDisplayContent.getConfiguration();
+ assertEquals(expectedWidthDp, config.screenWidthDp);
+ assertEquals(expectedHeightDp, config.screenHeightDp);
+ final WindowConfiguration windowConfig = config.windowConfiguration;
assertEquals(displayWidth, windowConfig.getBounds().width());
assertEquals(displayHeight, windowConfig.getBounds().height());
assertEquals(windowingMode, windowConfig.getWindowingMode());
@@ -2396,7 +2454,7 @@ public class DisplayContentTests extends WindowTestsBase {
@Test
public void testImeMenuDialogFocusWhenImeLayeringTargetChanges() {
final WindowState imeMenuDialog =
- createWindow(mImeWindow, TYPE_INPUT_METHOD_DIALOG, "imeMenuDialog");
+ createWindow(null, TYPE_INPUT_METHOD_DIALOG, "imeMenuDialog");
makeWindowVisibleAndDrawn(imeMenuDialog, mImeWindow);
assertTrue(imeMenuDialog.canReceiveKeys());
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
@@ -2409,13 +2467,11 @@ public class DisplayContentTests extends WindowTestsBase {
doReturn(true).when(imeAppTarget).getRequestedVisibility(ITYPE_IME);
assertEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
- // Verify imeMenuDialog doesn't be focused window if the next IME target does not
- // request IME visible.
+ // Verify imeMenuDialog doesn't be focused window if the next IME target is closing.
final WindowState nextImeAppTarget =
createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "nextImeAppTarget");
- spyOn(nextImeAppTarget);
- doReturn(true).when(nextImeAppTarget).isAnimating(PARENTS | TRANSITION,
- ANIMATION_TYPE_APP_TRANSITION);
+ makeWindowVisibleAndDrawn(nextImeAppTarget);
+ nextImeAppTarget.mActivityRecord.commitVisibility(false, false);
mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
assertNotEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 45ae81a71c44..34575ae2fe46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -43,6 +43,7 @@ import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
import android.view.DisplayInfo;
+import android.view.InsetsFrameProvider;
import android.view.InsetsState;
import android.view.PrivacyIndicatorBounds;
import android.view.RoundedCorners;
@@ -153,7 +154,10 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one.
WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
- win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES};
+ win.mAttrs.providedInsets = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(ITYPE_STATUS_BAR),
+ new InsetsFrameProvider(ITYPE_TOP_GESTURES)
+ };
win.getFrame().set(0, 0, 500, 100);
addWindow(win);
@@ -182,7 +186,9 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
public void addingWindow_InWindowTypeWithPredefinedInsets() {
mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one.
WindowState win = createWindow(null, TYPE_STATUS_BAR, "StatusBar");
- win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR};
+ win.mAttrs.providedInsets = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(ITYPE_STATUS_BAR)
+ };
win.getFrame().set(0, 0, 500, 100);
addWindow(win);
@@ -199,12 +205,19 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void addingWindow_throwsException_WithMultipleInsetTypes() {
WindowState win1 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
- win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
+ win1.mAttrs.providedInsets = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(ITYPE_STATUS_BAR),
+ new InsetsFrameProvider(ITYPE_NAVIGATION_BAR)
+ };
expectThrows(IllegalArgumentException.class, () -> addWindow(win1));
WindowState win2 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
- win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR, ITYPE_EXTRA_NAVIGATION_BAR};
+
+ win2.mAttrs.providedInsets = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(ITYPE_CLIMATE_BAR),
+ new InsetsFrameProvider(ITYPE_EXTRA_NAVIGATION_BAR)
+ };
expectThrows(IllegalArgumentException.class, () -> addWindow(win2));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
index dbb7fae548b7..2956c14155b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
@@ -168,7 +168,8 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
- prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT);
+ prepareLimitedBounds(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT,
+ false /* isUnresizable */);
final Rect dagBounds = new Rect(mFirstRoot.getBounds());
final Rect taskBounds = new Rect(mFirstTask.getBounds());
final Rect activityBounds = new Rect(mFirstActivity.getBounds());
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index ffa21fadff6b..6c161cf088f7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -27,6 +27,7 @@ import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -44,12 +45,15 @@ import static org.mockito.Mockito.verify;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.InsetsVisibilities;
import androidx.test.filters.SmallTest;
+import com.android.internal.util.function.TriConsumer;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -269,15 +273,18 @@ public class InsetsStateControllerTest extends WindowTestsBase {
@Test
public void testImeForDispatch() {
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
- final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+ final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
// IME cannot be the IME target.
ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
WindowContainerInsetsSourceProvider statusBarProvider =
getController().getSourceProvider(ITYPE_STATUS_BAR);
- statusBarProvider.setWindowContainer(statusBar, null, ((displayFrames, windowState, rect) ->
+ final SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>> imeOverrideProviders =
+ new SparseArray<>();
+ imeOverrideProviders.put(TYPE_INPUT_METHOD, ((displayFrames, windowState, rect) ->
rect.set(0, 1, 2, 3)));
+ statusBarProvider.setWindowContainer(statusBar, null, imeOverrideProviders);
getController().getSourceProvider(ITYPE_IME).setWindowContainer(ime, null, null);
statusBar.setControllableInsetProvider(statusBarProvider);
statusBar.updateSourceFrame(statusBar.getFrame());
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index 1e86522a2307..e502f2fbd173 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -63,7 +63,7 @@ public class LetterboxTest {
mLetterbox = new Letterbox(mSurfaces, StubTransaction::new,
() -> mAreCornersRounded, () -> Color.valueOf(mColor),
() -> mHasWallpaperBackground, () -> mBlurRadius, () -> mDarkScrimAlpha,
- /* doubleTapCallback= */ x -> {});
+ /* doubleTapCallbackX= */ x -> {}, /* doubleTapCallbackY= */ y -> {});
mTransaction = spy(StubTransaction.class);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index feb1c6fa6b5c..83f375f85fa3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1229,7 +1229,6 @@ public class RecentTasksTest extends WindowTestsBase {
RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true);
assertTrue(info.supportsMultiWindow);
- assertTrue(info.supportsSplitScreenMultiWindow);
// The task can be put in split screen even if it is not attached now.
task.removeImmediately();
@@ -1237,7 +1236,6 @@ public class RecentTasksTest extends WindowTestsBase {
info = mRecentTasks.createRecentTaskInfo(task, true);
assertTrue(info.supportsMultiWindow);
- assertTrue(info.supportsSplitScreenMultiWindow);
// Test non-resizable.
// The non-resizable task cannot be put in split screen because of the config.
@@ -1247,7 +1245,6 @@ public class RecentTasksTest extends WindowTestsBase {
info = mRecentTasks.createRecentTaskInfo(task, true);
assertFalse(info.supportsMultiWindow);
- assertFalse(info.supportsSplitScreenMultiWindow);
// Even if it is not attached, the non-resizable task can be put in split screen as long as
// the device supports it.
@@ -1256,8 +1253,6 @@ public class RecentTasksTest extends WindowTestsBase {
info = mRecentTasks.createRecentTaskInfo(task, true);
assertTrue(info.supportsMultiWindow);
- assertTrue(info.supportsSplitScreenMultiWindow);
-
}
private TaskSnapshot createSnapshot(Point taskSize, Point bufferSize) {
@@ -1337,7 +1332,7 @@ public class RecentTasksTest extends WindowTestsBase {
});
assertSecurityException(expectCallable,
() -> mAtm.startActivityFromRecents(0, new Bundle()));
- assertSecurityException(expectCallable, () -> mAtm.getTaskSnapshot(0, true));
+ assertSecurityException(expectCallable, () -> mAtm.getTaskSnapshot(0, true, false));
assertSecurityException(expectCallable, () -> mAtm.registerTaskStackListener(null));
assertSecurityException(expectCallable,
() -> mAtm.unregisterTaskStackListener(null));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 1b19a28a9790..a1d6a5006fef 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -37,13 +37,13 @@ import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -91,9 +91,15 @@ public class RecentsAnimationTest extends WindowTestsBase {
TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
Task recentsStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_RECENTS, true /* onTop */);
+ final WindowProcessController wpc = mSystemServicesTestRule.addProcess(
+ mRecentsComponent.getPackageName(), mRecentsComponent.getPackageName(),
+ // Use real pid/uid of the test so the corresponding process can be mapped by
+ // Binder.getCallingPid/Uid.
+ WindowManagerService.MY_PID, WindowManagerService.MY_UID);
ActivityRecord recentActivity = new ActivityBuilder(mAtm)
.setComponent(mRecentsComponent)
.setTask(recentsStack)
+ .setUseProcess(wpc)
.build();
ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
topActivity.getRootTask().moveToFront("testRecentsActivityVisiblility");
@@ -106,11 +112,14 @@ public class RecentsAnimationTest extends WindowTestsBase {
mRecentsComponent, true /* getRecentsAnimation */);
// The launch-behind state should make the recents activity visible.
assertTrue(recentActivity.mVisibleRequested);
+ assertEquals(ActivityTaskManagerService.DEMOTE_TOP_REASON_ANIMATING_RECENTS,
+ mAtm.mDemoteTopAppReasons);
// Simulate the animation is cancelled without changing the stack order.
recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */);
// The non-top recents activity should be invisible by the restored launch-behind state.
assertFalse(recentActivity.mVisibleRequested);
+ assertEquals(0, mAtm.mDemoteTopAppReasons);
}
@Test
@@ -138,11 +147,8 @@ public class RecentsAnimationTest extends WindowTestsBase {
anyInt() /* startFlags */, any() /* profilerInfo */);
// Assume its process is alive because the caller should be the recents service.
- WindowProcessController wpc = new WindowProcessController(mAtm, aInfo.applicationInfo,
- aInfo.processName, aInfo.applicationInfo.uid, 0 /* userId */,
- mock(Object.class) /* owner */, mock(WindowProcessListener.class));
- wpc.setThread(mock(IApplicationThread.class));
- doReturn(wpc).when(mAtm).getProcessController(eq(wpc.mName), eq(wpc.mUid));
+ mSystemServicesTestRule.addProcess(aInfo.packageName, aInfo.processName, 12345 /* pid */,
+ aInfo.applicationInfo.uid);
Intent recentsIntent = new Intent().setComponent(mRecentsComponent);
// Null animation indicates to preload.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index e6910c2c0eca..027f5218f820 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -79,19 +79,25 @@ import org.mockito.MockitoAnnotations;
/**
* Build/Install/Run:
- * atest WmTests:RemoteAnimationControllerTest
+ * atest WmTests:RemoteAnimationControllerTest
*/
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
public class RemoteAnimationControllerTest extends WindowTestsBase {
- @Mock SurfaceControl mMockLeash;
- @Mock SurfaceControl mMockThumbnailLeash;
- @Mock Transaction mMockTransaction;
- @Mock OnAnimationFinishedCallback mFinishedCallback;
- @Mock OnAnimationFinishedCallback mThumbnailFinishedCallback;
- @Mock IRemoteAnimationRunner mMockRunner;
+ @Mock
+ SurfaceControl mMockLeash;
+ @Mock
+ SurfaceControl mMockThumbnailLeash;
+ @Mock
+ Transaction mMockTransaction;
+ @Mock
+ OnAnimationFinishedCallback mFinishedCallback;
+ @Mock
+ OnAnimationFinishedCallback mThumbnailFinishedCallback;
+ @Mock
+ IRemoteAnimationRunner mMockRunner;
private RemoteAnimationAdapter mAdapter;
private RemoteAnimationController mController;
private final OffsettableClock mClock = new OffsettableClock.Stopped();
@@ -105,7 +111,8 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50, true /* changeNeedsSnapshot */);
mAdapter.setCallingPidUid(123, 456);
runWithScissors(mWm.mH, () -> mHandler = new TestHandler(null, mClock), 0);
- mController = new RemoteAnimationController(mWm, mDisplayContent, mAdapter, mHandler);
+ mController = new RemoteAnimationController(mWm, mDisplayContent, mAdapter,
+ mHandler, false /*isActivityEmbedding*/);
}
private WindowState createAppOverlayWindow() {
@@ -117,13 +124,47 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
}
@Test
+ public void testForwardsShowBackdrop() throws Exception {
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mActivityRecord);
+ final WindowState overlayWin = createAppOverlayWindow();
+ try {
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord,
+ new Point(50, 100), null, new Rect(50, 100, 150, 150), null,
+ true /* showBackdrop */).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
+ finishedCaptor.capture());
+ assertEquals(1, appsCaptor.getValue().length);
+ final RemoteAnimationTarget app = appsCaptor.getValue()[0];
+ assertTrue(app.showBackdrop);
+ } finally {
+ mDisplayContent.mOpeningApps.clear();
+ }
+ }
+
+ @Test
public void testRun() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mDisplayContent.mOpeningApps.add(win.mActivityRecord);
final WindowState overlayWin = createAppOverlayWindow();
try {
- final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
- new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord,
+ new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -162,8 +203,9 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@Test
public void testCancel() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
- new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord,
+ new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -175,8 +217,9 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@Test
public void testTimeout() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
- new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord,
+ new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -197,7 +240,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
"testWin");
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150),
- null).mAdapter;
+ null, false).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -229,7 +272,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
public void testNotReallyStarted() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mController.createRemoteAnimationRecord(win.mActivityRecord,
- new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
+ new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
verify(mMockRunner, never()).onAnimationStart(anyInt(), any(), any(), any(), any());
verify(mMockRunner).onAnimationCancelled(anyBoolean());
@@ -240,9 +283,10 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1");
final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2");
mController.createRemoteAnimationRecord(win1.mActivityRecord,
- new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
- final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win2.mActivityRecord,
- new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
+ new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false);
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win2.mActivityRecord,
+ new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -265,8 +309,9 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
@Test
public void testRemovedBeforeStarted() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
- new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord,
+ new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
win.mActivityRecord.removeImmediately();
@@ -310,7 +355,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
try {
final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150),
- new Rect(0, 0, 200, 200));
+ new Rect(0, 0, 200, 200), false);
assertNotNull(record.mThumbnailAdapter);
((AnimationAdapter) record.mAdapter)
.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION,
@@ -364,7 +409,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
try {
final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
win.mActivityRecord, new Point(0, 0), null, new Rect(0, 0, 200, 200),
- new Rect(50, 100, 150, 150));
+ new Rect(50, 100, 150, 150), false);
assertNotNull(record.mThumbnailAdapter);
((AnimationAdapter) record.mAdapter)
.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION,
@@ -418,7 +463,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
try {
final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
win.mActivityRecord, new Point(100, 100), null, new Rect(150, 150, 400, 400),
- new Rect(50, 100, 150, 150));
+ new Rect(50, 100, 150, 150), false);
assertNotNull(record.mThumbnailAdapter);
((AnimationAdapter) record.mAdapter)
.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION,
@@ -475,8 +520,9 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mDisplayContent.mOpeningApps.add(win.mActivityRecord);
try {
- final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
- new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord,
+ new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -507,8 +553,9 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mDisplayContent.mOpeningApps.add(win.mActivityRecord);
try {
- final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
- new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
+ win.mActivityRecord,
+ new Point(50, 100), null, new Rect(50, 100, 150, 150), null, false).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
@@ -544,7 +591,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
try {
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
win.mActivityRecord, new Point(50, 100), null,
- new Rect(50, 100, 150, 150), null).mAdapter;
+ new Rect(50, 100, 150, 150), null, false).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY);
@@ -594,7 +641,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
try {
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
win.mActivityRecord, new Point(50, 100), null,
- new Rect(50, 100, 150, 150), null).mAdapter;
+ new Rect(50, 100, 150, 150), null, false).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER);
@@ -728,7 +775,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mDisplayContent.mOpeningApps.add(win2.mActivityRecord);
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
win2.mActivityRecord, new Point(50, 100), null,
- new Rect(50, 100, 150, 150), null).mAdapter;
+ new Rect(50, 100, 150, 150), null, false).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
@@ -758,7 +805,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
win.mActivityRecord, new Point(50, 100), null,
- new Rect(50, 100, 150, 150), null).mAdapter;
+ new Rect(50, 100, 150, 150), null, false).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
mController.goodToGo(transit);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 700fadd61c9a..68079f4a9ac5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -657,7 +657,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any());
- mAtm.setBooted(true);
+ setBooted(mAtm);
// Trigger resume on all displays
mRootWindowContainer.resumeFocusedTasksTopActivities();
@@ -685,7 +685,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any());
- mAtm.setBooted(true);
+ setBooted(mAtm);
// Trigger resume on all displays
mRootWindowContainer.resumeFocusedTasksTopActivities();
@@ -771,17 +771,10 @@ public class RootWindowContainerTests extends WindowTestsBase {
@Test
public void testNotStartHomeBeforeBoot() {
final int displayId = 1;
- final boolean isBooting = mAtm.mAmInternal.isBooting();
- final boolean isBooted = mAtm.mAmInternal.isBooted();
- try {
- mAtm.mAmInternal.setBooting(false);
- mAtm.mAmInternal.setBooted(false);
- mRootWindowContainer.onDisplayAdded(displayId);
- verify(mRootWindowContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
- } finally {
- mAtm.mAmInternal.setBooting(isBooting);
- mAtm.mAmInternal.setBooted(isBooted);
- }
+ doReturn(false).when(mAtm).isBooting();
+ doReturn(false).when(mAtm).isBooted();
+ mRootWindowContainer.onDisplayAdded(displayId);
+ verify(mRootWindowContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 891b33baf2f1..324e244c46f5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1259,6 +1259,36 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testOverrideMinAspectRatioLargeForResizableAppInSplitScreen() {
+ setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, activity.getDisplayContent());
+
+ // Move activity to split screen which takes half of the screen.
+ mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+ organizer.mPrimary.setBounds(0, 0, 1000, 1400);
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode());
+
+ // The per-package override forces the activity into a 16:9 aspect ratio
+ assertEquals(1400, activity.getBounds().height());
+ assertEquals(1400 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
public void testLaunchWithFixedRotationTransform() {
final int dw = 1000;
final int dh = 2500;
@@ -1409,12 +1439,10 @@ public class SizeCompatTests extends WindowTestsBase {
setUpDisplaySizeWithApp(2800, 1400);
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
- // Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed
- // orientation letterbox.
final float fixedOrientationLetterboxAspectRatio = 1.1f;
mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(
fixedOrientationLetterboxAspectRatio);
- prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
+ prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable= */ false);
final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
final Rect activityBounds = new Rect(mActivity.getBounds());
@@ -1435,6 +1463,111 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testDisplayIgnoreOrientationRequest_unresizableWithCorrespondingMinAspectRatio() {
+ // Set up a display in landscape and ignoring orientation request.
+ setUpDisplaySizeWithApp(2800, 1400);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ final float fixedOrientationLetterboxAspectRatio = 1.1f;
+ mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(
+ fixedOrientationLetterboxAspectRatio);
+ mActivity.mWmService.mLetterboxConfiguration.setDefaultMinAspectRatioForUnresizableApps(
+ 1.5f);
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ final Rect activityBounds = new Rect(mActivity.getBounds());
+
+ // Display shouldn't be rotated.
+ assertEquals(SCREEN_ORIENTATION_UNSPECIFIED,
+ mActivity.mDisplayContent.getLastOrientation());
+ assertTrue(displayBounds.width() > displayBounds.height());
+
+ // App should launch in fixed orientation letterbox.
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ assertFalse(mActivity.inSizeCompatMode());
+
+ // Letterbox logic should use config_letterboxDefaultMinAspectRatioForUnresizableApps over
+ // config_fixedOrientationLetterboxAspectRatio.
+ assertEquals(displayBounds.height(), activityBounds.height());
+ final float defaultAspectRatio = mActivity.mWmService.mLetterboxConfiguration
+ .getDefaultMinAspectRatioForUnresizableApps();
+ assertEquals(displayBounds.height() / defaultAspectRatio, activityBounds.width(), 0.5);
+ }
+
+ @Test
+ public void testSplitAspectRatioForUnresizablePortraitApps() {
+ // Set up a display in landscape and ignoring orientation request.
+ int screenWidth = 1600;
+ int screenHeight = 1400;
+ setUpDisplaySizeWithApp(screenWidth, screenHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mActivity.mWmService.mLetterboxConfiguration
+ .setIsSplitScreenAspectRatioForUnresizableAppsEnabled(true);
+
+ mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ final Rect activityBounds = new Rect(mActivity.getBounds());
+
+ // App should launch in fixed orientation letterbox.
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ // Checking that there is no size compat mode.
+ assertFitted();
+
+ assertEquals(displayBounds.height(), activityBounds.height());
+ assertTrue(activityBounds.width() < displayBounds.width() / 2);
+
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+ // Move activity to split screen which takes half of the screen.
+ mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+ organizer.mPrimary.setBounds(0, 0, getExpectedSplitSize(screenWidth), screenHeight);
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+ // Checking that there is no size compat mode.
+ assertFitted();
+ }
+
+ @Test
+ public void testSplitAspectRatioForUnresizableLandscapeApps() {
+ // Set up a display in portrait and ignoring orientation request.
+ int screenWidth = 1400;
+ int screenHeight = 1600;
+ setUpDisplaySizeWithApp(screenWidth, screenHeight);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mActivity.mWmService.mLetterboxConfiguration
+ .setIsSplitScreenAspectRatioForUnresizableAppsEnabled(true);
+
+ mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f);
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ final Rect activityBounds = new Rect(mActivity.getBounds());
+
+ // App should launch in fixed orientation letterbox.
+ assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ // Checking that there is no size compat mode.
+ assertFitted();
+
+ assertEquals(displayBounds.width(), activityBounds.width());
+ assertTrue(activityBounds.height() < displayBounds.height() / 2);
+
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+ // Move activity to split screen which takes half of the screen.
+ mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test");
+ organizer.mPrimary.setBounds(0, 0, screenWidth, getExpectedSplitSize(screenHeight));
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+ // Checking that there is no size compat mode.
+ assertFitted();
+ }
+
+ @Test
public void
testDisplayIgnoreOrientationRequest_orientationLetterboxBecameSizeCompatAfterRotate() {
// Set up a display in landscape and ignoring orientation request.
@@ -1908,7 +2041,7 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
- public void testSupportsNonResizableInSplitScreen_fillTaskForSameOrientation() {
+ public void testSupportsNonResizableInSplitScreen_aspectRatioLetterboxInSameOrientation() {
// Support non resizable in multi window
mAtm.mDevEnableNonResizableMultiWindow = true;
setUpDisplaySizeWithApp(1000, 2800);
@@ -1960,7 +2093,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
// Bounds are letterboxed to respect the provided max aspect ratio.
- assertEquals(mActivity.getBounds(), new Rect(0, 0, 1000, 1100));
+ assertEquals(mActivity.getBounds(), new Rect(0, 850, 1000, 1950));
// Move activity to split screen which has landscape size.
mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents */ false, "test");
@@ -2077,19 +2210,14 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
letterboxHorizontalPositionMultiplier);
prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
-
assertEquals(fixedOrientationLetterbox, mActivity.getBounds());
-
// Rotate to put activity in size compat mode.
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
-
assertTrue(mActivity.inSizeCompatMode());
// Activity is in size compat mode but not scaled.
assertEquals(sizeCompatUnscaled, mActivity.getBounds());
-
// Force activity to scaled down for size compat mode.
resizeDisplay(mTask.mDisplayContent, 700, 1400);
-
assertTrue(mActivity.inSizeCompatMode());
assertScaled();
assertEquals(sizeCompatScaled, mActivity.getBounds());
@@ -2107,6 +2235,109 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testUpdateResolvedBoundsVerticalPosition_top() {
+ // Display configured as (1400, 2800).
+ assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
+ /* letterboxVerticalPositionMultiplier */ 0.0f,
+ // At launch.
+ /* fixedOrientationLetterbox */ new Rect(0, 0, 1400, 700),
+ // After 90 degree rotation.
+ /* sizeCompatUnscaled */ new Rect(700, 0, 2100, 700),
+ // After the display is resized to (1400, 700).
+ /* sizeCompatScaled */ new Rect(0, 0, 700, 350));
+ }
+
+ @Test
+ public void testUpdateResolvedBoundsVerticalPosition_center() {
+ // Display configured as (1400, 2800).
+ assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
+ /* letterboxVerticalPositionMultiplier */ 0.5f,
+ // At launch.
+ /* fixedOrientationLetterbox */ new Rect(0, 1050, 1400, 1750),
+ // After 90 degree rotation.
+ /* sizeCompatUnscaled */ new Rect(700, 350, 2100, 1050),
+ // After the display is resized to (1400, 700).
+ /* sizeCompatScaled */ new Rect(0, 525, 700, 875));
+ }
+
+ @Test
+ public void testUpdateResolvedBoundsVerticalPosition_invalidMultiplier_defaultToCenter() {
+ // Display configured as (1400, 2800).
+
+ // Below 0.0.
+ assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
+ /* letterboxVerticalPositionMultiplier */ -1.0f,
+ // At launch.
+ /* fixedOrientationLetterbox */ new Rect(0, 1050, 1400, 1750),
+ // After 90 degree rotation.
+ /* sizeCompatUnscaled */ new Rect(700, 350, 2100, 1050),
+ // After the display is resized to (1400, 700).
+ /* sizeCompatScaled */ new Rect(0, 525, 700, 875));
+
+ // Above 1.0
+ assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
+ /* letterboxVerticalPositionMultiplier */ 2.0f,
+ // At launch.
+ /* fixedOrientationLetterbox */ new Rect(0, 1050, 1400, 1750),
+ // After 90 degree rotation.
+ /* sizeCompatUnscaled */ new Rect(700, 350, 2100, 1050),
+ // After the display is resized to (1400, 700).
+ /* sizeCompatScaled */ new Rect(0, 525, 700, 875));
+ }
+
+ @Test
+ public void testUpdateResolvedBoundsVerticalPosition_bottom() {
+ // Display configured as (1400, 2800).
+ assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
+ /* letterboxVerticalPositionMultiplier */ 1.0f,
+ // At launch.
+ /* fixedOrientationLetterbox */ new Rect(0, 2100, 1400, 2800),
+ // After 90 degree rotation.
+ /* sizeCompatUnscaled */ new Rect(700, 700, 2100, 1400),
+ // After the display is resized to (1400, 700).
+ /* sizeCompatScaled */ new Rect(0, 1050, 700, 1400));
+ }
+
+ private void assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
+ float letterboxVerticalPositionMultiplier, Rect fixedOrientationLetterbox,
+ Rect sizeCompatUnscaled, Rect sizeCompatScaled) {
+ // Set up a display in portrait and ignoring orientation request.
+ setUpDisplaySizeWithApp(1400, 2800);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ mActivity.mWmService.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(
+ letterboxVerticalPositionMultiplier);
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+ assertEquals(fixedOrientationLetterbox, mActivity.getBounds());
+
+ // Rotate to put activity in size compat mode.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+ assertTrue(mActivity.inSizeCompatMode());
+ // Activity is in size compat mode but not scaled.
+ assertEquals(sizeCompatUnscaled, mActivity.getBounds());
+
+ // Force activity to scaled down for size compat mode.
+ resizeDisplay(mTask.mDisplayContent, 1400, 700);
+
+ assertTrue(mActivity.inSizeCompatMode());
+ assertScaled();
+ assertEquals(sizeCompatScaled, mActivity.getBounds());
+ }
+
+ @Test
+ public void testUpdateResolvedBoundsVerticalPosition_activityFillParentHeight() {
+ // When activity height equals parent height, multiplier shouldn't have any effect.
+ assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity(
+ /* letterboxVerticalPositionMultiplier */ 0.0f);
+ assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity(
+ /* letterboxVerticalPositionMultiplier */ 0.5f);
+ assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity(
+ /* letterboxVerticalPositionMultiplier */ 1.0f);
+ }
+
+ @Test
public void testAreBoundsLetterboxed_letterboxedForAspectRatio_returnsTrue() {
setUpDisplaySizeWithApp(1000, 2500);
@@ -2390,6 +2621,16 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
}
+ private int getExpectedSplitSize(int dimensionToSplit) {
+ int dividerWindowWidth =
+ mActivity.mWmService.mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ int dividerInsets =
+ mActivity.mWmService.mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_insets);
+ return (dimensionToSplit - (dividerWindowWidth - dividerInsets * 2)) / 2;
+ }
+
private void assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
float letterboxHorizontalPositionMultiplier) {
// Set up a display in landscape and ignoring orientation request.
@@ -2399,6 +2640,23 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
letterboxHorizontalPositionMultiplier);
prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+ assertFitted();
+ // Rotate to put activity in size compat mode.
+ rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+ assertTrue(mActivity.inSizeCompatMode());
+ // Activity is in size compat mode but not scaled.
+ assertEquals(new Rect(0, 1050, 1400, 1750), mActivity.getBounds());
+ }
+
+ private void assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity(
+ float letterboxVerticalPositionMultiplier) {
+ // Set up a display in portrait and ignoring orientation request.
+ setUpDisplaySizeWithApp(1400, 2800);
+ mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ mActivity.mWmService.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(
+ letterboxVerticalPositionMultiplier);
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
@@ -2407,7 +2665,7 @@ public class SizeCompatTests extends WindowTestsBase {
assertTrue(mActivity.inSizeCompatMode());
// Activity is in size compat mode but not scaled.
- assertEquals(new Rect(0, 1050, 1400, 1750), mActivity.getBounds());
+ assertEquals(new Rect(1050, 0, 1750, 1400), mActivity.getBounds());
}
private static WindowState addWindowToActivity(ActivityRecord activity) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index dc9a62554fdb..6c100d76f5fb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -43,12 +43,14 @@ import static org.mockito.Mockito.withSettings;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
+import android.app.IApplicationThread;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
import android.database.ContentObserver;
@@ -66,7 +68,6 @@ import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.Log;
import android.view.InputChannel;
-import android.view.Surface;
import android.view.SurfaceControl;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
@@ -77,7 +78,6 @@ import com.android.server.LockGuard;
import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService;
-import com.android.server.appop.AppOpsService;
import com.android.server.display.color.ColorDisplayService;
import com.android.server.firewall.IntentFirewall;
import com.android.server.input.InputManagerService;
@@ -94,10 +94,8 @@ import org.mockito.MockSettings;
import org.mockito.Mockito;
import org.mockito.quality.Strictness;
-import java.io.File;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Supplier;
/**
* JUnit test rule to correctly setting up system services like {@link WindowManagerService}
@@ -125,13 +123,11 @@ public class SystemServicesTestRule implements TestRule {
private Description mDescription;
private Context mContext;
private StaticMockitoSession mMockitoSession;
- private ActivityManagerService mAmService;
private ActivityTaskManagerService mAtmService;
private WindowManagerService mWmService;
private WindowState.PowerManagerWrapper mPowerManagerWrapper;
private InputManagerService mImService;
private InputChannel mInputChannel;
- private Supplier<Surface> mSurfaceFactory = () -> mock(Surface.class);
/**
* Spied {@link SurfaceControl.Transaction} class than can be used to verify calls.
*/
@@ -289,29 +285,9 @@ public class SystemServicesTestRule implements TestRule {
}
private void setUpActivityTaskManagerService() {
- // ActivityManagerService
- mAmService = new ActivityManagerService(new AMTestInjector(mContext), null /* thread */);
- spyOn(mAmService);
- doReturn(mock(IPackageManager.class)).when(mAmService).getPackageManager();
- doNothing().when(mAmService).grantImplicitAccess(
- anyInt(), any(), anyInt(), anyInt());
-
// ActivityManagerInternal
- final ActivityManagerInternal amInternal = mAmService.mInternal;
- spyOn(amInternal);
- doNothing().when(amInternal).trimApplications();
- doNothing().when(amInternal).scheduleAppGcs();
- doNothing().when(amInternal).updateCpuStats();
- doNothing().when(amInternal).updateOomAdj();
- doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean());
- doNothing().when(amInternal).updateActivityUsageStats(
- any(), anyInt(), anyInt(), any(), any());
- doNothing().when(amInternal).startProcess(
- any(), any(), anyBoolean(), anyBoolean(), any(), any());
- doNothing().when(amInternal).updateOomLevelsForDisplay(anyInt());
- doNothing().when(amInternal).broadcastGlobalConfigurationChanged(anyInt(), anyBoolean());
- doNothing().when(amInternal).cleanUpServices(anyInt(), any(), any());
- doNothing().when(amInternal).reportCurKeyguardUsageEvent(anyBoolean());
+ final ActivityManagerInternal amInternal =
+ mock(ActivityManagerInternal.class, withSettings().stubOnly());
doReturn(UserHandle.USER_SYSTEM).when(amInternal).getCurrentUserId();
doReturn(TEST_USER_PROFILE_IDS).when(amInternal).getCurrentProfileIds();
doReturn(true).when(amInternal).isUserRunning(anyInt(), anyInt());
@@ -320,7 +296,9 @@ public class SystemServicesTestRule implements TestRule {
doReturn(false).when(amInternal).isActivityStartsLoggingEnabled();
LocalServices.addService(ActivityManagerInternal.class, amInternal);
- mAtmService = new TestActivityTaskManagerService(mContext, mAmService);
+ final ActivityManagerService amService =
+ mock(ActivityManagerService.class, withSettings().stubOnly());
+ mAtmService = new TestActivityTaskManagerService(mContext, amService);
LocalServices.addService(ActivityTaskManagerInternal.class, mAtmService.getAtmInternal());
}
@@ -334,7 +312,7 @@ public class SystemServicesTestRule implements TestRule {
mWmService = WindowManagerService.main(
mContext, mImService, false, false, wmPolicy, mAtmService,
testDisplayWindowSettingsProvider, StubTransaction::new,
- () -> mSurfaceFactory.get(), (unused) -> new MockSurfaceControlBuilder());
+ (unused) -> new MockSurfaceControlBuilder());
spyOn(mWmService);
spyOn(mWmService.mRoot);
// Invoked during {@link ActivityStack} creation.
@@ -355,7 +333,7 @@ public class SystemServicesTestRule implements TestRule {
null, null, mTransaction, mWmService.mPowerManagerInternal);
mWmService.onInitReady();
- mAmService.setWindowManager(mWmService);
+ mAtmService.setWindowManager(mWmService);
mWmService.mDisplayEnabled = true;
mWmService.mDisplayReady = true;
// Set configuration for default display
@@ -454,8 +432,32 @@ public class SystemServicesTestRule implements TestRule {
.spiedInstance(sWakeLock).stubOnly());
}
- void setSurfaceFactory(Supplier<Surface> factory) {
- mSurfaceFactory = factory;
+ WindowProcessController addProcess(String pkgName, String procName, int pid, int uid) {
+ return addProcess(mAtmService, pkgName, procName, pid, uid);
+ }
+
+ static WindowProcessController addProcess(ActivityTaskManagerService atmService, String pkgName,
+ String procName, int pid, int uid) {
+ final ApplicationInfo info = new ApplicationInfo();
+ info.uid = uid;
+ info.packageName = pkgName;
+ return addProcess(atmService, info, procName, pid);
+ }
+
+ static WindowProcessController addProcess(ActivityTaskManagerService atmService,
+ ApplicationInfo info, String procName, int pid) {
+ final WindowProcessListener mockListener = mock(WindowProcessListener.class,
+ withSettings().stubOnly());
+ final int uid = info.uid;
+ final WindowProcessController proc = new WindowProcessController(atmService,
+ info, procName, uid, UserHandle.getUserId(uid), mockListener, mockListener);
+ proc.setThread(mock(IApplicationThread.class, withSettings().stubOnly()));
+ atmService.mProcessNames.put(procName, uid, proc);
+ if (pid > 0) {
+ proc.setPid(pid);
+ atmService.mProcessMap.put(pid, proc);
+ }
+ return proc;
}
void cleanupWindowManagerHandlers() {
@@ -618,32 +620,4 @@ public class SystemServicesTestRule implements TestRule {
doReturn(true).when(controller).checkKeyguardVisibility(any());
}
}
-
- // TODO: Can we just mock this?
- private static class AMTestInjector extends ActivityManagerService.Injector {
-
- AMTestInjector(Context context) {
- super(context);
- }
-
- @Override
- public Context getContext() {
- return getInstrumentation().getTargetContext();
- }
-
- @Override
- public AppOpsService getAppOpsService(File file, Handler handler) {
- return null;
- }
-
- @Override
- public Handler getUiHandler(ActivityManagerService service) {
- return UiThread.getHandler();
- }
-
- @Override
- public boolean isNetworkRestrictedForUid(int uid) {
- return false;
- }
- }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index e8f1d2390c34..7f09606d1c3a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -82,7 +82,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */);
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
Task actualRootTask = taskDisplayArea.getLaunchRootTask(
@@ -108,7 +108,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
final Task adjacentRootTask = createTask(
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
- adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */);
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchRootTask(rootTask,
new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD});
@@ -129,7 +129,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */);
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
final Task actualRootTask = taskDisplayArea.getLaunchRootTask(
@@ -756,18 +756,26 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
adjacentRootTask.mCreatedByOrganizer = true;
final Task candidateTask = createTaskInRootTask(rootTask, 0 /* userId*/);
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */);
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
// Verify the launch root with candidate task
Task actualRootTask = taskDisplayArea.getLaunchRootTask(WINDOWING_MODE_UNDEFINED,
ACTIVITY_TYPE_STANDARD, null /* options */, adjacentRootTask /* sourceTask */,
0 /* launchFlags */, candidateTask);
- assertSame(rootTask, actualRootTask.getRootTask());
+ assertSame(rootTask, actualRootTask);
// Verify the launch root task without candidate task
actualRootTask = taskDisplayArea.getLaunchRootTask(WINDOWING_MODE_UNDEFINED,
ACTIVITY_TYPE_STANDARD, null /* options */, adjacentRootTask /* sourceTask */,
0 /* launchFlags */);
- assertSame(adjacentRootTask, actualRootTask.getRootTask());
+ assertSame(adjacentRootTask, actualRootTask);
+
+ final Task pinnedTask = createTask(
+ mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+ // Verify not adjusting launch target for pinned candidate task
+ actualRootTask = taskDisplayArea.getLaunchRootTask(WINDOWING_MODE_UNDEFINED,
+ ACTIVITY_TYPE_STANDARD, null /* options */, adjacentRootTask /* sourceTask */,
+ 0 /* launchFlags */, pinnedTask /* candidateTask */);
+ assertNull(actualRootTask);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index ed8440027bdc..e47bcc98b908 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -35,7 +35,6 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -422,7 +421,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Throw exception if the transaction is trying to change a window that is not organized by
// the organizer.
- mTransaction.setAdjacentRoots(mFragmentWindowToken, token2, false /* moveTogether */);
+ mTransaction.setAdjacentRoots(mFragmentWindowToken, token2);
assertApplyTransactionDisallowed(mTransaction);
@@ -637,7 +636,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
eq(mErrorToken), any(IllegalArgumentException.class));
- verify(mTaskFragment, never()).setAdjacentTaskFragment(any(), anyBoolean());
+ verify(mTaskFragment, never()).setAdjacentTaskFragment(any());
}
@Test
@@ -923,13 +922,14 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
.setBounds(mTaskFragBounds)
.build();
mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
- clearInvocations(mAtm.mRootWindowContainer);
// Reparent activity to mTaskFragment, which is smaller than activity's
// minimum dimensions.
mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token)
.setErrorCallbackToken(mErrorToken);
mWindowOrganizerController.applyTransaction(mTransaction);
+ // The pending event will be dispatched on the handler (from requestTraversal).
+ waitHandlerIdle(mWm.mAnimationHandler);
verify(mOrganizer).onTaskFragmentError(eq(mErrorToken), any(SecurityException.class));
}
@@ -958,7 +958,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
.build();
mWindowOrganizerController.mLaunchTaskFragments.put(oldFragToken, oldTaskFrag);
mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
- clearInvocations(mAtm.mRootWindowContainer);
// Reparent oldTaskFrag's children to mTaskFragment, which is smaller than activity's
// minimum dimensions.
@@ -966,6 +965,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mTaskFragment.mRemoteToken.toWindowContainerToken())
.setErrorCallbackToken(mErrorToken);
mWindowOrganizerController.applyTransaction(mTransaction);
+ // The pending event will be dispatched on the handler (from requestTraversal).
+ waitHandlerIdle(mWm.mAnimationHandler);
verify(mOrganizer).onTaskFragmentError(eq(mErrorToken), any(SecurityException.class));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 3ed484ac7391..5f3096356bc5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -202,7 +202,7 @@ public class TaskFragmentTest extends WindowTestsBase {
doReturn(true).when(primaryActivity).supportsPictureInPicture();
doReturn(false).when(secondaryActivity).supportsPictureInPicture();
- primaryTf.setAdjacentTaskFragment(secondaryTf, false /* moveAdjacentTogether */);
+ primaryTf.setAdjacentTaskFragment(secondaryTf);
primaryActivity.setState(RESUMED, "test");
secondaryActivity.setState(RESUMED, "test");
@@ -448,7 +448,7 @@ public class TaskFragmentTest extends WindowTestsBase {
.setOrganizer(mOrganizer)
.setFragmentToken(new Binder())
.build();
- tf0.setAdjacentTaskFragment(tf1, false /* moveAdjacentTogether */);
+ tf0.setAdjacentTaskFragment(tf1);
tf0.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
tf1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
task.setBounds(0, 0, 1200, 1000);
@@ -475,5 +475,13 @@ public class TaskFragmentTest extends WindowTestsBase {
assertFalse(activity0.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(activity1.isLetterboxedForFixedOrientationAndAspectRatio());
assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
+
+ tf0.setResumedActivity(activity0, "test");
+ tf1.setResumedActivity(activity1, "test");
+ mDisplayContent.mFocusedApp = activity1;
+
+ // Making the activity0 be the focused activity and ensure the focused app is updated.
+ activity0.moveFocusableActivityToTop("test");
+ assertEquals(activity0, mDisplayContent.mFocusedApp);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 202168bae869..f4323db2ceec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -31,7 +31,6 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_90;
@@ -60,7 +59,6 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.clearInvocations;
@@ -270,6 +268,20 @@ public class TaskTests extends WindowTestsBase {
assertFalse(task.hasChild());
// In real case, the task should be preserved for adding new activity.
assertTrue(task.isAttached());
+
+ final ActivityRecord activityA = new ActivityBuilder(mAtm).setTask(task).build();
+ final ActivityRecord activityB = new ActivityBuilder(mAtm).setTask(task).build();
+ final ActivityRecord activityC = new ActivityBuilder(mAtm).setTask(task).build();
+ activityA.setState(ActivityRecord.State.STOPPED, "test");
+ activityB.setState(ActivityRecord.State.PAUSED, "test");
+ activityC.setState(ActivityRecord.State.RESUMED, "test");
+ doReturn(true).when(activityB).shouldBeVisibleUnchecked();
+ doReturn(true).when(activityC).shouldBeVisibleUnchecked();
+ activityA.getConfiguration().densityDpi += 100;
+ assertTrue(task.performClearTop(activityA, 0 /* launchFlags */).finishing);
+ // The bottom activity should destroy directly without relaunch for config change.
+ assertEquals(ActivityRecord.State.DESTROYING, activityA.getState());
+ verify(activityA, never()).startRelaunching();
}
@Test
@@ -497,34 +509,6 @@ public class TaskTests extends WindowTestsBase {
assertEquals(reqBounds.height(), task.getBounds().height());
}
- /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */
- @Test
- public void testBoundsOnModeChangeFreeformToFullscreen() {
- DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay();
- Task rootTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true)
- .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
- Task task = rootTask.getBottomMostTask();
- task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
- DisplayInfo info = new DisplayInfo();
- display.mDisplay.getDisplayInfo(info);
- final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
- final Rect freeformBounds = new Rect(fullScreenBounds);
- freeformBounds.inset((int) (freeformBounds.width() * 0.2),
- (int) (freeformBounds.height() * 0.2));
- task.setBounds(freeformBounds);
-
- assertEquals(freeformBounds, task.getBounds());
-
- // FULLSCREEN inherits bounds
- rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertEquals(fullScreenBounds, task.getBounds());
- assertEquals(freeformBounds, task.mLastNonFullscreenBounds);
-
- // FREEFORM restores bounds
- rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(freeformBounds, task.getBounds());
- }
-
/**
* Tests that a task with forced orientation has orientation-consistent bounds within the
* parent.
@@ -590,6 +574,7 @@ public class TaskTests extends WindowTestsBase {
// FULLSCREEN letterboxes bounds on activity level, no constraint on task level.
rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ rootTask.setBounds(null);
assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
assertEquals(fullScreenBoundsPort, task.getBounds());
@@ -667,8 +652,6 @@ public class TaskTests extends WindowTestsBase {
final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
final Configuration inOutConfig = new Configuration();
final Configuration parentConfig = new Configuration();
- final int longSide = 1200;
- final int shortSide = 600;
final Rect parentBounds = new Rect(0, 0, 250, 500);
final Rect parentAppBounds = new Rect(0, 0, 250, 480);
parentConfig.windowConfiguration.setBounds(parentBounds);
@@ -689,14 +672,17 @@ public class TaskTests extends WindowTestsBase {
// If bounds are overridden, config properties should be made to match. Surface hierarchy
// will crop for policy.
inOutConfig.setToDefaults();
+ final int longSide = 960;
+ final int shortSide = 540;
+ parentConfig.densityDpi = 192;
final Rect largerPortraitBounds = new Rect(0, 0, shortSide, longSide);
inOutConfig.windowConfiguration.setBounds(largerPortraitBounds);
task.computeConfigResourceOverrides(inOutConfig, parentConfig);
// The override bounds are beyond the parent, the out appBounds should not be intersected
// by parent appBounds.
assertEquals(largerPortraitBounds, inOutConfig.windowConfiguration.getAppBounds());
- assertEquals(longSide, inOutConfig.screenHeightDp * parentConfig.densityDpi / 160);
- assertEquals(shortSide, inOutConfig.screenWidthDp * parentConfig.densityDpi / 160);
+ assertEquals(800, inOutConfig.screenHeightDp); // 960/(192/160) = 800
+ assertEquals(450, inOutConfig.screenWidthDp); // 540/(192/160) = 450
inOutConfig.setToDefaults();
// Landscape bounds.
@@ -716,16 +702,17 @@ public class TaskTests extends WindowTestsBase {
// Without limiting to be inside the parent bounds, the out screen size should keep relative
// to the input bounds.
final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
- final ActivityRecord.CompatDisplayInsets compatIntsets =
+ final ActivityRecord.CompatDisplayInsets compatInsets =
new ActivityRecord.CompatDisplayInsets(
display, activity, /* fixedOrientationBounds= */ null);
- task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets);
+ task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatInsets);
assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds());
- assertEquals((shortSide - statusBarHeight) * DENSITY_DEFAULT / parentConfig.densityDpi,
- inOutConfig.screenHeightDp);
- assertEquals(longSide * DENSITY_DEFAULT / parentConfig.densityDpi,
- inOutConfig.screenWidthDp);
+ final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ final int expectedHeightDp = (int) ((shortSide - statusBarHeight) / density + 0.5f);
+ assertEquals(expectedHeightDp, inOutConfig.screenHeightDp);
+ final int expectedWidthDp = (int) (longSide / density + 0.5f);
+ assertEquals(expectedWidthDp, inOutConfig.screenWidthDp);
assertEquals(Configuration.ORIENTATION_LANDSCAPE, inOutConfig.orientation);
}
@@ -1443,25 +1430,6 @@ public class TaskTests extends WindowTestsBase {
}
@Test
- public void testMoveToFront_moveAdjacentTask() {
- final Task task1 =
- createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
- final Task task2 =
- createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
- spyOn(task2);
-
- task1.setAdjacentTaskFragment(task2, false /* moveTogether */);
- task1.moveToFront("" /* reason */);
- verify(task2, never()).moveToFrontInner(anyString(), isNull());
-
- // Reset adjacent tasks to move together.
- task1.setAdjacentTaskFragment(null, false /* moveTogether */);
- task1.setAdjacentTaskFragment(task2, true /* moveTogether */);
- task1.moveToFront("" /* reason */);
- verify(task2).moveToFrontInner(anyString(), isNull());
- }
-
- @Test
public void testResumeTask_doNotResumeTaskFragmentBehindTranslucent() {
final Task task = createTask(mDisplayContent);
final TaskFragment tfBehind = createTaskFragmentWithParentTask(
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index ea98b6b17e83..92457c73b315 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -26,7 +26,6 @@ import android.os.IBinder;
import android.os.PowerManager.GoToSleepReason;
import android.os.PowerManager.WakeReason;
import android.util.proto.ProtoOutputStream;
-import android.view.IWindowManager;
import android.view.KeyEvent;
import android.view.WindowManager;
import android.view.animation.Animation;
@@ -50,8 +49,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
}
@Override
- public void init(Context context, IWindowManager windowManager,
- WindowManagerFuncs windowManagerFuncs) {
+ public void init(Context context, WindowManagerFuncs windowManagerFuncs) {
}
public void setDefaultDisplay(DisplayContentInfo displayContentInfo) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 6c1c0865446e..c323e02ad149 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -30,6 +30,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.window.TransitionInfo.FLAG_IS_EMBEDDED;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
@@ -61,8 +62,10 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.view.SurfaceControl;
import android.window.IDisplayAreaOrganizer;
+import android.window.ITaskFragmentOrganizer;
import android.window.ITaskOrganizer;
import android.window.ITransitionPlayer;
+import android.window.TaskFragmentOrganizer;
import android.window.TransitionInfo;
import androidx.test.filters.SmallTest;
@@ -85,9 +88,14 @@ import java.util.function.Function;
@Presubmit
@RunWith(WindowTestRunner.class)
public class TransitionTests extends WindowTestsBase {
+ final SurfaceControl.Transaction mMockT = mock(SurfaceControl.Transaction.class);
private Transition createTestTransition(int transitType) {
- TransitionController controller = mock(TransitionController.class);
+ TransitionTracer tracer = mock(TransitionTracer.class);
+ final TransitionController controller = new TransitionController(
+ mock(ActivityTaskManagerService.class), mock(TaskSnapshotController.class),
+ mock(TransitionTracer.class));
+
final BLASTSyncEngine sync = createTestBLASTSyncEngine();
final Transition t = new Transition(transitType, 0 /* flags */, controller, sync);
t.startCollecting(0 /* timeoutMs */);
@@ -121,7 +129,8 @@ public class TransitionTests extends WindowTestsBase {
participants.add(oldTask);
participants.add(newTask);
ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
- TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes,
+ mMockT);
assertEquals(2, info.getChanges().size());
assertEquals(transit, info.getType());
@@ -129,7 +138,7 @@ public class TransitionTests extends WindowTestsBase {
participants.add(opening);
participants.add(closing);
targets = Transition.calculateTargets(participants, changes);
- info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT);
assertEquals(2, info.getChanges().size());
assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
@@ -137,7 +146,7 @@ public class TransitionTests extends WindowTestsBase {
// Check combined prune and promote
participants.remove(newTask);
targets = Transition.calculateTargets(participants, changes);
- info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT);
assertEquals(2, info.getChanges().size());
assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
@@ -145,7 +154,7 @@ public class TransitionTests extends WindowTestsBase {
// Check multi promote
participants.remove(oldTask);
targets = Transition.calculateTargets(participants, changes);
- info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT);
assertEquals(2, info.getChanges().size());
assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
@@ -186,7 +195,8 @@ public class TransitionTests extends WindowTestsBase {
participants.add(opening);
participants.add(opening2);
ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
- TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes,
+ mMockT);
assertEquals(2, info.getChanges().size());
assertEquals(transit, info.getType());
assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken()));
@@ -195,7 +205,7 @@ public class TransitionTests extends WindowTestsBase {
// Check that unchanging but visible descendant of sibling prevents promotion
participants.remove(opening2);
targets = Transition.calculateTargets(participants, changes);
- info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT);
assertEquals(2, info.getChanges().size());
assertNotNull(info.getChange(newNestedTask.mRemoteToken.toWindowContainerToken()));
assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken()));
@@ -232,7 +242,8 @@ public class TransitionTests extends WindowTestsBase {
participants.add(showing);
participants.add(showing2);
ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
- TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes,
+ mMockT);
assertEquals(1, info.getChanges().size());
assertEquals(transit, info.getType());
assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken()));
@@ -240,14 +251,14 @@ public class TransitionTests extends WindowTestsBase {
// Check that organized tasks get reported even if not top
makeTaskOrganized(showTask);
targets = Transition.calculateTargets(participants, changes);
- info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT);
assertEquals(2, info.getChanges().size());
assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken()));
assertNotNull(info.getChange(showTask.mRemoteToken.toWindowContainerToken()));
// Even if DisplayArea explicitly participating
participants.add(tda);
targets = Transition.calculateTargets(participants, changes);
- info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT);
assertEquals(2, info.getChanges().size());
}
@@ -271,7 +282,7 @@ public class TransitionTests extends WindowTestsBase {
ArrayList<WindowContainer> targets = Transition.calculateTargets(
transition.mParticipants, transition.mChanges);
TransitionInfo info = Transition.calculateTransitionInfo(
- 0, 0, targets, transition.mChanges);
+ 0, 0, targets, transition.mChanges, mMockT);
assertEquals(2, info.getChanges().size());
// There was an existence change on open, so it should be OPEN rather than SHOW
assertEquals(TRANSIT_OPEN,
@@ -308,7 +319,7 @@ public class TransitionTests extends WindowTestsBase {
ArrayList<WindowContainer> targets = Transition.calculateTargets(
transition.mParticipants, transition.mChanges);
TransitionInfo info = Transition.calculateTransitionInfo(
- 0, 0, targets, transition.mChanges);
+ 0, 0, targets, transition.mChanges, mMockT);
assertEquals(taskCount, info.getChanges().size());
// verify order is top-to-bottem
for (int i = 0; i < taskCount; ++i) {
@@ -358,7 +369,7 @@ public class TransitionTests extends WindowTestsBase {
ArrayList<WindowContainer> targets = Transition.calculateTargets(
transition.mParticipants, transition.mChanges);
TransitionInfo info = Transition.calculateTransitionInfo(
- 0, 0, targets, transition.mChanges);
+ 0, 0, targets, transition.mChanges, mMockT);
// verify that wallpaper is at bottom
assertEquals(taskCount + 1, info.getChanges().size());
// The wallpaper is not organized, so it won't have a token; however, it will be marked
@@ -392,7 +403,7 @@ public class TransitionTests extends WindowTestsBase {
ArrayList<WindowContainer> targets = Transition.calculateTargets(
transition.mParticipants, transition.mChanges);
TransitionInfo info = Transition.calculateTransitionInfo(
- 0, 0, targets, transition.mChanges);
+ 0, 0, targets, transition.mChanges, mMockT);
// The wallpaper is not organized, so it won't have a token; however, it will be marked
// as IS_WALLPAPER
assertEquals(FLAG_IS_WALLPAPER, info.getChanges().get(0).getFlags());
@@ -474,7 +485,8 @@ public class TransitionTests extends WindowTestsBase {
participants.add(changeTask);
final ArrayList<WindowContainer> targets =
Transition.calculateTargets(participants, changes);
- TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes,
+ mMockT);
// Root changes should always be considered independent
assertTrue(isIndependent(
info.getChange(openTask.mRemoteToken.toWindowContainerToken()), info));
@@ -526,7 +538,8 @@ public class TransitionTests extends WindowTestsBase {
participants.add(oldTask);
participants.add(newTask);
ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
- TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes,
+ mMockT);
assertEquals(2, info.getChanges().size());
assertEquals(transit, info.getType());
@@ -566,7 +579,8 @@ public class TransitionTests extends WindowTestsBase {
participants.add(oldTask);
participants.add(newTask);
ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
- TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes);
+ TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes,
+ mMockT);
assertEquals(2, info.getChanges().size());
assertEquals(transit, info.getType());
@@ -577,7 +591,7 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testTimeout() {
final TransitionController controller = new TransitionController(mAtm,
- mock(TaskSnapshotController.class));
+ mock(TaskSnapshotController.class), mock(TransitionTracer.class));
final BLASTSyncEngine sync = new BLASTSyncEngine(mWm);
final CountDownLatch latch = new CountDownLatch(1);
// When the timeout is reached, it will finish the sync-group and notify transaction ready.
@@ -608,7 +622,7 @@ public class TransitionTests extends WindowTestsBase {
final int flags = 0;
final TransitionInfo info = Transition.calculateTransitionInfo(transition.mType, flags,
Transition.calculateTargets(transition.mParticipants, transition.mChanges),
- transition.mChanges);
+ transition.mChanges, mMockT);
transition.abort();
return info.getChanges().get(0);
};
@@ -654,6 +668,11 @@ public class TransitionTests extends WindowTestsBase {
mDisplayContent.setLastHasContent();
mDisplayContent.requestChangeTransitionIfNeeded(1 /* any changes */,
null /* displayChange */);
+ assertEquals(WindowContainer.SYNC_STATE_NONE, statusBar.mSyncState);
+ assertEquals(WindowContainer.SYNC_STATE_NONE, navBar.mSyncState);
+ assertEquals(WindowContainer.SYNC_STATE_NONE, screenDecor.mSyncState);
+ assertEquals(WindowContainer.SYNC_STATE_WAITING_FOR_DRAW, ime.mSyncState);
+
final AsyncRotationController asyncRotationController =
mDisplayContent.getAsyncRotationController();
assertNotNull(asyncRotationController);
@@ -667,6 +686,7 @@ public class TransitionTests extends WindowTestsBase {
assertTrue(ime.mToken.inTransition());
assertTrue(task.inTransition());
assertTrue(asyncRotationController.isTargetToken(decorToken));
+ assertTrue(asyncRotationController.shouldFreezeInsetsPosition(navBar));
screenDecor.setOrientationChanging(false);
// Status bar finishes drawing before the start transaction. Its fade-in animation will be
@@ -767,7 +787,7 @@ public class TransitionTests extends WindowTestsBase {
player.start();
player.finish();
- app.getTask().clearSyncState();
+ app.getTask().finishSync(mWm.mTransactionFactory.get(), false /* cancel */);
// The open transition is finished. Continue to play seamless display change transition,
// so the previous async rotation controller should still exist.
@@ -777,6 +797,10 @@ public class TransitionTests extends WindowTestsBase {
assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
assertNotNull(mDisplayContent.getAsyncRotationController());
+ // The app is still in transition, so the callback should be no-op.
+ mDisplayContent.mTransitionController.dispatchLegacyAppTransitionFinished(app);
+ assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
+
statusBar.setOrientationChanging(true);
player.startTransition();
// Non-app windows should not be collected.
@@ -814,8 +838,11 @@ public class TransitionTests extends WindowTestsBase {
player.onTransactionReady(mDisplayContent.getSyncTransaction());
final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
+ final RemoteDisplayChangeController displayChangeController = mDisplayContent
+ .mRemoteDisplayChangeController;
spyOn(displayRotation);
- doReturn(true).when(displayRotation).isWaitingForRemoteRotation();
+ spyOn(displayChangeController);
+ doReturn(true).when(displayChangeController).isWaitingForRemoteDisplayChange();
doReturn(prevRotation + 1).when(displayRotation).rotationForOrientation(
anyInt() /* orientation */, anyInt() /* lastRotation */);
// Rotation update is skipped while the recents animation is running.
@@ -831,7 +858,8 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testIntermediateVisibility() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
- final TransitionController controller = new TransitionController(mAtm, snapshotController);
+ final TransitionController controller = new TransitionController(mAtm, snapshotController,
+ mock(TransitionTracer.class));
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* appThread */);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
@@ -895,7 +923,8 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testTransientLaunch() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
- final TransitionController controller = new TransitionController(mAtm, snapshotController);
+ final TransitionController controller = new TransitionController(mAtm, snapshotController,
+ mock(TransitionTracer.class));
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* appThread */);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
@@ -955,6 +984,67 @@ public class TransitionTests extends WindowTestsBase {
verify(snapshotController, times(1)).recordTaskSnapshot(eq(task1), eq(false));
}
+ @Test
+ public void testNotReadyPushPop() {
+ final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
+ final TransitionController controller = new TransitionController(mAtm, snapshotController,
+ mock(TransitionTracer.class));
+ final ITransitionPlayer player = new ITransitionPlayer.Default();
+ controller.registerTransitionPlayer(player, null /* appThread */);
+ final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
+
+ // Start out with task2 visible and set up a transition that closes task2 and opens task1
+ final Task task1 = createTask(mDisplayContent);
+ openTransition.collectExistenceChange(task1);
+
+ assertFalse(openTransition.allReady());
+
+ openTransition.setAllReady();
+
+ openTransition.deferTransitionReady();
+ assertFalse(openTransition.allReady());
+
+ openTransition.continueTransitionReady();
+ assertTrue(openTransition.allReady());
+ }
+
+ @Test
+ public void testIsEmbeddedChange() {
+ final Transition transition = createTestTransition(TRANSIT_OPEN);
+ final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ final ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ final Task task = createTask(mDisplayContent);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ mAtm.mTaskFragmentOrganizerController.registerOrganizer(
+ ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
+ final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .createActivityCount(2)
+ .setOrganizer(organizer)
+ .build();
+ final ActivityRecord closingActivity = embeddedTf.getBottomMostActivity();
+ final ActivityRecord openingActivity = embeddedTf.getTopMostActivity();
+ // Start states.
+ changes.put(embeddedTf, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ changes.put(closingActivity, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ changes.put(openingActivity, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+ // End states.
+ closingActivity.mVisibleRequested = false;
+ openingActivity.mVisibleRequested = true;
+
+ participants.add(closingActivity);
+ participants.add(openingActivity);
+ final ArrayList<WindowContainer> targets = Transition.calculateTargets(
+ participants, changes);
+ final TransitionInfo info = Transition.calculateTransitionInfo(
+ transition.mType, 0 /* flags */, targets, changes, mMockT);
+
+ assertEquals(2, info.getChanges().size());
+ assertTrue((info.getChanges().get(0).getFlags() & FLAG_IS_EMBEDDED) != 0);
+ assertTrue((info.getChanges().get(1).getFlags() & FLAG_IS_EMBEDDED) != 0);
+ }
+
private static void makeTaskOrganized(Task... tasks) {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
for (Task t : tasks) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 1f68608d2ce6..fba4ff1f1c25 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -35,6 +35,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -82,10 +83,7 @@ public class WallpaperControllerTests extends WindowTestsBase {
assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
// No wallpaper WSA Surface
- WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
- true, dc, true /* ownerCanManageAppTokens */);
- WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER,
- wallpaperWindowToken, "wallpaperWindow");
+ final WindowState wallpaperWindow = createWallpaperWindow(dc);
assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
// Wallpaper with not visible WSA surface.
@@ -107,13 +105,10 @@ public class WallpaperControllerTests extends WindowTestsBase {
@Test
public void testWallpaperSizeWithFixedTransform() {
// No wallpaper
- final DisplayContent dc = createNewDisplay();
+ final DisplayContent dc = mDisplayContent;
// No wallpaper WSA Surface
- WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
- true, dc, true /* ownerCanManageAppTokens */);
- WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER,
- wallpaperWindowToken, "wallpaperWindow");
+ final WindowState wallpaperWindow = createWallpaperWindow(dc);
WindowManager.LayoutParams attrs = wallpaperWindow.getAttrs();
Rect bounds = dc.getBounds();
@@ -153,7 +148,7 @@ public class WallpaperControllerTests extends WindowTestsBase {
final WmDisplayCutout cutout = dc.calculateDisplayCutoutForRotation(Surface.ROTATION_0);
final DisplayFrames displayFrames = new DisplayFrames(dc.getDisplayId(), new InsetsState(),
info, cutout, RoundedCorners.NO_ROUNDED_CORNERS, new PrivacyIndicatorBounds());
- wallpaperWindowToken.applyFixedRotationTransform(info, displayFrames, config);
+ wallpaperWindow.mToken.applyFixedRotationTransform(info, displayFrames, config);
// Check that the wallpaper has the same frame in landscape than in portrait
assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getConfiguration().orientation);
@@ -163,10 +158,7 @@ public class WallpaperControllerTests extends WindowTestsBase {
@Test
public void testWallpaperZoom() throws RemoteException {
final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
- final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
- mock(IBinder.class), true, dc, true /* ownerCanManageAppTokens */);
- final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
- "wallpaperWindow");
+ final WindowState wallpaperWindow = createWallpaperWindow(dc);
wallpaperWindow.getAttrs().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
@@ -189,10 +181,7 @@ public class WallpaperControllerTests extends WindowTestsBase {
@Test
public void testWallpaperZoom_shouldNotScaleWallpaper() throws RemoteException {
final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
- final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
- mock(IBinder.class), true, dc, true /* ownerCanManageAppTokens */);
- final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
- "wallpaperWindow");
+ final WindowState wallpaperWindow = createWallpaperWindow(dc);
wallpaperWindow.getAttrs().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
@@ -219,11 +208,7 @@ public class WallpaperControllerTests extends WindowTestsBase {
@Test
public void testWallpaperZoom_multipleCallers() {
final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
- final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
- mock(IBinder.class), true, dc,
- true /* ownerCanManageAppTokens */);
- final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
- "wallpaperWindow");
+ final WindowState wallpaperWindow = createWallpaperWindow(dc);
wallpaperWindow.getAttrs().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
@@ -261,6 +246,22 @@ public class WallpaperControllerTests extends WindowTestsBase {
assertEquals(otherWindowInitialZoom, wallpaperWindow.mWallpaperZoomOut, .01f);
}
+ @Test
+ public void testUpdateWallpaperTarget() {
+ final DisplayContent dc = mDisplayContent;
+ final WindowState homeWin = createWallpaperTargetWindow(dc);
+ final WindowState appWin = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ final RecentsAnimationController recentsController = mock(RecentsAnimationController.class);
+ doReturn(true).when(recentsController).isWallpaperVisible(eq(appWin));
+ mWm.setRecentsAnimationController(recentsController);
+
+ dc.mWallpaperController.adjustWallpaperWindows();
+ assertEquals(appWin, dc.mWallpaperController.getWallpaperTarget());
+ // The wallpaper target is gone, so it should adjust to the next target.
+ appWin.removeImmediately();
+ assertEquals(homeWin, dc.mWallpaperController.getWallpaperTarget());
+ }
+
/**
* Tests that the windowing mode of the wallpaper window must always be fullscreen.
*/
@@ -278,37 +279,51 @@ public class WallpaperControllerTests extends WindowTestsBase {
assertEquals(WINDOWING_MODE_FULLSCREEN, token.getWindowingMode());
}
- @UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testFixedRotationRecentsAnimatingTask() {
+ final WindowState wallpaperWindow = createWallpaperWindow(mDisplayContent);
+ final WallpaperWindowToken wallpaperToken = wallpaperWindow.mToken.asWallpaperToken();
+ final WindowState appWin = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ makeWindowVisible(appWin);
+ final ActivityRecord r = appWin.mActivityRecord;
final RecentsAnimationController recentsController = mock(RecentsAnimationController.class);
- doReturn(true).when(recentsController).isWallpaperVisible(eq(mAppWindow));
+ doReturn(true).when(recentsController).isWallpaperVisible(eq(appWin));
mWm.setRecentsAnimationController(recentsController);
- mAppWindow.mActivityRecord.applyFixedRotationTransform(mDisplayContent.getDisplayInfo(),
+ r.applyFixedRotationTransform(mDisplayContent.getDisplayInfo(),
mDisplayContent.mDisplayFrames, mDisplayContent.getConfiguration());
- mAppWindow.mActivityRecord.mVisibleRequested = true;
+ // Invisible requested activity should not share its rotation transform.
+ r.mVisibleRequested = false;
mDisplayContent.mWallpaperController.adjustWallpaperWindows();
+ assertFalse(wallpaperToken.hasFixedRotationTransform());
- assertEquals(mAppWindow, mDisplayContent.mWallpaperController.getWallpaperTarget());
// Wallpaper should link the transform of its target.
- assertTrue(mAppWindow.mActivityRecord.hasFixedRotationTransform());
-
- mAppWindow.mActivityRecord.finishFixedRotationTransform();
- // Invisible requested activity should not share its rotation transform.
- mAppWindow.mActivityRecord.mVisibleRequested = false;
+ r.mVisibleRequested = true;
mDisplayContent.mWallpaperController.adjustWallpaperWindows();
+ assertEquals(appWin, mDisplayContent.mWallpaperController.getWallpaperTarget());
+ assertTrue(r.hasFixedRotationTransform());
+ assertTrue(wallpaperToken.hasFixedRotationTransform());
- assertFalse(mAppWindow.mActivityRecord.hasFixedRotationTransform());
+ // The case with shell transition.
+ registerTestTransitionPlayer();
+ final Transition t = r.mTransitionController.createTransition(TRANSIT_OPEN);
+ final ActivityRecord recents = mock(ActivityRecord.class);
+ t.collect(r.getTask());
+ r.mTransitionController.setTransientLaunch(recents, r.getTask());
+ // The activity in restore-below task should not be the target if keyguard is not locked.
+ mDisplayContent.mWallpaperController.adjustWallpaperWindows();
+ assertNotEquals(appWin, mDisplayContent.mWallpaperController.getWallpaperTarget());
+ // The activity in restore-below task should be the target if keyguard is occluded.
+ doReturn(true).when(mDisplayContent).isKeyguardLocked();
+ mDisplayContent.mWallpaperController.adjustWallpaperWindows();
+ assertEquals(appWin, mDisplayContent.mWallpaperController.getWallpaperTarget());
}
@Test
public void testWallpaperTokenVisibility() {
final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
- final WallpaperWindowToken token = new WallpaperWindowToken(mWm, mock(IBinder.class),
- true, dc, true /* ownerCanManageAppTokens */);
- final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, token,
- "wallpaperWindow");
+ final WindowState wallpaperWindow = createWallpaperWindow(dc);
+ final WallpaperWindowToken token = wallpaperWindow.mToken.asWallpaperToken();
wallpaperWindow.setHasSurface(true);
// Set-up mock shell transitions
@@ -350,6 +365,13 @@ public class WallpaperControllerTests extends WindowTestsBase {
assertTrue(token.isVisible());
}
+ private WindowState createWallpaperWindow(DisplayContent dc) {
+ final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
+ true /* explicit */, dc, true /* ownerCanManageAppTokens */);
+ return createWindow(null /* parent */, TYPE_WALLPAPER, wallpaperWindowToken,
+ "wallpaperWindow");
+ }
+
private WindowState createWallpaperTargetWindow(DisplayContent dc) {
final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService)
.setTask(dc.getDefaultTaskDisplayArea().getRootHomeTask())
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 1715a295ded3..c8ea70c5d650 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -22,8 +22,8 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.view.InsetsState.ITYPE_LOCAL_NAVIGATION_BAR_1;
-import static android.view.InsetsState.ITYPE_LOCAL_NAVIGATION_BAR_2;
+import static android.view.InsetsState.ITYPE_BOTTOM_GENERIC_OVERLAY;
+import static android.view.InsetsState.ITYPE_TOP_GENERIC_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -1302,40 +1302,42 @@ public class WindowContainerTests extends WindowTestsBase {
TYPE_BASE_APPLICATION);
attrs2.setTitle("AppWindow2");
activity2.addWindow(createWindowState(attrs2, activity2));
- Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700);
- Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200);
-
- rootTask.addLocalRectInsetsSourceProvider(navigationBarInsetsRect1,
- new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
- container.addLocalRectInsetsSourceProvider(navigationBarInsetsRect2,
- new int[]{ITYPE_LOCAL_NAVIGATION_BAR_2});
-
- InsetsSource navigationBarInsetsProvider1Source = new InsetsSource(
- ITYPE_LOCAL_NAVIGATION_BAR_1);
- navigationBarInsetsProvider1Source.setFrame(navigationBarInsetsRect1);
- navigationBarInsetsProvider1Source.setVisible(true);
- InsetsSource navigationBarInsetsProvider2Source = new InsetsSource(
- ITYPE_LOCAL_NAVIGATION_BAR_2);
- navigationBarInsetsProvider2Source.setFrame(navigationBarInsetsRect2);
- navigationBarInsetsProvider2Source.setVisible(true);
+ Rect genericOverlayInsetsRect1 = new Rect(0, 200, 1080, 700);
+ Rect genericOverlayInsetsRect2 = new Rect(0, 0, 1080, 200);
+
+ rootTask.addLocalRectInsetsSourceProvider(genericOverlayInsetsRect1,
+ new int[]{ITYPE_TOP_GENERIC_OVERLAY});
+ container.addLocalRectInsetsSourceProvider(genericOverlayInsetsRect2,
+ new int[]{ITYPE_BOTTOM_GENERIC_OVERLAY});
+
+ InsetsSource genericOverlayInsetsProvider1Source = new InsetsSource(
+ ITYPE_TOP_GENERIC_OVERLAY);
+ genericOverlayInsetsProvider1Source.setFrame(genericOverlayInsetsRect1);
+ genericOverlayInsetsProvider1Source.setVisible(true);
+ InsetsSource genericOverlayInsetsProvider2Source = new InsetsSource(
+ ITYPE_BOTTOM_GENERIC_OVERLAY);
+ genericOverlayInsetsProvider2Source.setFrame(genericOverlayInsetsRect2);
+ genericOverlayInsetsProvider2Source.setVisible(true);
activity0.forAllWindows(window -> {
- assertEquals(navigationBarInsetsRect1,
- window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame());
+ assertEquals(genericOverlayInsetsRect1,
+ window.getInsetsState().peekSource(ITYPE_TOP_GENERIC_OVERLAY).getFrame());
assertEquals(null,
- window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2));
+ window.getInsetsState().peekSource(ITYPE_BOTTOM_GENERIC_OVERLAY));
}, true);
activity1.forAllWindows(window -> {
- assertEquals(navigationBarInsetsRect1,
- window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame());
- assertEquals(navigationBarInsetsRect2,
- window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+ assertEquals(genericOverlayInsetsRect1,
+ window.getInsetsState().peekSource(ITYPE_TOP_GENERIC_OVERLAY).getFrame());
+ assertEquals(genericOverlayInsetsRect2,
+ window.getInsetsState().peekSource(ITYPE_BOTTOM_GENERIC_OVERLAY)
+ .getFrame());
}, true);
activity2.forAllWindows(window -> {
- assertEquals(navigationBarInsetsRect1,
- window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame());
- assertEquals(navigationBarInsetsRect2,
- window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+ assertEquals(genericOverlayInsetsRect1,
+ window.getInsetsState().peekSource(ITYPE_TOP_GENERIC_OVERLAY).getFrame());
+ assertEquals(genericOverlayInsetsRect2,
+ window.getInsetsState().peekSource(ITYPE_BOTTOM_GENERIC_OVERLAY)
+ .getFrame());
}, true);
}
@@ -1344,7 +1346,7 @@ public class WindowContainerTests extends WindowTestsBase {
/*
___ rootTask ________________________________________
| | |
- activity0 navigationBarInsetsProvider1 navigationBarInsetsProvider2
+ activity0 genericOverlayInsetsProvider1 genericOverlayInsetsProvider2
*/
final Task rootTask = createTask(mDisplayContent);
@@ -1355,22 +1357,22 @@ public class WindowContainerTests extends WindowTestsBase {
attrs.setTitle("AppWindow0");
activity0.addWindow(createWindowState(attrs, activity0));
- Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700);
- Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200);
+ Rect genericOverlayInsetsRect1 = new Rect(0, 200, 1080, 700);
+ Rect genericOverlayInsetsRect2 = new Rect(0, 0, 1080, 200);
- rootTask.addLocalRectInsetsSourceProvider(navigationBarInsetsRect1,
- new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+ rootTask.addLocalRectInsetsSourceProvider(genericOverlayInsetsRect1,
+ new int[]{ITYPE_TOP_GENERIC_OVERLAY});
activity0.forAllWindows(window -> {
- assertEquals(navigationBarInsetsRect1,
- window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame());
+ assertEquals(genericOverlayInsetsRect1,
+ window.getInsetsState().peekSource(ITYPE_TOP_GENERIC_OVERLAY).getFrame());
}, true);
- rootTask.addLocalRectInsetsSourceProvider(navigationBarInsetsRect2,
- new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+ rootTask.addLocalRectInsetsSourceProvider(genericOverlayInsetsRect2,
+ new int[]{ITYPE_TOP_GENERIC_OVERLAY});
activity0.forAllWindows(window -> {
- assertEquals(navigationBarInsetsRect2,
- window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame());
+ assertEquals(genericOverlayInsetsRect2,
+ window.getInsetsState().peekSource(ITYPE_TOP_GENERIC_OVERLAY).getFrame());
}, true);
}
@@ -1412,30 +1414,32 @@ public class WindowContainerTests extends WindowTestsBase {
Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200);
rootTask.addLocalRectInsetsSourceProvider(navigationBarInsetsRect1,
- new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+ new int[]{ITYPE_TOP_GENERIC_OVERLAY});
container.addLocalRectInsetsSourceProvider(navigationBarInsetsRect2,
- new int[]{ITYPE_LOCAL_NAVIGATION_BAR_2});
+ new int[]{ITYPE_BOTTOM_GENERIC_OVERLAY});
mDisplayContent.getInsetsStateController().onPostLayout();
- rootTask.removeLocalInsetsSourceProvider(new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+ rootTask.removeLocalInsetsSourceProvider(new int[]{ITYPE_TOP_GENERIC_OVERLAY});
mDisplayContent.getInsetsStateController().onPostLayout();
activity0.forAllWindows(window -> {
assertEquals(null,
- window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1));
+ window.getInsetsState().peekSource(ITYPE_TOP_GENERIC_OVERLAY));
assertEquals(null,
- window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2));
+ window.getInsetsState().peekSource(ITYPE_BOTTOM_GENERIC_OVERLAY));
}, true);
activity1.forAllWindows(window -> {
assertEquals(null,
- window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1));
+ window.getInsetsState().peekSource(ITYPE_TOP_GENERIC_OVERLAY));
assertEquals(navigationBarInsetsRect2,
- window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+ window.getInsetsState().peekSource(ITYPE_BOTTOM_GENERIC_OVERLAY)
+ .getFrame());
}, true);
activity2.forAllWindows(window -> {
assertEquals(null,
- window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1));
+ window.getInsetsState().peekSource(ITYPE_TOP_GENERIC_OVERLAY));
assertEquals(navigationBarInsetsRect2,
- window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+ window.getInsetsState().peekSource(ITYPE_BOTTOM_GENERIC_OVERLAY)
+ .getFrame());
}, true);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java
index ea18e58fe999..739e783e7c1b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowLayoutTests.java
@@ -72,7 +72,7 @@ public class WindowLayoutTests {
private static final Insets WATERFALL_INSETS = Insets.of(6, 0, 12, 0);
private final WindowLayout mWindowLayout = new WindowLayout();
- private final ClientWindowFrames mOutFrames = new ClientWindowFrames();
+ private final ClientWindowFrames mFrames = new ClientWindowFrames();
private WindowManager.LayoutParams mAttrs;
private InsetsState mState;
@@ -82,7 +82,6 @@ public class WindowLayoutTests {
private int mRequestedWidth;
private int mRequestedHeight;
private InsetsVisibilities mRequestedVisibilities;
- private Rect mAttachedWindowFrame;
private float mCompatScale;
@Before
@@ -100,14 +99,14 @@ public class WindowLayoutTests {
mRequestedWidth = DISPLAY_WIDTH;
mRequestedHeight = DISPLAY_HEIGHT;
mRequestedVisibilities = new InsetsVisibilities();
- mAttachedWindowFrame = null;
mCompatScale = 1f;
+ mFrames.attachedFrame = null;
}
private void computeFrames() {
mWindowLayout.computeFrames(mAttrs, mState, mDisplayCutoutSafe, mWindowBounds,
mWindowingMode, mRequestedWidth, mRequestedHeight, mRequestedVisibilities,
- mAttachedWindowFrame, mCompatScale, mOutFrames);
+ mCompatScale, mFrames);
}
private void addDisplayCutout() {
@@ -145,9 +144,9 @@ public class WindowLayoutTests {
public void defaultParams() {
computeFrames();
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.displayFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.parentFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.frame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.displayFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.parentFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.frame);
}
@Test
@@ -156,9 +155,9 @@ public class WindowLayoutTests {
mRequestedHeight = UNSPECIFIED_LENGTH;
computeFrames();
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.displayFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.parentFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.frame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.displayFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.parentFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.frame);
}
@Test
@@ -172,9 +171,9 @@ public class WindowLayoutTests {
mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
computeFrames();
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.displayFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.parentFrame);
- assertRect(0, STATUS_BAR_HEIGHT, width, STATUS_BAR_HEIGHT + height, mOutFrames.frame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.displayFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.parentFrame);
+ assertRect(0, STATUS_BAR_HEIGHT, width, STATUS_BAR_HEIGHT + height, mFrames.frame);
}
@Test
@@ -186,11 +185,24 @@ public class WindowLayoutTests {
computeFrames();
assertRect(0, top, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
- mOutFrames.displayFrame);
+ mFrames.displayFrame);
assertRect(0, top, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
- mOutFrames.parentFrame);
+ mFrames.parentFrame);
assertRect(0, top, DISPLAY_WIDTH, DISPLAY_HEIGHT - NAVIGATION_BAR_HEIGHT,
- mOutFrames.frame);
+ mFrames.frame);
+ }
+
+ @Test
+ public void attachedFrame() {
+ final int bottom = (DISPLAY_HEIGHT - STATUS_BAR_HEIGHT - NAVIGATION_BAR_HEIGHT) / 2;
+ mFrames.attachedFrame = new Rect(0, STATUS_BAR_HEIGHT, DISPLAY_WIDTH, bottom);
+ mRequestedWidth = UNSPECIFIED_LENGTH;
+ mRequestedHeight = UNSPECIFIED_LENGTH;
+ computeFrames();
+
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.displayFrame);
+ assertEquals(mFrames.attachedFrame, mFrames.parentFrame);
+ assertEquals(mFrames.attachedFrame, mFrames.frame);
}
@Test
@@ -198,9 +210,9 @@ public class WindowLayoutTests {
mAttrs.setFitInsetsTypes(WindowInsets.Type.statusBars());
computeFrames();
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mOutFrames.displayFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mOutFrames.parentFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mOutFrames.frame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mFrames.displayFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mFrames.parentFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mFrames.frame);
}
@Test
@@ -208,9 +220,9 @@ public class WindowLayoutTests {
mAttrs.setFitInsetsTypes(WindowInsets.Type.navigationBars());
computeFrames();
- assertInsetByTopBottom(0, NAVIGATION_BAR_HEIGHT, mOutFrames.displayFrame);
- assertInsetByTopBottom(0, NAVIGATION_BAR_HEIGHT, mOutFrames.parentFrame);
- assertInsetByTopBottom(0, NAVIGATION_BAR_HEIGHT, mOutFrames.frame);
+ assertInsetByTopBottom(0, NAVIGATION_BAR_HEIGHT, mFrames.displayFrame);
+ assertInsetByTopBottom(0, NAVIGATION_BAR_HEIGHT, mFrames.parentFrame);
+ assertInsetByTopBottom(0, NAVIGATION_BAR_HEIGHT, mFrames.frame);
}
@Test
@@ -218,9 +230,9 @@ public class WindowLayoutTests {
mAttrs.setFitInsetsTypes(0);
computeFrames();
- assertInsetByTopBottom(0, 0, mOutFrames.displayFrame);
- assertInsetByTopBottom(0, 0, mOutFrames.parentFrame);
- assertInsetByTopBottom(0, 0, mOutFrames.frame);
+ assertInsetByTopBottom(0, 0, mFrames.displayFrame);
+ assertInsetByTopBottom(0, 0, mFrames.parentFrame);
+ assertInsetByTopBottom(0, 0, mFrames.frame);
}
@Test
@@ -228,9 +240,9 @@ public class WindowLayoutTests {
mAttrs.setFitInsetsSides(WindowInsets.Side.all());
computeFrames();
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.displayFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.parentFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.frame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.displayFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.parentFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.frame);
}
@Test
@@ -238,9 +250,9 @@ public class WindowLayoutTests {
mAttrs.setFitInsetsSides(WindowInsets.Side.TOP);
computeFrames();
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mOutFrames.displayFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mOutFrames.parentFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mOutFrames.frame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mFrames.displayFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mFrames.parentFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, 0, mFrames.frame);
}
@Test
@@ -248,9 +260,9 @@ public class WindowLayoutTests {
mAttrs.setFitInsetsSides(0);
computeFrames();
- assertInsetByTopBottom(0, 0, mOutFrames.displayFrame);
- assertInsetByTopBottom(0, 0, mOutFrames.parentFrame);
- assertInsetByTopBottom(0, 0, mOutFrames.frame);
+ assertInsetByTopBottom(0, 0, mFrames.displayFrame);
+ assertInsetByTopBottom(0, 0, mFrames.parentFrame);
+ assertInsetByTopBottom(0, 0, mFrames.frame);
}
@Test
@@ -259,9 +271,9 @@ public class WindowLayoutTests {
mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(false);
computeFrames();
- assertInsetByTopBottom(0, 0, mOutFrames.displayFrame);
- assertInsetByTopBottom(0, 0, mOutFrames.parentFrame);
- assertInsetByTopBottom(0, 0, mOutFrames.frame);
+ assertInsetByTopBottom(0, 0, mFrames.displayFrame);
+ assertInsetByTopBottom(0, 0, mFrames.parentFrame);
+ assertInsetByTopBottom(0, 0, mFrames.frame);
}
@Test
@@ -271,9 +283,9 @@ public class WindowLayoutTests {
mAttrs.setFitInsetsIgnoringVisibility(true);
computeFrames();
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.displayFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.parentFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.frame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.displayFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.parentFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.frame);
}
@Test
@@ -284,9 +296,9 @@ public class WindowLayoutTests {
mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
computeFrames();
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mOutFrames.displayFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, IME_HEIGHT, mOutFrames.parentFrame);
- assertInsetByTopBottom(STATUS_BAR_HEIGHT, IME_HEIGHT, mOutFrames.frame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, NAVIGATION_BAR_HEIGHT, mFrames.displayFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, IME_HEIGHT, mFrames.parentFrame);
+ assertInsetByTopBottom(STATUS_BAR_HEIGHT, IME_HEIGHT, mFrames.frame);
}
@Test
@@ -297,11 +309,11 @@ public class WindowLayoutTests {
computeFrames();
assertInsetBy(WATERFALL_INSETS.left, DISPLAY_CUTOUT_HEIGHT, WATERFALL_INSETS.right, 0,
- mOutFrames.displayFrame);
+ mFrames.displayFrame);
assertInsetBy(WATERFALL_INSETS.left, DISPLAY_CUTOUT_HEIGHT, WATERFALL_INSETS.right, 0,
- mOutFrames.parentFrame);
+ mFrames.parentFrame);
assertInsetBy(WATERFALL_INSETS.left, DISPLAY_CUTOUT_HEIGHT, WATERFALL_INSETS.right, 0,
- mOutFrames.frame);
+ mFrames.frame);
}
@Test
@@ -312,11 +324,11 @@ public class WindowLayoutTests {
computeFrames();
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
- mOutFrames.displayFrame);
+ mFrames.displayFrame);
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
- mOutFrames.parentFrame);
+ mFrames.parentFrame);
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
- mOutFrames.frame);
+ mFrames.frame);
}
@Test
@@ -327,9 +339,9 @@ public class WindowLayoutTests {
mAttrs.setFitInsetsTypes(0);
computeFrames();
- assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mOutFrames.displayFrame);
- assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mOutFrames.parentFrame);
- assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mOutFrames.frame);
+ assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mFrames.displayFrame);
+ assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mFrames.parentFrame);
+ assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mFrames.frame);
}
@Test
@@ -344,9 +356,9 @@ public class WindowLayoutTests {
mAttrs.privateFlags |= PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
computeFrames();
- assertRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, mOutFrames.displayFrame);
- assertRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, mOutFrames.parentFrame);
- assertRect(0, 0, DISPLAY_WIDTH, height + DISPLAY_CUTOUT_HEIGHT, mOutFrames.frame);
+ assertRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, mFrames.displayFrame);
+ assertRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, mFrames.parentFrame);
+ assertRect(0, 0, DISPLAY_WIDTH, height + DISPLAY_CUTOUT_HEIGHT, mFrames.frame);
}
@Test
@@ -359,11 +371,11 @@ public class WindowLayoutTests {
computeFrames();
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
- mOutFrames.displayFrame);
+ mFrames.displayFrame);
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
- mOutFrames.parentFrame);
+ mFrames.parentFrame);
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
- mOutFrames.frame);
+ mFrames.frame);
}
@Test
@@ -373,9 +385,9 @@ public class WindowLayoutTests {
mAttrs.setFitInsetsTypes(0);
computeFrames();
- assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mOutFrames.displayFrame);
- assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mOutFrames.parentFrame);
- assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mOutFrames.frame);
+ assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mFrames.displayFrame);
+ assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mFrames.parentFrame);
+ assertInsetBy(WATERFALL_INSETS.left, 0, WATERFALL_INSETS.right, 0, mFrames.frame);
}
@Test
@@ -386,11 +398,11 @@ public class WindowLayoutTests {
computeFrames();
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
- mOutFrames.displayFrame);
+ mFrames.displayFrame);
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
- mOutFrames.parentFrame);
+ mFrames.parentFrame);
assertInsetBy(WATERFALL_INSETS.left, STATUS_BAR_HEIGHT, WATERFALL_INSETS.right, 0,
- mOutFrames.frame);
+ mFrames.frame);
}
@Test
@@ -400,8 +412,8 @@ public class WindowLayoutTests {
mAttrs.setFitInsetsTypes(0);
computeFrames();
- assertInsetByTopBottom(0, 0, mOutFrames.displayFrame);
- assertInsetByTopBottom(0, 0, mOutFrames.parentFrame);
- assertInsetByTopBottom(0, 0, mOutFrames.frame);
+ assertInsetByTopBottom(0, 0, mFrames.displayFrame);
+ assertInsetByTopBottom(0, 0, mFrames.parentFrame);
+ assertInsetByTopBottom(0, 0, mFrames.frame);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index a0c20c2c6b68..1a64f5e3a356 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -49,6 +49,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.pm.PackageManager;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -60,6 +61,7 @@ import android.view.InsetsState;
import android.view.InsetsVisibilities;
import android.view.View;
import android.view.WindowManager;
+import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
@@ -281,7 +283,7 @@ public class WindowManagerServiceTests extends WindowTestsBase {
mWm.addWindow(session, new TestIWindow(), params, View.VISIBLE, DEFAULT_DISPLAY,
UserHandle.USER_SYSTEM, new InsetsVisibilities(), null, new InsetsState(),
- new InsetsSourceControl[0]);
+ new InsetsSourceControl[0], new Rect());
verify(mWm.mWindowContextListenerController, never()).registerWindowContainerListener(any(),
any(), anyInt(), anyInt(), any());
@@ -316,4 +318,76 @@ public class WindowManagerServiceTests extends WindowTestsBase {
verify(mWm.mInputManager).setInTouchMode(
!currentTouchMode, callingPid, callingUid, /* hasPermission= */ false);
}
+
+ @Test
+ public void testGetTaskWindowContainerTokenForLaunchCookie_nullCookie() {
+ WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(null);
+ assertThat(wct).isNull();
+ }
+
+ @Test
+ public void testGetTaskWindowContainerTokenForLaunchCookie_invalidCookie() {
+ Binder cookie = new Binder("test cookie");
+ WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
+ assertThat(wct).isNull();
+
+ final ActivityRecord testActivity = new ActivityBuilder(mAtm)
+ .setCreateTask(true)
+ .build();
+
+ wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
+ assertThat(wct).isNull();
+ }
+
+ @Test
+ public void testGetTaskWindowContainerTokenForLaunchCookie_validCookie() {
+ final Binder cookie = new Binder("ginger cookie");
+ final WindowContainerToken launchRootTask = mock(WindowContainerToken.class);
+ setupActivityWithLaunchCookie(cookie, launchRootTask);
+
+ WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
+ assertThat(wct).isEqualTo(launchRootTask);
+ }
+
+ @Test
+ public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies() {
+ final Binder cookie1 = new Binder("ginger cookie");
+ final WindowContainerToken launchRootTask1 = mock(WindowContainerToken.class);
+ setupActivityWithLaunchCookie(cookie1, launchRootTask1);
+
+ setupActivityWithLaunchCookie(new Binder("choc chip cookie"),
+ mock(WindowContainerToken.class));
+
+ setupActivityWithLaunchCookie(new Binder("peanut butter cookie"),
+ mock(WindowContainerToken.class));
+
+ WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie1);
+ assertThat(wct).isEqualTo(launchRootTask1);
+ }
+
+ @Test
+ public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies_noneValid() {
+ setupActivityWithLaunchCookie(new Binder("ginger cookie"),
+ mock(WindowContainerToken.class));
+
+ setupActivityWithLaunchCookie(new Binder("choc chip cookie"),
+ mock(WindowContainerToken.class));
+
+ setupActivityWithLaunchCookie(new Binder("peanut butter cookie"),
+ mock(WindowContainerToken.class));
+
+ WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(
+ new Binder("some other cookie"));
+ assertThat(wct).isNull();
+ }
+
+ private void setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct) {
+ final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class);
+ when(remoteToken.toWindowContainerToken()).thenReturn(wct);
+ final ActivityRecord testActivity = new ActivityBuilder(mAtm)
+ .setCreateTask(true)
+ .build();
+ testActivity.mLaunchCookie = launchCookie;
+ testActivity.getTask().mRemoteToken = remoteToken;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 40ca2506fab1..84c2c551de85 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -30,7 +30,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
-import static android.view.InsetsState.ITYPE_LOCAL_NAVIGATION_BAR_1;
+import static android.view.InsetsState.ITYPE_TOP_GENERIC_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
@@ -223,7 +223,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
- assertTaskVanished(organizer, false /* expectVanished */, rootTask);
+ verify(organizer, times(0)).onTaskVanished(any());
assertFalse(rootTask.isOrganized());
}
@@ -297,7 +297,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
- assertTaskVanished(organizer, true /* expectVanished */, rootTask);
+ verify(organizer, times(0)).onTaskVanished(any());
assertFalse(rootTask.isOrganized());
}
@@ -341,7 +341,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(3))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- assertTaskVanished(organizer2, true /* expectVanished */, rootTask, rootTask2, rootTask3);
+ verify(organizer2, times(0)).onTaskVanished(any());
}
@Test
@@ -395,6 +395,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertFalse(task2.isAttached());
// Normal task should keep.
assertTrue(task.isAttached());
+ verify(organizer2, times(0)).onTaskVanished(any());
}
@Test
@@ -439,7 +440,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(3))
.onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
- assertTaskVanished(organizer2, true /* expectVanished */, rootTask, rootTask2, rootTask3);
+ verify(organizer2, times(0)).onTaskVanished(any());
}
@Test
@@ -458,6 +459,23 @@ public class WindowOrganizerTests extends WindowTestsBase {
}
@Test
+ public void testRegisterTaskOrganizerWithExistingTasks_noSurfaceControl()
+ throws RemoteException {
+ final Task rootTask = createRootTask();
+ final Task task = createTask(rootTask);
+ final Task rootTask2 = createRootTask();
+ final Task task2 = createTask(rootTask2);
+ rootTask2.setSurfaceControl(null);
+ ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>();
+ final ITaskOrganizer organizer = registerMockOrganizer(existingTasks);
+ assertContainsTasks(existingTasks, rootTask);
+
+ // Verify we don't get onTaskAppeared if we are returned the tasks
+ verify(organizer, never())
+ .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
+ }
+
+ @Test
public void testTaskTransaction() {
removeGlobalMinSizeRestriction();
final Task rootTask = new TaskBuilder(mSupervisor)
@@ -522,7 +540,10 @@ public class WindowOrganizerTests extends WindowTestsBase {
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode());
- assertEquals(WINDOWING_MODE_PINNED, rootTask.getWindowingMode());
+ // Get the root task from the PIP activity record again, since the PIP root task may have
+ // changed when the activity entered PIP mode.
+ final Task pipRootTask = record.getRootTask();
+ assertEquals(WINDOWING_MODE_PINNED, pipRootTask.getWindowingMode());
}
@Test
@@ -687,7 +708,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
final RunningTaskInfo info2 = task2.getTaskInfo();
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setAdjacentRoots(info1.token, info2.token, false /* moveTogether */);
+ wct.setAdjacentRoots(info1.token, info2.token);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertEquals(task1.getAdjacentTaskFragment(), task2);
assertEquals(task2.getAdjacentTaskFragment(), task1);
@@ -697,8 +718,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, task1);
- task1.setAdjacentTaskFragment(null, false /* moveTogether */);
- task2.setAdjacentTaskFragment(null, false /* moveTogether */);
+ task1.setAdjacentTaskFragment(null);
+ task2.setAdjacentTaskFragment(null);
wct = new WindowContainerTransaction();
wct.clearLaunchAdjacentFlagRoot(info1.token);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
@@ -758,11 +779,11 @@ public class WindowOrganizerTests extends WindowTestsBase {
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.addRectInsetsProvider(navigationBarInsetsReceiverTask.mRemoteToken
.toWindowContainerToken(), navigationBarInsetsProviderRect,
- new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+ new int[]{ITYPE_TOP_GENERIC_OVERLAY});
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSourceProviders
- .valueAt(0).getSource().getType()).isEqualTo(ITYPE_LOCAL_NAVIGATION_BAR_1);
+ .valueAt(0).getSource().getType()).isEqualTo(ITYPE_TOP_GENERIC_OVERLAY);
}
@Test
@@ -778,12 +799,12 @@ public class WindowOrganizerTests extends WindowTestsBase {
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.addRectInsetsProvider(navigationBarInsetsReceiverTask.mRemoteToken
.toWindowContainerToken(), navigationBarInsetsProviderRect,
- new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+ new int[]{ITYPE_TOP_GENERIC_OVERLAY});
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
final WindowContainerTransaction wct2 = new WindowContainerTransaction();
wct2.removeInsetsProvider(navigationBarInsetsReceiverTask.mRemoteToken
- .toWindowContainerToken(), new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+ .toWindowContainerToken(), new int[]{ITYPE_TOP_GENERIC_OVERLAY});
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct2);
assertThat(navigationBarInsetsReceiverTask.mLocalInsetsSourceProviders.size()).isEqualTo(0);
@@ -1015,6 +1036,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
final ActivityRecord record = createActivityRecordWithParentTask(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+ record.setPictureInPictureParams(new PictureInPictureParams.Builder()
+ .setAutoEnterEnabled(true).build());
spyOn(record);
doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean());
@@ -1259,7 +1282,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
waitUntilHandlersIdle();
- ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
assertEquals(1, pendingEvents.size());
assertEquals(PendingTaskEvent.EVENT_APPEARED, pendingEvents.get(0).mEventType);
assertEquals("TestDescription",
@@ -1279,7 +1302,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
rootTask.removeImmediately();
waitUntilHandlersIdle();
- ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
assertEquals(0, pendingEvents.size());
}
@@ -1297,7 +1320,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription"));
waitUntilHandlersIdle();
- ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
assertEquals(1, pendingEvents.size());
assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType);
assertEquals("TestDescription",
@@ -1306,7 +1329,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription2"));
waitUntilHandlersIdle();
- pendingEvents = getTaskPendingEvent(rootTask);
+ pendingEvents = getTaskPendingEvent(organizer, rootTask);
assertEquals(1, pendingEvents.size());
assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType);
assertEquals("TestDescription2",
@@ -1329,7 +1352,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
rootTask.removeImmediately();
waitUntilHandlersIdle();
- ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
assertEquals(1, pendingEvents.size());
assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
assertEquals("TestDescription",
@@ -1350,7 +1373,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
rootTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
waitUntilHandlersIdle();
- ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
assertEquals(1, pendingEvents.size());
assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
}
@@ -1370,14 +1393,16 @@ public class WindowOrganizerTests extends WindowTestsBase {
new IRequestFinishCallback.Default());
waitUntilHandlersIdle();
- ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(rootTask);
+ ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(organizer, rootTask);
assertEquals(1, pendingEvents.size());
assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType);
}
- private ArrayList<PendingTaskEvent> getTaskPendingEvent(Task task) {
+ private ArrayList<PendingTaskEvent> getTaskPendingEvent(ITaskOrganizer organizer, Task task) {
ArrayList<PendingTaskEvent> total =
- mWm.mAtmService.mTaskOrganizerController.getPendingEventList();
+ mWm.mAtmService.mTaskOrganizerController
+ .getTaskOrganizerPendingEvents(organizer.asBinder())
+ .getPendingEventList();
ArrayList<PendingTaskEvent> result = new ArrayList();
for (int i = 0; i < total.size(); i++) {
@@ -1499,7 +1524,11 @@ public class WindowOrganizerTests extends WindowTestsBase {
verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
clearInvocations(mWm.mAtmService.mRootWindowContainer);
- t.setWindowingMode(wct, WINDOWING_MODE_FULLSCREEN);
+ // The token for the PIP root task may have changed when the task entered PIP mode, so do
+ // not reuse the one from above.
+ final WindowContainerToken newToken =
+ record.getRootTask().mRemoteToken.toWindowContainerToken();
+ t.setWindowingMode(newToken, WINDOWING_MODE_FULLSCREEN);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 746f2b50ac3a..3abf7ce665ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -39,24 +40,30 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import android.Manifest;
+import android.app.ActivityManager;
+import android.app.ClientTransactionHandler;
import android.app.IApplicationThread;
+import android.app.servertransaction.ConfigurationChangeItem;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.LocaleList;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mockito;
@@ -282,6 +289,39 @@ public class WindowProcessControllerTests extends WindowTestsBase {
}
@Test
+ public void testCachedStateConfigurationChange() throws RemoteException {
+ final ClientLifecycleManager clientManager = mAtm.getLifecycleManager();
+ doNothing().when(clientManager).scheduleTransaction(any(), any());
+ final IApplicationThread thread = mWpc.getThread();
+ final Configuration newConfig = new Configuration(mWpc.getConfiguration());
+ newConfig.densityDpi += 100;
+ // Non-cached state will send the change directly.
+ mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ clearInvocations(clientManager);
+ mWpc.onConfigurationChanged(newConfig);
+ verify(clientManager).scheduleTransaction(eq(thread), any());
+
+ // Cached state won't send the change.
+ clearInvocations(clientManager);
+ mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
+ newConfig.densityDpi += 100;
+ mWpc.onConfigurationChanged(newConfig);
+ verify(clientManager, never()).scheduleTransaction(eq(thread), any());
+
+ // Cached -> non-cached will send the previous deferred config immediately.
+ mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_RECEIVER);
+ final ArgumentCaptor<ConfigurationChangeItem> captor =
+ ArgumentCaptor.forClass(ConfigurationChangeItem.class);
+ verify(clientManager).scheduleTransaction(eq(thread), captor.capture());
+ final ClientTransactionHandler client = mock(ClientTransactionHandler.class);
+ captor.getValue().preExecute(client, null /* token */);
+ final ArgumentCaptor<Configuration> configCaptor =
+ ArgumentCaptor.forClass(Configuration.class);
+ verify(client).updatePendingConfiguration(configCaptor.capture());
+ assertEquals(newConfig, configCaptor.getValue());
+ }
+
+ @Test
public void testComputeOomAdjFromActivities() {
final ActivityRecord activity = createActivityRecord(mWpc);
activity.mVisibleRequested = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 20935f1d199e..446ec8b2680b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -712,6 +712,25 @@ public class WindowStateTests extends WindowTestsBase {
// Keyguard host window should be always contained. The drawn app or app with starting
// window are unnecessary to draw.
assertEquals(Arrays.asList(keyguardHostWindow, startingWindow), outWaitingForDrawn);
+
+ // No need to wait for a window of invisible activity even if the window has surface.
+ final WindowState invisibleApp = mAppWindow;
+ invisibleApp.mActivityRecord.mVisibleRequested = false;
+ invisibleApp.mActivityRecord.allDrawn = false;
+ outWaitingForDrawn.clear();
+ invisibleApp.requestDrawIfNeeded(outWaitingForDrawn);
+ assertTrue(outWaitingForDrawn.isEmpty());
+
+ // Drawn state should not be changed for insets change when screen is off.
+ spyOn(mWm.mPolicy);
+ doReturn(false).when(mWm.mPolicy).isScreenOn();
+ makeWindowVisibleAndDrawn(startingApp);
+ startingApp.getConfiguration().orientation = 0; // Reset to be the same as last reported.
+ startingApp.getWindowFrames().setInsetsChanged(true);
+ startingApp.updateResizingWindowIfNeeded();
+ assertTrue(mWm.mResizingWindows.contains(startingApp));
+ assertTrue(startingApp.isDrawn());
+ assertFalse(startingApp.getOrientationChanging());
}
@UseTestDisplay(addWindows = W_ABOVE_ACTIVITY)
@@ -984,6 +1003,7 @@ public class WindowStateTests extends WindowTestsBase {
assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
// Verify the IME insets is visible on app, but not for app2 during app task switching.
+ mDisplayContent.computeImeTargetIfNeeded(app.mActivityRecord);
assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 20fbda428381..0cbf1b2c7cc8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -64,7 +64,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
-import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -229,14 +228,28 @@ class WindowTestsBase extends SystemServiceTestsBase {
// {@link com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}, is set
// on some device form factors.
mAtm.mWindowManager.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(0);
- // Ensure letterbox position multiplier is not overridden on any device target.
+ // Ensure letterbox horizontal position multiplier is not overridden on any device target.
// {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier},
// may be set on some device form factors.
mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(0.5f);
- // Ensure letterbox reachability treatment isn't overridden on any device target.
- // {@link com.android.internal.R.bool.config_letterboxIsReachabilityEnabled},
+ // Ensure letterbox vertical position multiplier is not overridden on any device target.
+ // {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier},
+ // may be set on some device form factors.
+ mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
+ // Ensure letterbox horizontal reachability treatment isn't overridden on any device target.
+ // {@link com.android.internal.R.bool.config_letterboxIsHorizontalReachabilityEnabled},
// may be set on some device form factors.
- mAtm.mWindowManager.mLetterboxConfiguration.setIsReachabilityEnabled(false);
+ mAtm.mWindowManager.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(false);
+ // Ensure letterbox vertical reachability treatment isn't overridden on any device target.
+ // {@link com.android.internal.R.bool.config_letterboxIsVerticalReachabilityEnabled},
+ // may be set on some device form factors.
+ mAtm.mWindowManager.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(false);
+ // Ensure aspect ratio for unresizable apps isn't overridden on any device target.
+ // {@link com.android.internal.R.bool
+ // .config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled}, may be set on some
+ // device form factors.
+ mAtm.mWindowManager.mLetterboxConfiguration
+ .setIsSplitScreenAspectRatioForUnresizableAppsEnabled(false);
checkDeviceSpecificOverridesNotApplied();
}
@@ -246,7 +259,11 @@ class WindowTestsBase extends SystemServiceTestsBase {
// Revert back to device overrides.
mAtm.mWindowManager.mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
mAtm.mWindowManager.mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
- mAtm.mWindowManager.mLetterboxConfiguration.resetIsReachabilityEnabled();
+ mAtm.mWindowManager.mLetterboxConfiguration.resetLetterboxVerticalPositionMultiplier();
+ mAtm.mWindowManager.mLetterboxConfiguration.resetIsHorizontalReachabilityEnabled();
+ mAtm.mWindowManager.mLetterboxConfiguration.resetIsVerticalReachabilityEnabled();
+ mAtm.mWindowManager.mLetterboxConfiguration
+ .resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
}
/**
@@ -794,7 +811,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
}
@Override
- public void topFocusedWindowChanged(String packageName,
+ public void topFocusedWindowChanged(ComponentName component,
InsetsVisibilities requestedVisibilities) {
}
};
@@ -915,13 +932,15 @@ class WindowTestsBase extends SystemServiceTestsBase {
*/
protected static class ActivityBuilder {
static final int DEFAULT_FAKE_UID = 12345;
+ static final String DEFAULT_PROCESS_NAME = "procName";
+ static int sProcNameSeq;
private final ActivityTaskManagerService mService;
private ComponentName mComponent;
private String mTargetActivity;
private Task mTask;
- private String mProcessName = "name";
+ private String mProcessName = DEFAULT_PROCESS_NAME;
private String mAffinity;
private int mUid = DEFAULT_FAKE_UID;
private boolean mCreateTask = false;
@@ -1109,6 +1128,9 @@ class WindowTestsBase extends SystemServiceTestsBase {
aInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
aInfo.applicationInfo.packageName = mComponent.getPackageName();
aInfo.applicationInfo.uid = mUid;
+ if (DEFAULT_PROCESS_NAME.equals(mProcessName)) {
+ mProcessName += ++sProcNameSeq;
+ }
aInfo.processName = mProcessName;
aInfo.packageName = mComponent.getPackageName();
aInfo.name = mComponent.getClassName();
@@ -1173,16 +1195,11 @@ class WindowTestsBase extends SystemServiceTestsBase {
if (mWpc != null) {
wpc = mWpc;
} else {
- wpc = new WindowProcessController(mService,
- aInfo.applicationInfo, mProcessName, mUid,
- UserHandle.getUserId(mUid), mock(Object.class),
- mock(WindowProcessListener.class));
- wpc.setThread(mock(IApplicationThread.class));
+ final WindowProcessController p = mService.getProcessController(mProcessName, mUid);
+ wpc = p != null ? p : SystemServicesTestRule.addProcess(
+ mService, aInfo.applicationInfo, mProcessName, 0 /* pid */);
}
- wpc.setThread(mock(IApplicationThread.class));
activity.setProcess(wpc);
- doReturn(wpc).when(mService).getProcessController(
- activity.processName, activity.info.applicationInfo.uid);
// Resume top activities to make sure all other signals in the system are connected.
mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
@@ -1550,7 +1567,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
mSecondary = mService.mTaskOrganizerController.createRootTask(
display, WINDOWING_MODE_MULTI_WINDOW, null);
- mPrimary.setAdjacentTaskFragment(mSecondary, true);
+ mPrimary.setAdjacentTaskFragment(mSecondary);
display.getDefaultTaskDisplayArea().setLaunchAdjacentFlagRootTask(mSecondary);
final Rect primaryBounds = new Rect();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 2df1d23c0497..77fca451547d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -328,7 +328,6 @@ public class ZOrderingTests extends WindowTestsBase {
assertWindowHigher(mImeWindow, imeSystemOverlayTarget);
assertWindowHigher(mImeWindow, mChildAppWindowAbove);
assertWindowHigher(mImeWindow, mAppWindow);
- assertWindowHigher(mImeWindow, mDockedDividerWindow);
// The IME has a higher base layer than the status bar so we may expect it to go
// above the status bar once they are both in the Non-App layer, as past versions of this
@@ -349,7 +348,6 @@ public class ZOrderingTests extends WindowTestsBase {
assertWindowHigher(mImeWindow, mChildAppWindowAbove);
assertWindowHigher(mImeWindow, mAppWindow);
- assertWindowHigher(mImeWindow, mDockedDividerWindow);
assertWindowHigher(mImeWindow, mStatusBarWindow);
// And, IME dialogs should always have an higher layer than the IME.
@@ -489,77 +487,6 @@ public class ZOrderingTests extends WindowTestsBase {
}
@Test
- public void testDockedDividerPosition() {
- final Task pinnedTask =
- createTask(mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
- final WindowState pinnedWindow =
- createAppWindow(pinnedTask, ACTIVITY_TYPE_STANDARD, "pinnedWindow");
-
- final Task belowTask =
- createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final WindowState belowTaskWindow =
- createAppWindow(belowTask, ACTIVITY_TYPE_STANDARD, "belowTaskWindow");
-
- final Task splitScreenTask1 =
- createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
- final WindowState splitWindow1 =
- createAppWindow(splitScreenTask1, ACTIVITY_TYPE_STANDARD, "splitWindow1");
- final Task splitScreenTask2 =
- createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
- final WindowState splitWindow2 =
- createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2");
- splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2, true /* moveTogether */);
- splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1, true /* moveTogether */);
-
- final Task aboveTask =
- createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final WindowState aboveTaskWindow =
- createAppWindow(aboveTask, ACTIVITY_TYPE_STANDARD, "aboveTaskWindow");
-
- mDisplayContent.assignChildLayers(mTransaction);
-
- assertWindowHigher(splitWindow1, belowTaskWindow);
- assertWindowHigher(splitWindow2, belowTaskWindow);
- assertWindowHigher(mDockedDividerWindow, splitWindow1);
- assertWindowHigher(mDockedDividerWindow, splitWindow2);
- assertWindowHigher(aboveTaskWindow, mDockedDividerWindow);
- assertWindowHigher(pinnedWindow, aboveTaskWindow);
- }
-
-
- @Test
- public void testDockedDividerPosition_noAboveTask() {
- final Task pinnedTask =
- createTask(mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
- final WindowState pinnedWindow =
- createAppWindow(pinnedTask, ACTIVITY_TYPE_STANDARD, "pinnedWindow");
-
- final Task belowTask =
- createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- final WindowState belowTaskWindow =
- createAppWindow(belowTask, ACTIVITY_TYPE_STANDARD, "belowTaskWindow");
-
- final Task splitScreenTask1 =
- createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
- final WindowState splitWindow1 =
- createAppWindow(splitScreenTask1, ACTIVITY_TYPE_STANDARD, "splitWindow1");
- final Task splitScreenTask2 =
- createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
- final WindowState splitWindow2 =
- createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2");
- splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2, true /* moveTogether */);
- splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1, true /* moveTogether */);
-
- mDisplayContent.assignChildLayers(mTransaction);
-
- assertWindowHigher(splitWindow1, belowTaskWindow);
- assertWindowHigher(splitWindow2, belowTaskWindow);
- assertWindowHigher(mDockedDividerWindow, splitWindow1);
- assertWindowHigher(mDockedDividerWindow, splitWindow2);
- assertWindowHigher(pinnedWindow, mDockedDividerWindow);
- }
-
- @Test
public void testAttachNavBarWhenEnteringRecents_expectNavBarHigherThanIme() {
// create RecentsAnimationController
IRecentsAnimationRunner mockRunner = mock(IRecentsAnimationRunner.class);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index cc33f88d396d..26a1e9d8af11 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -16,6 +16,7 @@
package com.android.server.usage;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
@@ -24,6 +25,7 @@ import android.app.usage.UsageStatsManager;
import android.os.Build;
import android.os.SystemProperties;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
@@ -55,8 +57,11 @@ import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
/**
* Provides an interface to query for UsageStat data from a Protocol Buffer database.
@@ -1252,6 +1257,10 @@ public class UsageStatsDatabase {
Slog.wtf(TAG, "Attempting to backup UsageStats as XML with version " + version);
return null;
}
+ if (version < 1 || version > BACKUP_VERSION) {
+ Slog.wtf(TAG, "Attempting to backup UsageStats with an unknown version: " + version);
+ return null;
+ }
synchronized (mLock) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (KEY_USAGE_STATS.equals(key)) {
@@ -1300,14 +1309,26 @@ public class UsageStatsDatabase {
}
return baos.toByteArray();
}
+ }
+ /**
+ * Updates the set of packages given to only include those that have been used within the
+ * given timeframe (as defined by {@link UsageStats#getLastTimePackageUsed()}).
+ */
+ private void calculatePackagesUsedWithinTimeframe(
+ IntervalStats stats, Set<String> packagesList, long timeframeMs) {
+ for (UsageStats stat : stats.packageStats.values()) {
+ if (stat.getLastTimePackageUsed() > timeframeMs) {
+ packagesList.add(stat.mPackageName);
+ }
+ }
}
/**
* @hide
*/
@VisibleForTesting
- public void applyRestoredPayload(String key, byte[] payload) {
+ public @NonNull Set<String> applyRestoredPayload(String key, byte[] payload) {
synchronized (mLock) {
if (KEY_USAGE_STATS.equals(key)) {
// Read stats files for the current device configs
@@ -1320,12 +1341,15 @@ public class UsageStatsDatabase {
IntervalStats yearlyConfigSource =
getLatestUsageStats(UsageStatsManager.INTERVAL_YEARLY);
+ final Set<String> packagesRestored = new ArraySet<>();
try {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
int backupDataVersion = in.readInt();
// Can't handle this backup set
- if (backupDataVersion < 1 || backupDataVersion > BACKUP_VERSION) return;
+ if (backupDataVersion < 1 || backupDataVersion > BACKUP_VERSION) {
+ return packagesRestored;
+ }
// Delete all stats files
// Do this after reading version and before actually restoring
@@ -1333,10 +1357,14 @@ public class UsageStatsDatabase {
deleteDirectoryContents(mIntervalDirs[i]);
}
+ // 90 days before today in epoch
+ final long timeframe = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(90);
int fileCount = in.readInt();
for (int i = 0; i < fileCount; i++) {
IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in),
backupDataVersion);
+ calculatePackagesUsedWithinTimeframe(stats, packagesRestored, timeframe);
+ packagesRestored.addAll(stats.packageStats.keySet());
stats = mergeStats(stats, dailyConfigSource);
putUsageStats(UsageStatsManager.INTERVAL_DAILY, stats);
}
@@ -1345,6 +1373,7 @@ public class UsageStatsDatabase {
for (int i = 0; i < fileCount; i++) {
IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in),
backupDataVersion);
+ calculatePackagesUsedWithinTimeframe(stats, packagesRestored, timeframe);
stats = mergeStats(stats, weeklyConfigSource);
putUsageStats(UsageStatsManager.INTERVAL_WEEKLY, stats);
}
@@ -1353,6 +1382,7 @@ public class UsageStatsDatabase {
for (int i = 0; i < fileCount; i++) {
IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in),
backupDataVersion);
+ calculatePackagesUsedWithinTimeframe(stats, packagesRestored, timeframe);
stats = mergeStats(stats, monthlyConfigSource);
putUsageStats(UsageStatsManager.INTERVAL_MONTHLY, stats);
}
@@ -1361,6 +1391,7 @@ public class UsageStatsDatabase {
for (int i = 0; i < fileCount; i++) {
IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in),
backupDataVersion);
+ calculatePackagesUsedWithinTimeframe(stats, packagesRestored, timeframe);
stats = mergeStats(stats, yearlyConfigSource);
putUsageStats(UsageStatsManager.INTERVAL_YEARLY, stats);
}
@@ -1370,7 +1401,9 @@ public class UsageStatsDatabase {
} finally {
indexFilesLocked();
}
+ return packagesRestored;
}
+ return Collections.EMPTY_SET;
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index ef13cd964f6c..f595c3de104e 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -3034,7 +3034,8 @@ public class UsageStatsService extends SystemService implements
if (userStats == null) {
return; // user was stopped or removed
}
- userStats.applyRestoredPayload(key, payload);
+ final Set<String> restoredApps = userStats.applyRestoredPayload(key, payload);
+ mAppStandby.restoreAppsToRare(restoredApps, user);
}
}
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index c609add0b5d7..34c6c1623eec 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -63,6 +63,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
+import java.util.Set;
/**
* A per-user UsageStatsService. All methods are meant to be called with the main lock held
@@ -1374,8 +1375,8 @@ class UserUsageStatsService {
return mDatabase.getBackupPayload(key);
}
- void applyRestoredPayload(String key, byte[] payload){
+ Set<String> applyRestoredPayload(String key, byte[] payload) {
checkAndGetTimeLocked();
- mDatabase.applyRestoredPayload(key, payload);
+ return mDatabase.applyRestoredPayload(key, payload);
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 434663bd8167..25db81fa2667 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -31,11 +31,18 @@ import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERV
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_UNKNOWN_TIMEOUT;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__AUDIO_SERVICE_DIED;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTION_SERVICE_RESTARTED__REASON__SCHEDULE;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__APP_REQUEST_UPDATE_STATE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__CALLBACK_UPDATE_STATE_AFTER_TIMEOUT;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__EXTERNAL_SOURCE_DETECTED;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__EXTERNAL_SOURCE_DETECT_SECURITY_EXCEPTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__EXTERNAL_SOURCE_REJECTED;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__ON_CONNECTED;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__ON_DISCONNECTED;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_BIND_SERVICE_FAIL;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__REQUEST_UPDATE_STATE;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__START_EXTERNAL_SOURCE_DETECTION;
+import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENTS__EVENT__START_SOFTWARE_DETECTION;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP;
import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECTED;
@@ -146,6 +153,13 @@ final class HotwordDetectionConnection {
private static final int METRICS_KEYPHRASE_TRIGGERED_REJECT_UNEXPECTED_CALLBACK =
HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECT_UNEXPECTED_CALLBACK;
+ private static final int METRICS_EXTERNAL_SOURCE_DETECTED =
+ HOTWORD_DETECTOR_EVENTS__EVENT__EXTERNAL_SOURCE_DETECTED;
+ private static final int METRICS_EXTERNAL_SOURCE_REJECTED =
+ HOTWORD_DETECTOR_EVENTS__EVENT__EXTERNAL_SOURCE_REJECTED;
+ private static final int METRICS_EXTERNAL_SOURCE_DETECT_SECURITY_EXCEPTION =
+ HOTWORD_DETECTOR_EVENTS__EVENT__EXTERNAL_SOURCE_DETECT_SECURITY_EXCEPTION;
+
private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool();
// TODO: This may need to be a Handler(looper)
private final ScheduledExecutorService mScheduledExecutorService =
@@ -382,6 +396,10 @@ final class HotwordDetectionConnection {
}
void updateStateLocked(PersistableBundle options, SharedMemory sharedMemory) {
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ HOTWORD_DETECTOR_EVENTS__EVENT__APP_REQUEST_UPDATE_STATE,
+ mVoiceInteractionServiceUid);
+
// Prevent doing the init late, so restart is handled equally to a clean process start.
// TODO(b/191742511): this logic needs a test
if (!mUpdateStateAfterStartFinished.get()
@@ -422,14 +440,23 @@ final class HotwordDetectionConnection {
Slog.d(TAG, "onDetected");
}
synchronized (mLock) {
+ HotwordMetricsLogger.writeKeyphraseTriggerEvent(
+ mDetectorType,
+ HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECTED);
if (!mPerformingSoftwareHotwordDetection) {
Slog.i(TAG, "Hotword detection has already completed");
+ HotwordMetricsLogger.writeKeyphraseTriggerEvent(
+ mDetectorType,
+ METRICS_KEYPHRASE_TRIGGERED_DETECT_UNEXPECTED_CALLBACK);
return;
}
mPerformingSoftwareHotwordDetection = false;
try {
enforcePermissionsForDataDelivery();
} catch (SecurityException e) {
+ HotwordMetricsLogger.writeKeyphraseTriggerEvent(
+ mDetectorType,
+ METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION);
mSoftwareCallback.onError();
return;
}
@@ -449,6 +476,9 @@ final class HotwordDetectionConnection {
if (DEBUG) {
Slog.wtf(TAG, "onRejected");
}
+ HotwordMetricsLogger.writeKeyphraseTriggerEvent(
+ mDetectorType,
+ HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED);
// onRejected isn't allowed here, and we are not expecting it.
}
};
@@ -460,6 +490,9 @@ final class HotwordDetectionConnection {
null,
null,
internalCallback));
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ HOTWORD_DETECTOR_EVENTS__EVENT__START_SOFTWARE_DETECTION,
+ mVoiceInteractionServiceUid);
}
public void startListeningFromExternalSource(
@@ -891,6 +924,9 @@ final class HotwordDetectionConnection {
@Override
public void onRejected(HotwordRejectedResult result)
throws RemoteException {
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ METRICS_EXTERNAL_SOURCE_REJECTED,
+ mVoiceInteractionServiceUid);
mScheduledExecutorService.schedule(
() -> {
bestEffortClose(serviceAudioSink, audioSource);
@@ -912,6 +948,9 @@ final class HotwordDetectionConnection {
@Override
public void onDetected(HotwordDetectedResult triggerResult)
throws RemoteException {
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ METRICS_EXTERNAL_SOURCE_DETECTED,
+ mVoiceInteractionServiceUid);
mScheduledExecutorService.schedule(
() -> {
bestEffortClose(serviceAudioSink, audioSource);
@@ -922,6 +961,9 @@ final class HotwordDetectionConnection {
try {
enforcePermissionsForDataDelivery();
} catch (SecurityException e) {
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ METRICS_EXTERNAL_SOURCE_DETECT_SECURITY_EXCEPTION,
+ mVoiceInteractionServiceUid);
callback.onError();
return;
}
@@ -942,6 +984,9 @@ final class HotwordDetectionConnection {
// A copy of this has been created and passed to the hotword validator
bestEffortClose(serviceAudioSource);
});
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ HOTWORD_DETECTOR_EVENTS__EVENT__START_EXTERNAL_SOURCE_DETECTION,
+ mVoiceInteractionServiceUid);
}
private class ServiceConnectionFactory {
@@ -1002,7 +1047,12 @@ final class HotwordDetectionConnection {
return;
}
mIsBound = connected;
- if (connected && !mIsLoggedFirstConnect) {
+
+ if (!connected) {
+ HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
+ HOTWORD_DETECTOR_EVENTS__EVENT__ON_DISCONNECTED,
+ mVoiceInteractionServiceUid);
+ } else if (!mIsLoggedFirstConnect) {
mIsLoggedFirstConnect = true;
HotwordMetricsLogger.writeDetectorEvent(mDetectorType,
HOTWORD_DETECTOR_EVENTS__EVENT__ON_CONNECTED,
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index 4924a82c385f..423022599de6 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -1146,4 +1146,35 @@ public final class SmsApplication {
}
return null;
}
+
+ /**
+ * Check if a package is default mms app (or equivalent, like bluetooth)
+ *
+ * @param context context from the calling app
+ * @param packageName the name of the package to be checked
+ * @return true if the package is default mms app or bluetooth
+ */
+ @UnsupportedAppUsage
+ public static boolean isDefaultMmsApplication(Context context, String packageName) {
+ if (packageName == null) {
+ return false;
+ }
+ String defaultMmsPackage = getDefaultMmsApplicationPackageName(context);
+ String bluetoothPackageName = context.getResources()
+ .getString(com.android.internal.R.string.config_systemBluetoothStack);
+
+ if ((defaultMmsPackage != null && defaultMmsPackage.equals(packageName))
+ || bluetoothPackageName.equals(packageName)) {
+ return true;
+ }
+ return false;
+ }
+
+ private static String getDefaultMmsApplicationPackageName(Context context) {
+ ComponentName component = getDefaultMmsApplication(context, false);
+ if (component != null) {
+ return component.getPackageName();
+ }
+ return null;
+ }
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index 38becc6af0dc..297940e9c8c6 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -49,7 +49,7 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
private static final String TAG = "CellSignalStrengthNr";
// Lifted from Default carrier configs and max range of SSRSRP
- // Boundaries: [-140 dB, -44 dB]
+ // Boundaries: [-156 dB, -31 dB]
private int[] mSsRsrpThresholds = new int[] {
-110, /* SIGNAL_STRENGTH_POOR */
-90, /* SIGNAL_STRENGTH_MODERATE */
@@ -173,14 +173,14 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
*/
public CellSignalStrengthNr(int csiRsrp, int csiRsrq, int csiSinr, int csiCqiTableIndex,
List<Byte> csiCqiReport, int ssRsrp, int ssRsrq, int ssSinr) {
- mCsiRsrp = inRangeOrUnavailable(csiRsrp, -140, -44);
+ mCsiRsrp = inRangeOrUnavailable(csiRsrp, -156, -31);
mCsiRsrq = inRangeOrUnavailable(csiRsrq, -20, -3);
mCsiSinr = inRangeOrUnavailable(csiSinr, -23, 23);
mCsiCqiTableIndex = inRangeOrUnavailable(csiCqiTableIndex, 1, 3);
mCsiCqiReport = csiCqiReport.stream()
- .map(cqi -> new Integer(inRangeOrUnavailable(Byte.toUnsignedInt(cqi), 0, 15)))
+ .map(cqi -> inRangeOrUnavailable(Byte.toUnsignedInt(cqi), 0, 15))
.collect(Collectors.toList());
- mSsRsrp = inRangeOrUnavailable(ssRsrp, -140, -44);
+ mSsRsrp = inRangeOrUnavailable(ssRsrp, -156, -31);
mSsRsrq = inRangeOrUnavailable(ssRsrq, -43, 20);
mSsSinr = inRangeOrUnavailable(ssSinr, -23, 40);
updateLevel(null, null);
@@ -212,8 +212,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
}
/**
- * Reference: 3GPP TS 38.215.
- * Range: -140 dBm to -44 dBm.
+ * Reference: 3GPP TS 38.133 10.1.6.1.
+ * Range: -156 dBm to -31 dBm.
* @return SS reference signal received power, {@link CellInfo#UNAVAILABLE} means unreported
* value.
*/
@@ -242,8 +242,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
}
/**
- * Reference: 3GPP TS 38.215.
- * Range: -140 dBm to -44 dBm.
+ * Reference: 3GPP TS 38.133 10.1.6.1.
+ * Range: -156 dBm to -31 dBm.
* @return CSI reference signal received power, {@link CellInfo#UNAVAILABLE} means unreported
* value.
*/
diff --git a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
index 24dfbd028d03..a004cc3a1642 100644
--- a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
+++ b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
@@ -21,6 +21,7 @@ import android.os.Build;
import android.text.Editable;
import android.text.Selection;
import android.text.TextWatcher;
+import android.text.style.TtsSpan;
import com.android.i18n.phonenumbers.AsYouTypeFormatter;
import com.android.i18n.phonenumbers.PhoneNumberUtil;
@@ -119,6 +120,13 @@ public class PhoneNumberFormattingTextWatcher implements TextWatcher {
}
mSelfChange = false;
}
+
+ //remove previous TTS spans
+ TtsSpan[] ttsSpans = s.getSpans(0, s.length(), TtsSpan.class);
+ for (TtsSpan ttsSpan : ttsSpans) {
+ s.removeSpan(ttsSpan);
+ }
+
PhoneNumberUtils.ttsSpanAsPhoneNumber(s, 0, s.length());
}
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 06c5b5cfeda1..5e02532e85a8 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -129,7 +129,7 @@ public class UiccSlotInfo implements Parcelable {
this.mLogicalSlotIdx = logicalSlotIdx;
this.mIsExtendedApduSupported = isExtendedApduSupported;
this.mIsRemovable = false;
- this.mPortList = null;
+ this.mPortList = new ArrayList<UiccPortInfo>();
}
/**
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 235ed842b749..3ed87e1b9fe2 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1137,10 +1137,7 @@ public class ApnSetting implements Parcelable {
return false;
}
// DEFAULT can handle HIPRI.
- if (hasApnType(type)) {
- return true;
- }
- return false;
+ return hasApnType(type);
}
// Check whether the types of two APN same (even only one type of each APN is same).
@@ -2193,11 +2190,10 @@ public class ApnSetting implements Parcelable {
}
if ((mApnTypeBitmask & TYPE_MMS) != 0 && !TextUtils.isEmpty(mMmsProxyAddress)
&& mMmsProxyAddress.startsWith("http")) {
- if (Build.IS_DEBUGGABLE) {
- throw new IllegalArgumentException("mms proxy(" + mMmsProxyAddress
- + ") should be a hostname, not a url");
- }
- return null;
+ Log.wtf(LOG_TAG,"mms proxy(" + mMmsProxyAddress
+ + ") should be a hostname, not a url");
+ Uri mMmsProxyAddressUri = Uri.parse(mMmsProxyAddress);
+ mMmsProxyAddress = mMmsProxyAddressUri.getHost();
}
return new ApnSetting(this);
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 7f309e1974e1..315c40ffa9ba 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -18,10 +18,13 @@
package com.android.server.wm.flicker
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.traces.region.RegionSubject
import com.android.server.wm.traces.common.FlickerComponentName
-val LAUNCHER_COMPONENT = FlickerComponentName("com.google.android.apps.nexuslauncher",
- "com.google.android.apps.nexuslauncher.NexusLauncherActivity")
+val LAUNCHER_COMPONENT = FlickerComponentName(
+ "com.google.android.apps.nexuslauncher",
+ "com.google.android.apps.nexuslauncher.NexusLauncherActivity"
+)
/**
* Checks that [FlickerComponentName.STATUS_BAR] window is visible and above the app windows in
@@ -109,9 +112,9 @@ fun FlickerTestParameter.statusBarLayerIsVisible() {
fun FlickerTestParameter.navBarLayerPositionStart() {
assertLayersStart {
val display = this.entry.displays.minByOrNull { it.id }
- ?: throw RuntimeException("There is no display!")
+ ?: throw RuntimeException("There is no display!")
this.visibleRegion(FlickerComponentName.NAV_BAR)
- .coversExactly(WindowUtils.getNavigationBarPosition(display))
+ .coversExactly(WindowUtils.getNavigationBarPosition(display, isGesturalNavigation))
}
}
@@ -122,9 +125,9 @@ fun FlickerTestParameter.navBarLayerPositionStart() {
fun FlickerTestParameter.navBarLayerPositionEnd() {
assertLayersEnd {
val display = this.entry.displays.minByOrNull { it.id }
- ?: throw RuntimeException("There is no display!")
+ ?: throw RuntimeException("There is no display!")
this.visibleRegion(FlickerComponentName.NAV_BAR)
- .coversExactly(WindowUtils.getNavigationBarPosition(display))
+ .coversExactly(WindowUtils.getNavigationBarPosition(display, isGesturalNavigation))
}
}
@@ -173,6 +176,33 @@ fun FlickerTestParameter.statusBarLayerRotatesScales() {
}
/**
+ * Asserts that the visibleRegion of the [FlickerComponentName.SNAPSHOT] layer can cover
+ * the visibleRegion of the given app component exactly
+ */
+fun FlickerTestParameter.snapshotStartingWindowLayerCoversExactlyOnApp(
+ component: FlickerComponentName) {
+ assertLayers {
+ invoke("snapshotStartingWindowLayerCoversExactlyOnApp") {
+ val snapshotLayers = it.subjects.filter { subject ->
+ subject.name.contains(
+ FlickerComponentName.SNAPSHOT.toLayerName()) && subject.isVisible
+ }
+ // Verify the size of snapshotRegion covers appVisibleRegion exactly in animation.
+ if (snapshotLayers.isNotEmpty()) {
+ val visibleAreas = snapshotLayers.mapNotNull { snapshotLayer ->
+ snapshotLayer.layer?.visibleRegion
+ }.toTypedArray()
+ val snapshotRegion = RegionSubject.assertThat(visibleAreas, this, timestamp)
+ val appVisibleRegion = it.visibleRegion(component)
+ if (snapshotRegion.region.isNotEmpty) {
+ snapshotRegion.coversExactly(appVisibleRegion.region)
+ }
+ }
+ }
+ }
+}
+
+/**
* Asserts that:
* [originalLayer] is visible at the start of the trace
* [originalLayer] becomes invisible during the trace and (in the same entry) [newLayer]
@@ -216,11 +246,11 @@ fun FlickerTestParameter.replacesLayer(
assertLayersStart {
this.isVisible(originalLayer)
- .isInvisible(newLayer)
+ .isInvisible(newLayer)
}
assertLayersEnd {
this.isInvisible(originalLayer)
- .isVisible(newLayer)
+ .isVisible(newLayer)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
index 6257484be9bd..2e29b3e314ca 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
@@ -59,7 +59,7 @@ class CloseImeEditorPopupDialogTest(private val testSpec: FlickerTestParameter)
}
transitions {
imeTestApp.dismissDialog(wmHelper)
- instrumentation.uiAutomation.syncInputTransactions()
+ wmHelper.waitImeGone()
}
teardown {
eachRun {
@@ -91,7 +91,7 @@ class CloseImeEditorPopupDialogTest(private val testSpec: FlickerTestParameter)
.then()
.isVisible(FlickerComponentName.IME_SNAPSHOT)
.then()
- .isInvisible(FlickerComponentName.IME_SNAPSHOT)
+ .isInvisible(FlickerComponentName.IME_SNAPSHOT, isOptional = true)
.isInvisible(FlickerComponentName.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
index 78aea1f1fb17..88fb1a220910 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
@@ -17,23 +17,25 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
import android.view.Surface
import android.view.WindowManagerPolicyConstants
import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarLayerPositionEnd
import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.snapshotStartingWindowLayerCoversExactlyOnApp
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import org.junit.FixMethodOrder
@@ -43,7 +45,8 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test IME window layer will become visible when switching from the fixed orientation activity.
+ * Test IME window layer will become visible when switching from the fixed orientation activity
+ * (e.g. Launcher activity).
* To run this test: `atest FlickerTests:OpenImeWindowFromFixedOrientationAppTest`
*/
@RequiresDevice
@@ -53,24 +56,31 @@ import org.junit.runners.Parameterized
@Group2
class OpenImeWindowFromFixedOrientationAppTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val fixedOrientationApp = FixedOrientationAppHelper(instrumentation)
private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
+ private val taplInstrumentation = LauncherInstrumentation()
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
setup {
+ test {
+ // Launch the activity with expecting IME will be shown.
+ imeTestApp.launchViaIntent(wmHelper)
+ }
eachRun {
- fixedOrientationApp.launchViaIntent(wmHelper)
- this.setRotation(Surface.ROTATION_90)
+ // Swiping out the IME activity to home.
+ taplInstrumentation.goHome()
+ wmHelper.waitForHomeActivityVisible()
}
}
transitions {
+ // Bring the exist IME activity to the front in landscape mode device rotation.
+ setRotation(Surface.ROTATION_90)
imeTestApp.launchViaIntent(wmHelper)
}
teardown {
test {
- fixedOrientationApp.exit(wmHelper)
+ imeTestApp.exit(wmHelper)
}
}
}
@@ -90,7 +100,7 @@ class OpenImeWindowFromFixedOrientationAppTest(private val testSpec: FlickerTest
@Presubmit
@Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerPositionEnd()
@FlakyTest(bugId = 206753786)
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@@ -99,6 +109,12 @@ class OpenImeWindowFromFixedOrientationAppTest(private val testSpec: FlickerTest
@Test
fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible()
+ @Postsubmit
+ @Test
+ fun snapshotStartingWindowLayerCoversExactlyOnApp() {
+ testSpec.snapshotStartingWindowLayerCoversExactlyOnApp(imeTestApp.component)
+ }
+
companion object {
/**
* Creates the test configurations.
@@ -112,7 +128,7 @@ class OpenImeWindowFromFixedOrientationAppTest(private val testSpec: FlickerTest
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_0),
+ supportedRotations = listOf(Surface.ROTATION_90),
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
)
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 43aa4b151548..3e2130dc480f 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -45,6 +45,7 @@
android:theme="@style/CutoutShortEdges"
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus"
android:windowSoftInputMode="stateVisible"
+ android:configChanges="orientation|screenSize"
android:label="ImeAppAutoFocus"
android:exported="true">
<intent-filter>
diff --git a/tests/WindowInsetsTests/res/layout/controller_activity.xml b/tests/WindowInsetsTests/res/layout/controller_activity.xml
index d51a4ddd43e8..5550eab61a33 100644
--- a/tests/WindowInsetsTests/res/layout/controller_activity.xml
+++ b/tests/WindowInsetsTests/res/layout/controller_activity.xml
@@ -88,7 +88,7 @@
<TextView
android:id="@+id/textViewControllableInsets"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp" />
diff --git a/tests/WindowInsetsTests/res/values/strings.xml b/tests/WindowInsetsTests/res/values/strings.xml
index d6355f5a0464..516d4584426e 100644
--- a/tests/WindowInsetsTests/res/values/strings.xml
+++ b/tests/WindowInsetsTests/res/values/strings.xml
@@ -22,7 +22,7 @@
<!-- The item positions should match the flag values respectively. -->
<string-array name="behaviors">
- <item>BEHAVIOR_SHOW_BARS_BY_TOUCH</item>
+ <item>BEHAVIOR_SHOW_BARS_BY_TOUCH (deprecated)</item>
<item>BEHAVIOR_DEFAULT</item>
<item>BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE</item>
</string-array>
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java
index 95fd959e5587..e6b60cfbe84f 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java
@@ -83,7 +83,51 @@ public class ControllerActivity extends Activity implements View.OnApplyWindowIn
final View contentView = findViewById(R.id.content);
contentView.setOnApplyWindowInsetsListener(this);
contentView.getWindowInsetsController().addOnControllableInsetsChangedListener(
- (c, types) -> mTextControllableInsets.setText("ControllableInsetsTypes=" + types));
+ (c, types) -> mTextControllableInsets.setText(
+ "ControllableInsetsTypes:\n" + insetsTypesToString(types)));
+ }
+
+ private static String insetsTypesToString(int types) {
+ if (types == 0) {
+ return "none";
+ }
+ final StringBuilder sb = new StringBuilder();
+ if ((types & Type.statusBars()) != 0) {
+ types &= ~Type.statusBars();
+ sb.append("statusBars ");
+ }
+ if ((types & Type.navigationBars()) != 0) {
+ types &= ~Type.navigationBars();
+ sb.append("navigationBars ");
+ }
+ if ((types & Type.captionBar()) != 0) {
+ types &= ~Type.captionBar();
+ sb.append("captionBar ");
+ }
+ if ((types & Type.ime()) != 0) {
+ types &= ~Type.ime();
+ sb.append("ime ");
+ }
+ if ((types & Type.systemGestures()) != 0) {
+ types &= ~Type.systemGestures();
+ sb.append("systemGestures ");
+ }
+ if ((types & Type.mandatorySystemGestures()) != 0) {
+ types &= ~Type.mandatorySystemGestures();
+ sb.append("mandatorySystemGestures ");
+ }
+ if ((types & Type.tappableElement()) != 0) {
+ types &= ~Type.tappableElement();
+ sb.append("tappableElement ");
+ }
+ if ((types & Type.displayCutout()) != 0) {
+ types &= ~Type.displayCutout();
+ sb.append("displayCutout ");
+ }
+ if (types != 0) {
+ sb.append("unknownTypes:").append(types);
+ }
+ return sb.toString();
}
@Override
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 1fe13fe97fbe..06cbeb5368a5 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -124,16 +124,6 @@ public class WindowManagerPermissionTests extends TestCase {
@SmallTest
public void testSET_ORIENTATION() {
try {
- mWm.updateRotation(true, false);
- fail("IWindowManager.updateRotation did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
-
- try {
mWm.freezeRotation(-1);
fail("IWindowManager.freezeRotation did not throw SecurityException as"
+ " expected");